Repository navigation
Conversation
resolve_litellm_model() tie-breaks a bare model name that several LiteLLM providers list by returning the alphabetically first key whenever those listings agree on price. As LiteLLM's model_cost has grown, aggregators that re-key a model under a namespaced path now sort ahead of the model's own first-party provider: with litellm 1.90.1, "grok-4.5" resolves to "openrouter/x-ai/grok-4.5" instead of "xai/grok-4.5". The estimate stays correct (same price), but the reported provider is wrong, and it breaks test_resolves_common_bare_model_names on the pinned litellm. Prefer the first-party listing: the match whose key is exactly "<litellm_provider>/<name>". When that is unique, return it before falling back to price consensus. This restores native-provider attribution and also resolves names the old rule gave up on when aggregators disagreed on price (e.g. a unique first-party listing among differently priced mirrors). Add deterministic, mock-backed regression tests for both cases so the behavior no longer rides on live LiteLLM catalog data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The change is narrowly scoped, improves determinism/provider attribution, and is covered by targeted regression tests.
Pull request overview
This PR fixes LiteLLM model-name resolution so that when a bare model name matches both aggregator re-listings and the model’s first-party provider listing, the resolver prefers the native provider key (e.g., xai/grok-4.5) rather than the alphabetically first aggregator key. This keeps provider attribution accurate and stabilizes resolution behavior as LiteLLM’s model_cost catalog grows.
Changes:
- Update
resolve_litellm_model()to prefer a unique first-party (“native”)"<litellm_provider>/<name>"match before falling back to the existing price-consensus tie-break. - Add two mock-backed regression tests covering native-vs-aggregator preference and the “unique native even when aggregators disagree on price” case.
File summaries
| File | Description |
|---|---|
strix/report/pricing.py |
Adjusts model resolution logic to prioritize unique native provider listings over aggregator re-listings. |
tests/test_pricing.py |
Adds deterministic tests that pin the resolver behavior for native-provider preference scenarios. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Greptile SummaryThis PR updates LiteLLM model resolution to prefer a unique first-party provider listing over aggregator re-listings before applying price consensus.
Confidence Score: 5/5The PR appears safe to merge with no actionable correctness, security, or repository-rule violations identified. The native-provider preference is applied only when exactly one matching catalog key identifies itself as the provider’s direct listing, while ambiguous cases retain the existing price-consensus behavior. Important Files Changed
Reviews (1): Last reviewed commit: "fix(pricing): prefer a native provider o..." | Re-trigger Greptile |
…#1279) Bump .fork-version to 1.5.3+shvia.14 and record the LiteLLM native-provider resolution fix (cherry-picked from the upstream PR usestrix#1279 branch) in FORK.md. Also realign version.md's "Versão atual" line, which was stale at shvia.12. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sync the fork line onto usestrix/strix v1.6.2 (was 1.5.3). Conflict resolution and reconciliation of the fork's patches: - warmup: adopt upstream's wait_for_import_warmup() approach and drop the fork's synchronous agents pre-import (_preimport_thread_unsafe_sdk), which upstream's own warmup rework supersedes. Remove the now-obsolete tests/test_warmup.py (upstream covers this in tests/test_import_warmup.py). - runner: keep upstream's dedicated `except RateLimitError` block; narrow the fork's F3 usage-limit handler to the non-RateLimitError (LiteLLM) case so there is no dead code, and record the same "rate_limited" exit reason. - viewer: rebuild the compiled frontend bundle (static/) from the merged source so the fork's local-PDF button rides on upstream's new frontend. - test_viewer: adapt the historical-run-data test to the fork's session-only gating (the fork removed the upstream email-verification gate). Full suite: 1740 passed (isolated HOME). The pricing native-provider fix (shvia.14, PR usestrix#1279) is carried on top. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Drive-by review — the native-route preference is the right call, but the placement here widens the change well beyond #1288, and the existing guard test only looks like it still covers the old behavior. The guard test passes by accident
"provider-a/example": {"input_cost_per_token": 1.0, "output_cost_per_token": 2.0},With no In the real registry that never happens. Every entry carries the field: So in production the How much this actually movesSweeping the registry for bare names where 121 bare model names change from Those are ~4x apart on input cost, so the resolved route drives a materially different cost estimate. That may well be the behavior you want — a first-party listing is a better guess than nothing — but it is a separate decision from #1288, where the three (Measured on litellm 1.100.0 rather than the 1.90.1 in SuggestionEither:
#1289 takes approach (1) on the same issue, so it's worth the two of you settling which contract you want rather than the placement being decided by merge order. |
…e model the same Review feedback on usestrix#1279: returning the native listing before the price check turned bare names whose routes disagree on price from None into a guess (91 of 1993 bare names on litellm 1.101). Move the preference inside the branch that already accepts the match (one route, or all routes at one price), so it only breaks ties the resolver was willing to resolve and changes nothing for differently priced routes. The guard test now uses fixtures with litellm_provider, as every real catalog entry has, and the test that asserted the wide behavior asserts None instead. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TavwVMH7PvrD68JTDfd6Hb
|
Thanks for the careful review, and for measuring it. You were right on both points, and I have confirmed them:
On #1289: I read it, and it makes the same change in the same place (it also prefers the native key only inside the |
Problem
resolve_litellm_model()resolves a bare model name that several LiteLLM providers list by returning the alphabetically first matching key when those listings agree on price. LiteLLM'smodel_costhas since grown aggregator re-listings that sort ahead of a model's first-party provider. With the pinnedlitellm==1.90.1,grok-4.5has three same-priced listings:openrouter/x-ai/grok-4.5perplexity/xai/grok-4.5xai/grok-4.5so the resolver returns
openrouter/x-ai/grok-4.5instead ofxai/grok-4.5. The cost estimate is unaffected (identical price), but the reported provider is wrong, andtests/test_pricing.py::test_resolves_common_bare_model_namesfails onmainagainst the pinned litellm.Fix
Where the resolver already accepts a match (one route, or every route at one price), prefer the first-party listing: the key that is exactly
<litellm_provider>/<name>. If that listing is unique it wins; otherwise the first key is returned as before.The preference only breaks ties the resolver was already willing to resolve. Routes that disagree on price still return
None, because the cost depends on the route. Measured over the 1,993 bare names in litellm 1.101: no name goes fromNoneto a guess, and 40 names change route, all of them same-priced ties where the old pick was an aggregator (for exampleMiniMax-M2.5:baseten/MiniMaxAI/MiniMax-M2.5→minimax/MiniMax-M2.5).An earlier revision returned the native listing before the price check. That turned 91 of those names from
Noneinto a guess among differently priced routes, so it was narrowed after review.Tests
test_resolves_common_bare_model_namespasses again.Noneeven when one of them is the native listing. The guard's fixtures carrylitellm_provider, as every real catalog entry does, so it exercises the new code.ruff format,ruff check,mypyandpytest tests/test_pricing.py tests/test_cost_tracking.pypass.🤖 Generated with Claude Code