Skip to content

fix: remove litellm#8852

Merged
dmadisetti merged 3 commits intomainfrom
dm/llm-remove
Mar 24, 2026
Merged

fix: remove litellm#8852
dmadisetti merged 3 commits intomainfrom
dm/llm-remove

Conversation

@dmadisetti
Copy link
Copy Markdown
Collaborator

📝 Summary

Remove litllm

Copilot AI review requested due to automatic review settings March 24, 2026 15:19
@dmadisetti dmadisetti requested a review from akshayka as a code owner March 24, 2026 15:19
@dmadisetti dmadisetti requested a review from Light2Dark March 24, 2026 15:20
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Mar 24, 2026 3:51pm

Request Review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Removes the litellm dependency and updates the Bedrock chat model implementation to call AWS Bedrock directly via boto3 (Converse / ConverseStream), with corresponding test updates.

Changes:

  • Dropped litellm from optional/test dependency groups and the dependency manager.
  • Reworked bedrock ChatModel to use boto3 Bedrock Runtime converse_stream with fallback to converse.
  • Updated Bedrock unit tests and example metadata to reflect the removal.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
marimo/_ai/llm/_impl.py Replace LiteLLM-based Bedrock calls with direct boto3 Converse/ConverseStream logic.
tests/_ai/llm/test_impl.py Update Bedrock fixtures/tests to mock boto3 client and validate new request/response handling.
marimo/_dependencies/dependencies.py Remove DependencyManager.litellm.
pyproject.toml Remove litellm from test-optional and typecheck extras.
examples/ai/chat/bedrock_example.py Remove litellm from example dependency header.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1089 to 1090


Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

TestBedrock currently validates the streaming path only. Since the implementation now falls back from converse_stream to converse on streaming errors, add a test that forces converse_stream to raise a streaming-related exception and asserts the non-streaming fallback behavior (including response parsing). This will prevent regressions in the error-handling path introduced here.

Suggested change
def test_call_streaming_error_fallback_to_converse(
self, mock_bedrock_client, test_messages, test_config
):
"""Test that Bedrock falls back to non-streaming Converse on streaming errors.
This forces converse_stream to raise a streaming-related exception and
asserts that the implementation calls converse instead and correctly
parses the non-streaming response.
"""
model_name = "anthropic.claude-3-sonnet-20240229"
# Simulate a streaming-related error from converse_stream
mock_bedrock_client.converse_stream.side_effect = RuntimeError(
"streaming error"
)
# Set up a minimal non-streaming response structure for converse
mock_bedrock_client.converse.return_value = {
"output": {
"message": {
"content": [
{"text": "Fallback response"},
]
}
}
}
model = bedrock(model_name)
result = list(model(test_messages, test_config))
# The fallback path should return the parsed text from the converse response
assert result == ["Fallback response"]
# Ensure we attempted streaming first, then fell back to non-streaming
mock_bedrock_client.converse_stream.assert_called_once()
mock_bedrock_client.converse.assert_called_once()

Copilot uses AI. Check for mistakes.
) -> dict[str, Any]:
converse_messages: list[dict[str, Any]] = []
for msg in messages:
content = str(msg.content) if msg.content else ""
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

In _build_converse_params(), message content is derived from str(msg.content) if msg.content else "", which (a) treats falsy-but-valid content (e.g. 0/False) as empty, and (b) stringifies potentially rich Python objects even when ChatMessage.parts contains the actual text to send. This can leak reprs into the prompt and diverges from the other providers’ parts-based conversions. Prefer building Bedrock message text from msg.parts when available, and only fall back to msg.content when it is a string / when parts are empty; also check msg.content is None rather than truthiness.

Suggested change
content = str(msg.content) if msg.content else ""
# Prefer building content from parts when available, to match other providers
content = ""
parts = getattr(msg, "parts", None)
if parts:
text_fragments: list[str] = []
for part in parts:
if isinstance(part, str):
text_fragments.append(part)
elif hasattr(part, "text"):
part_text = getattr(part, "text")
if part_text is not None:
text_fragments.append(str(part_text))
content = "".join(text_fragments)
if content == "":
# Fall back to msg.content when parts are empty or unavailable
if isinstance(msg.content, str):
content = msg.content
elif msg.content is None:
content = ""
else:
content = str(msg.content)

Copilot uses AI. Check for mistakes.
Comment on lines +652 to +653
if content and "text" in content[0]:
return content[0]["text"]
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

Non-streaming fallback parsing for client.converse() only returns content[0]["text"] when present. Bedrock Converse responses can contain multiple content blocks; returning only the first block can truncate the assistant response. Consider concatenating all text blocks (and safely ignoring non-text blocks) to produce the complete string response.

Suggested change
if content and "text" in content[0]:
return content[0]["text"]
if isinstance(content, list):
text_parts = []
for block in content:
if isinstance(block, dict) and "text" in block:
text_value = block["text"]
if isinstance(text_value, str):
text_parts.append(text_value)
if text_parts:
return "".join(text_parts)

Copilot uses AI. Check for mistakes.
mscolnick
mscolnick previously approved these changes Mar 24, 2026
Copy link
Copy Markdown
Collaborator

@Light2Dark Light2Dark left a comment

Choose a reason for hiding this comment

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

nice! quick change

@dmadisetti dmadisetti merged commit 270c1b7 into main Mar 24, 2026
50 of 52 checks passed
@dmadisetti dmadisetti deleted the dm/llm-remove branch March 24, 2026 16:30
@github-actions
Copy link
Copy Markdown

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.21.2-dev48

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants