fix(models): resolve nested provider behind litellm_proxy prefix - #6578
fix(models): resolve nested provider behind litellm_proxy prefix#6578vietnamesekid wants to merge 1 commit into
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
`litellm_proxy` selects the transport, not the model family, but `_get_provider_from_model` took the first path segment and classified `litellm_proxy/azure/<deployment>` as the `litellm_proxy` provider. Since that value is not in `_FILE_ID_REQUIRED_PROVIDERS`, the Azure/OpenAI file-upload path was skipped and PDFs were emitted as a bare `file_data` block. Azure rejects the resulting `input_file` item with `Missing required parameter: 'input[N].content[M]'` before inference. The same first-segment assumption also broke the model-family predicates, so proxied Anthropic models lost thinking-block formatting and proxied Gemini models were not recognized as Vertex/Gemini routes. Strip the routing prefix before provider and model-family detection, so a proxied model is shaped exactly like its direct equivalent. A bare `litellm_proxy/<deployment>` has no nested provider and still falls back to the model-name heuristics. Non-proxied model strings are unaffected. Fixes google#6538
8aaaa02 to
ebc8787
Compare
|
@llalitkumarrr @ross-p I verified this branch against the reproduction in #6538. It fixes what the issue diagnoses, but while testing I found a second layer that this PR does not cover. Details below, and I would like your call on scope before I push anything further. Everything below is reproducible. The layer 1 output comes from the commit currently on this PR ( Layer 1: payload shape (what this PR fixes)Running the repro from the issue against a local HTTP endpoint standing in for the upload target, so these are real requests rather than assertions about mocks. On On this PR, with The upload happens and a real One note on the script as written in the issue: it no longer prints, it raises. That is the fix working. The traceback goes through Which leads to the part I want to raise. Layer 2: the upload does not go through the proxy
So after this PR the request splits in two:
That is invisible if you happen to have Same PR commit, same script, only difference is Nothing is sent. So for a proxy-only setup this PR turns "Azure rejects the request" into "the client cannot upload at all". Better in that it fails fast and locally, but still broken. Proposed fix for layer 2Forward I have this working in vietnamesekid/adk-python@3b96796 (2 files, +182/-9). Same proxy-only scenario, The For the record, I first tried passing Known limit of that fixIt only covers proxy settings passed to the constructor: LiteLlm(model="litellm_proxy/azure/my-deployment", api_base=..., api_key=...)If the proxy is configured through Covering that means reading those env vars in ADK as a fallback. I have not done it, since it is a judgment call about how much LiteLLM configuration ADK should reimplement, and I would rather ask than guess. What I would like decidedThree options, and I am happy with any of them:
I lean towards 1, since a proxy-only setup is the common case and layer 1 alone does not make the reported scenario work end to end. Also worth flaggingThe same first-segment assumption affects four other helpers, already included in this PR:
In practice proxied Anthropic models lose thinking block formatting and proxied Gemini models are not recognized as Vertex or Gemini routes. Same root cause, so I bundled them, but they can also be split out if you would rather keep this PR narrow. What I could not verifyI do not have an Azure backed proxy deployment, so none of this is confirmed against a live endpoint. Everything above is the request ADK builds and where it sends it, tested locally against a stub server. @ross-p, if you can run this branch against your setup that would close the gap. One specific question that decides whether layer 2 affects you today: do you have Test status: on the PR commit, 376 passed in |
Link to Issue or Description of Change
Problem:
_get_provider_from_modelsplits the model string on the first/and treatsthat segment as the provider. That assumption breaks for nested LiteLLM Proxy
identifiers, because
litellm_proxynames the transport, not the model family:litellm_proxyis not in_FILE_ID_REQUIRED_PROVIDERS({"openai", "azure"}),so the Azure/OpenAI file-upload path in
_get_contentnever runs. The PDF goesout as an inline
file_datablock instead of an uploadedfile_id. When theproxy translates that for the Azure Responses API, Azure rejects the content
item before inference:
While tracing this I found the same first-segment assumption in four more
helpers, so the blast radius is wider than the PDF case in the issue. Every
provider-specific behavior silently degrades once a model is reached through
the proxy:
litellm_proxy/...input_get_provider_from_modellitellm_proxy/azure/gpt-4litellm_proxyazure_is_anthropic_modellitellm_proxy/anthropic/claude-4FalseTrue_is_litellm_vertex_modellitellm_proxy/vertex_ai/gemini-2.5-flashFalseTrue_is_litellm_gemini_modellitellm_proxy/vertex_ai/gemini-2.5-flashFalseTrue_extract_gemini_model_from_litellmlitellm_proxy/vertex_ai/gemini-2.5-provertex_ai/gemini-2.5-progemini-2.5-proPractical effect beyond the PDF bug: proxied Anthropic models lose thinking
block formatting, and proxied Gemini models are not recognized as Vertex or
Gemini routes.
Worth noting that
litellm.get_llm_provider()also returnslitellm_proxyfor these strings. That is correct for LiteLLM, which only needs to know where
to send the request. ADK uses the value for something different, namely how to
shape the payload, and that has to follow the provider that actually serves
the model. So the fix belongs here rather than upstream.
Solution:
Add
_strip_proxy_prefix()and call it before provider and model familydetection. A proxied model is then shaped exactly like its direct equivalent.
Three things I deliberately kept intact:
helper returns the input unchanged when there is nothing to strip.
litellm_proxy/<deployment>has no nested provider, so theremainder falls through to the existing model name heuristics.
litellm_proxy/azure-gpt-4resolves toazure; an opaquelitellm_proxy/my-deploymentresolves to"".LiteLLM_Proxy/azure/gpt-4works.One behavior change worth calling out for review: an opaque
litellm_proxy/my-deploymentnow returns""rather than"litellm_proxy".I grepped for consumers of that literal and there are none.
""is alreadythe established "provider not determinable" value in this function, so
unknown deployments now take the generic path, which is the honest answer when
the backing provider cannot be known from the string alone.
Testing Plan
Unit Tests:
Extended the existing
test_get_provider_from_modeltable with the nestedproxy forms, the case insensitive variant, and both bare deployment cases.
Added
test_model_family_detection_through_litellm_proxy, which pins all fourmodel family helpers across proxied and direct strings so the two stay in
lockstep.
Added
test_get_content_pdf_proxied_azure_uses_file_idas the regression testfor the reported symptom. It drives
_get_contentfrom the model string andasserts the upload actually happens with
custom_llm_provider="azure", whichis the part that was silently skipped.
I checked that the new tests actually fail without the fix rather than passing
by construction. Reverting just the
_strip_proxy_prefixcall in_get_provider_from_modeland rerunning:Formatted with
pyink25.12.0 andisort8.0.1, matching the pinned versionsin
.pre-commit-config.yaml.Manual End-to-End (E2E) Tests:
Verified the conversion decision without network access, mocking the upload so
the emitted content block is visible. This is the assertion that matters,
since it is the payload Azure rejects:
Before:
After:
The proxied Azure model now produces a payload byte for byte identical to the
direct Azure model. The opaque deployment still uses
file_data, which is thecorrect fallback when the backing provider is unknowable from the string.
I did not run this against a live Azure backed proxy, since I do not have a
deployment to test with. The reporter in #6538 is set up for that and could
confirm on a real endpoint.
Checklist
Additional context
Scope note: I limited this to reading the nested provider out of the model
string. Routing, credentials, and how LiteLLM itself resolves the proxy are
untouched.
If maintainers would rather keep the blast radius to the reported PDF bug, the
change to
_get_provider_from_modelalone fixes #6538 and the four modelfamily helpers can be split into a follow up. I kept them together because
they share one root cause, and fixing only the provider lookup leaves the same
bug reachable through the Anthropic and Gemini paths.