Skip to content

fix(litellm): use underlying provider for metadata, drop eager serialization#594

Merged
Abhijeet Prasad (AbhiPrasad) merged 3 commits into
mainfrom
fix/litellm-provider-metadata-drop-eager-serialization
Jul 17, 2026
Merged

fix(litellm): use underlying provider for metadata, drop eager serialization#594
Abhijeet Prasad (AbhiPrasad) merged 3 commits into
mainfrom
fix/litellm-provider-metadata-drop-eager-serialization

Conversation

@starfolkai

@starfolkai starfolkai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Align the LiteLLM integration with .agents/skills/sdk-integrations/SKILL.md. Audit turned up four issues:

  • Wrong metadata.provider. SKILL §Completion-style explicitly says LiteLLM must set metadata.provider to the underlying provider (openai / cohere / openrouter / …), not the framework name. Fixed by routing every span through _resolve_provider(model) which calls litellm.get_llm_provider(model); falls back to "litellm" only when the lookup fails. @lru_cache(maxsize=256) on the resolver so the hot path doesn't re-dispatch through litellm's routing table per call.
  • Excess serialization. Every non-streaming wrapper called _try_to_dict(response) on the full pydantic response then indexed into it — but bt_json already serializes at log time. Replaced with direct attribute reads via a new shared _get_field(obj, key, default) helper in integrations/utils.py that works on both dicts and SDK objects (openai / anthropic / cohere had all been open-coding this pattern). Streaming paths left intact.
  • Dead code. _is_litellm_patched was only used by crewai until fix(crewai): add provider metadata, route tools to metadata, drop eager serialization #587 moved crewai to the "wrapper never owns tokens" pattern; no remaining callers anywhere. Removed the helper and its two subprocess tests.
  • Excessive comments. Trimmed the multi-example wrap_litellm / _is_litellm_patched docstrings and the """wrapt wrapper for litellm.X.""" one-liner on every wrapper.

Also lifted the rerank output cap (results[:100]) into integrations/utils.RERANK_OUTPUT_MAX_RESULTS and documented the "rerank exception to no-silent-caps" rule in the SKILL. Cohere carries the same magic number and can adopt the shared constant in a follow-up.

Test plan

  • Updated existing VCR-backed tests to assert the underlying provider (openai, cohere, openrouter) instead of "litellm". No cassette re-recording — HTTP requests unchanged, span-shape assertions only.
  • Added positive metadata["provider"] == "openrouter" assertion to the existing OpenRouter cassette test as coverage for the new resolver.
  • Dropped the two _is_litellm_patched subprocess tests alongside the helper.
  • No new mocks/fakes, no cassette re-recording.
  • ruff check + ruff format --check on touched files — clean.
  • Reviewer: cd py && nox -s "test_litellm(latest)" and cd py && nox -s "test_litellm(1.74.0)" (author's box has a broken Python 3.14 install).

🤖 Generated with Claude Code

Created by abhijeet

Slack thread

…ization

Align the LiteLLM integration with .agents/skills/sdk-integrations/SKILL.md.
Audit against the SKILL surfaced four issues:

- **`metadata.provider` was the framework name.** SKILL §Completion-style
  explicitly says LiteLLM must set `metadata.provider` to the underlying
  provider (openai / cohere / openrouter / ...), not "litellm". Fixed by
  routing every span through `_resolve_provider(model)` which calls
  `litellm.get_llm_provider(model)`; falls back to `"litellm"` only if the
  lookup fails. `@lru_cache(maxsize=256)` on the resolver so the hot path
  doesn't re-dispatch through litellm's routing table per call.
- **Excess serialization.** Every non-streaming wrapper called
  `_try_to_dict(response)` on the full pydantic response then indexed
  into it — but `bt_json` already serializes at log time. Replaced with
  direct attribute reads via a shared `_get_field(obj, key, default)`
  helper that works on both dicts and SDK objects (openai / anthropic /
  cohere had all been open-coding this). Streaming paths left intact.
- **Dead code.** `_is_litellm_patched` was only used by crewai until #587
  moved crewai to the "wrapper never owns tokens" pattern; no remaining
  callers anywhere. Removed the helper and its two subprocess tests.
- **Excessive comments.** Trimmed the huge `wrap_litellm` /
  `_is_litellm_patched` docstrings and the "wrapt wrapper for litellm.X"
  one-liner on every wrapper.

Also lifted the rerank output cap (`results[:100]`) into
`integrations/utils.RERANK_OUTPUT_MAX_RESULTS` and documented the
"rerank exception to no-silent-caps" rule in the SKILL — cohere carries
the same magic number and can adopt the shared constant in a follow-up.

## Test plan

- [x] Updated existing VCR-backed tests to assert the underlying
      provider (`openai`, `cohere`, `openrouter`) instead of `"litellm"`.
      No cassette re-recording (HTTP requests unchanged; span-shape
      assertions only).
- [x] Added positive `metadata["provider"] == "openrouter"` assertion
      to the existing OpenRouter cassette test as coverage for the new
      resolver.
- [x] Dropped the two `_is_litellm_patched` subprocess tests alongside
      the helper.
- [x] **No new mocks/fakes, no cassette re-recording.**
- [x] `ruff check` + `ruff format --check` on touched files — clean.
- [ ] Reviewer to run: `cd py && nox -s "test_litellm(latest)"` and
      `cd py && nox -s "test_litellm(1.74.0)"` (author's box has a
      broken Python 3.14 install).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@starfolkai
starfolkai Bot force-pushed the fix/litellm-provider-metadata-drop-eager-serialization branch from 1bace7d to ba72da4 Compare July 17, 2026 20:40
starfolkbot and others added 2 commits July 17, 2026 20:54
Follow-up to the provider-metadata change. CI surfaced two categories:

- Three ``auto_test_scripts/`` subprocess scripts (test_auto_litellm,
  test_patch_litellm_responses, test_patch_litellm_aresponses) still
  hardcoded ``provider == "litellm"``. All three call ``gpt-4o-mini`` →
  expect ``"openai"``.
- ``test_litellm_text_completion_metrics`` / ``test_litellm_atext_completion_metrics``
  use ``gpt-3.5-turbo-instruct``, for which
  ``litellm.get_llm_provider`` returns ``"text-completion-openai"`` (its
  legacy text-completion endpoint category, distinct from chat
  completions). Assertion updated to that string — it IS the provider
  identifier litellm routes to.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
``litellm.get_llm_provider`` reports endpoint-category identifiers for
legacy ``/v1/completions`` routes (``text-completion-openai``,
``azure_text``, ``text-completion-codestral``, ``text-completion-inception``).
The SKILL says ``metadata.provider`` names whose pricing applies — same
billing entity as the chat variant — so filtering by ``openai`` in the
UI should include both chat and text-completion calls.

Added a small ``_PROVIDER_ALIASES`` table that folds those routing keys
back to the pricing provider before returning from ``_resolve_provider``.
List sourced from litellm ``constants.py``.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@AbhiPrasad
Abhijeet Prasad (AbhiPrasad) merged commit 59c9d5b into main Jul 17, 2026
82 checks passed
@AbhiPrasad
Abhijeet Prasad (AbhiPrasad) deleted the fix/litellm-provider-metadata-drop-eager-serialization branch July 17, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants