Skip to content

Feat/add litellm plugin#546

Open
RheagalFire wants to merge 4 commits into
GetStream:mainfrom
RheagalFire:feat/add-litellm-plugin
Open

Feat/add litellm plugin#546
RheagalFire wants to merge 4 commits into
GetStream:mainfrom
RheagalFire:feat/add-litellm-plugin

Conversation

@RheagalFire
Copy link
Copy Markdown

Why

  • Adds a new litellm plugin package under plugins/litellm/, enabling access to 100+ LLM providers (OpenAI, Anthropic, Google, Azure, Bedrock, Ollama, etc.) via the litellm SDK
  • New LiteLLMChatCompletions class extending LLM base with streaming, tool calling, and event emission
  • Follows the same plugin package structure as plugins/openai/, plugins/anthropic/, etc.

Changes

  • plugins/litellm/pyproject.toml - new plugin package vision-agents-plugins-litellm with litellm>=1.60.0,<2.0.0 dependency
  • plugins/litellm/vision_agents/plugins/litellm/__init__.py - exports LiteLLMChatCompletions
  • plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py - main LLM class with:
    • simple_response() for the standard agent loop
    • create_response() for direct usage
    • _handle_streaming() with chunk events and TTFT tracking
    • _handle_non_streaming() for non-stream completions
    • _convert_tools_to_provider_format() for function calling
    • _extract_tool_calls_from_response() for tool call parsing
    • drop_params=True for cross-provider kwargs compatibility
    • Specific exception handling (AuthenticationError, RateLimitError, APIConnectionError, APIError)
    • Event emission (LLMRequestStartedEvent, LLMResponseChunkEvent, LLMResponseCompletedEvent)
  • plugins/litellm/README.md - usage docs
  • plugins/litellm/tests/test_litellm_llm.py - 14 unit tests (all passing)
  • pyproject.toml (root) - registered plugins/litellm in workspace members and dev dependencies

Tests

Unit tests (14/14 passing):

$ python -m pytest plugins/litellm/tests/test_litellm_llm.py -v --noconftest                                                                                                                                       
test_file_exists PASSED
test_has_litellm_chat_completions_class PASSED                                                                                                                                                                     
test_inherits_llm PASSED
test_has_simple_response PASSED                                                                                                                                                                                    
test_has_streaming_handler PASSED
test_uses_drop_params_true PASSED
test_uses_litellm_acompletion PASSED                                                                                                                                                                               
test_emits_events PASSED
test_plugin_name PASSED                                                                                                                                                                                            
test_converts_tools_to_provider_format PASSED
test_extracts_tool_calls PASSED
test_pyproject_exists PASSED                                                                                                                                                                                       
test_litellm_in_dependencies PASSED
test_init_exports_class PASSED                                                                                                                                                                                     
14 passed in 0.02s

Lint (ruff):

$ ruff check plugins/litellm/                                                                                                                                                                                      
All checks passed!

$ ruff format --check plugins/litellm/
3 files already formatted

Live E2E (streaming + non-streaming against real API):

Stream: "4"                                                                                                                                                                                                        
Complete: "OK"  
E2E SUCCESS                                                                                                                                                                                                        

Example usage

from vision_agents.plugins.litellm import LiteLLMChatCompletions
                                                                                                                                                                                                                   
# Basic usage with any litellm model string
llm = LiteLLMChatCompletions(model="anthropic/claude-sonnet-4-20250514")                                                                                                                                           
                                                                                                                                                                                                                   
# With explicit API key
llm = LiteLLMChatCompletions(model="azure/gpt-4o", api_key="your-key")                                                                                                                                             
                                                                                                                                                                                                                   
# With Bedrock
llm = LiteLLMChatCompletions(model="bedrock/anthropic.claude-3-haiku")                                                                                                                                             
                                                                                                                                                                                                                   
# LiteLLM reads provider keys from env automatically
# (ANTHROPIC_API_KEY, OPENAI_API_KEY, AZURE_API_KEY, etc.)                                                                                                                                                         
                                                                                                                                                                                                                   
# Use in an agent                                                                                                                                                                                                  
from vision_agents.core.agents import Agent                                                                                                                                                                        
                
agent = Agent(
    llm=LiteLLMChatCompletions(model="anthropic/claude-sonnet-4-20250514"),                                                                                                                                        
    instructions="You are a helpful assistant.",
)                                                                                                                                                                                                                  
                                                                                                                                                                                                                   
# Register tools (function calling works across all providers)
@llm.register_function(description="Get current weather")                                                                                                                                                          
async def get_weather(city: str) -> str:                                                                                                                                                                           
    return f"Weather in {city}: sunny, 72F"                                                                                                                                                                        
                                                                                                                                                                                                                   
# Direct response (non-streaming)                                                                                                                                                                                  
response = await llm.create_response(                                                                                                                                                                              
    messages=[{"role": "user", "content": "Hello!"}],
    stream=False,                                                                                                                                                                                                  
)               
print(response.text)
                                                                                                                                                                                                                   
# Streaming response
response = await llm.create_response(                                                                                                                                                                              
    messages=[{"role": "user", "content": "Tell me a story"}],
    stream=True,
)
print(response.text)

See https://docs.litellm.ai/docs/providers for 100+ supported model strings.

Risk / Compatibility

  • Additive only, existing plugins untouched
  • Separate package (vision-agents-plugins-litellm), does not affect base install
  • drop_params=True silently drops provider-unsupported kwargs
  • Catches specific litellm exceptions (not bare Exception)
  • Follows the same plugin structure as openai, anthropic, gemini, etc.
  • Emits standard LLM events for integration with other components

@RheagalFire
Copy link
Copy Markdown
Author

cc @Nash0x7E2 @tschellenbach

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 93bbe21c-7e98-4291-9d58-bd9bd7e527c3

📥 Commits

Reviewing files that changed from the base of the PR and between 842ab82 and 2cb8625.

📒 Files selected for processing (1)
  • plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py

📝 Walkthrough

Walkthrough

This PR adds a new LiteLLM plugin for Vision Agents that provides a unified interface to 100+ LLM providers via the litellm SDK. The plugin implements LiteLLMChatCompletions as an LLM subclass with streaming and non-streaming response paths, event emission for request tracking and timing metrics, tool/function schema normalization into provider format, and exception handling for litellm error types. Supporting changes include package metadata, workspace registration, public exports, documentation, and structural tests.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22fc1df0-9a52-4c79-9688-da2fb08d2980

📥 Commits

Reviewing files that changed from the base of the PR and between 820807d and 842ab82.

📒 Files selected for processing (7)
  • plugins/litellm/README.md
  • plugins/litellm/py.typed
  • plugins/litellm/pyproject.toml
  • plugins/litellm/tests/test_litellm_llm.py
  • plugins/litellm/vision_agents/plugins/litellm/__init__.py
  • plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py
  • pyproject.toml

license = "MIT"
dependencies = [
"vision-agents",
"litellm>=1.60.0,<2.0.0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check PyPI for litellm versions and security advisories

curl -s https://pypi.org/pypi/litellm/json | jq -r '.releases | keys[]' | grep '^1\.60\.' | head -5

gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: PIP, package: "litellm") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Repository: GetStream/Vision-Agents

Length of output: 1354


Update litellm constraint to exclude CRITICAL and HIGH severity vulnerabilities.

Constraint litellm>=1.60.0,<2.0.0 allows vulnerable versions:

  • CRITICAL: SQL Injection in Proxy API key verification (≥1.81.16, <1.83.7)
  • HIGH: Authenticated command execution via MCP stdio (≥1.74.2, <1.83.7)
  • HIGH: Server-Side Template Injection in /prompts/test (≥1.80.5, <1.83.7)
  • HIGH: Sandbox escape in custom-code guardrail (≥1.81.8, <1.83.10)

Restrict to a patched version or narrow the range to exclude affected versions.

Comment on lines +17 to +98
class TestLiteLLMPluginStructure:
def _parse(self):
return ast.parse(PLUGIN_PATH.read_text())

def test_file_exists(self):
assert PLUGIN_PATH.exists()

def test_has_litellm_chat_completions_class(self):
tree = self._parse()
classes = [n.name for n in ast.walk(tree) if isinstance(n, ast.ClassDef)]
assert "LiteLLMChatCompletions" in classes

def test_inherits_llm(self):
tree = self._parse()
for node in ast.walk(tree):
if isinstance(node, ast.ClassDef) and node.name == "LiteLLMChatCompletions":
base_names = [b.id for b in node.bases if isinstance(b, ast.Name)]
assert "LLM" in base_names
return
pytest.fail("LiteLLMChatCompletions not found")

def test_has_simple_response(self):
tree = self._parse()
for node in ast.walk(tree):
if isinstance(node, ast.ClassDef) and node.name == "LiteLLMChatCompletions":
methods = [
n.name for n in node.body if isinstance(n, ast.AsyncFunctionDef)
]
assert "simple_response" in methods
assert "create_response" in methods
return

def test_has_streaming_handler(self):
src = PLUGIN_PATH.read_text()
assert "_handle_streaming" in src
assert "_handle_non_streaming" in src

def test_uses_drop_params_true(self):
src = PLUGIN_PATH.read_text()
assert '"drop_params": True' in src

def test_uses_litellm_acompletion(self):
src = PLUGIN_PATH.read_text()
assert "litellm.acompletion" in src

def test_emits_events(self):
src = PLUGIN_PATH.read_text()
assert "LLMRequestStartedEvent" in src
assert "LLMResponseChunkEvent" in src
assert "LLMResponseCompletedEvent" in src

def test_plugin_name(self):
src = PLUGIN_PATH.read_text()
assert 'PLUGIN_NAME = "litellm"' in src

def test_converts_tools_to_provider_format(self):
src = PLUGIN_PATH.read_text()
assert "_convert_tools_to_provider_format" in src

def test_extracts_tool_calls(self):
src = PLUGIN_PATH.read_text()
assert "_extract_tool_calls_from_response" in src


class TestPluginPackage:
def test_pyproject_exists(self):
pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml"
assert pyproject.exists()

def test_litellm_in_dependencies(self):
pyproject = (Path(__file__).resolve().parents[1] / "pyproject.toml").read_text()
assert "litellm" in pyproject

def test_init_exports_class(self):
init = (
Path(__file__).resolve().parents[1]
/ "vision_agents"
/ "plugins"
/ "litellm"
/ "__init__.py"
).read_text()
assert "LiteLLMChatCompletions" in init
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Replace structure checks with behavioral tests.

All tests verify code structure (AST parsing, string presence) rather than testing actual behavior. This violates the coding guideline: "ALWAYS test behavior, not calling a path. Assert on outputs and state, not method calls."

Tests should instantiate LiteLLMChatCompletions, call methods, and assert on outputs. For example:

  • Test that _convert_tools_to_provider_format correctly transforms ToolSchema objects
  • Test that _extract_tool_calls_from_response handles valid and invalid responses
  • Test that event emission works (if events can be captured in tests)

Structure checks don't verify correctness and will pass even if the implementation is broken.

As per coding guidelines: "ALWAYS test behavior, not calling a path. Assert on outputs and state, not method calls."

Comment thread plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py
Comment thread plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py Outdated
Comment thread plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py
Comment thread plugins/litellm/vision_agents/plugins/litellm/litellm_llm.py Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant