fix(vertex_ai): return create_vertex_url result directly for openai-path partner models with custom api_base - #32380
Conversation
…ath partner models with custom api_base
Greptile SummaryFixes a malformed URL bug for Vertex AI OpenAI-compatible partner models (Llama, DeepSeek, MiniMax, etc.) when a custom
Confidence Score: 5/5Safe to merge — the change is a 2-line targeted early return that removes a URL-reconstruction branch for exactly the partner type that lacked a :verb suffix, while leaving all three rawPredict-style partners untouched. The root cause analysis in the PR description is accurate: create_vertex_url already builds the correct full URL from api_base, so re-splitting on : to extract a pseudo-verb was inherently lossy for the OpenAI-compatible path. The early return mirrors the pre-existing pattern in vertex_model_garden/main.py. All edge cases (bare host, path-bearing host, streaming, default with no api_base, mistralai regression) are covered by the new tests. No other partner providers are affected — ai21 and claude both carry :rawPredict/:streamRawPredict suffixes that make the split logic work correctly for them. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/llms/vertex_ai/vertex_llm_base.py | Adds a 2-line early return in get_complete_vertex_url for the llama (OpenAI-compatible path) partner, bypassing the :split logic that duplicated the host when a custom api_base was provided |
| tests/test_litellm/llms/vertex_ai/test_vertex_llm_base.py | Adds five parametrized tests covering: PSC bare-host api_base, PSC bare-host streaming, path-bearing api_base, default (no api_base), and mistralai rawPredict regression; all tests are mock-only with no network calls |
Reviews (1): Last reviewed commit: "fix(vertex_ai): return create_vertex_url..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will not alter performance
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing |
46d9742
into
litellm_internal_staging
…d of model refusal wording (#32388) test_text_message_blocked_by_guardrail_no_ai_response classified the model's reply against a safe_markers keyword list to decide whether the guardrail had blocked the message. gpt-realtime words its refusal of the guardrail's "say exactly" voice prompt nondeterministically, so any new phrasing outside the list turned CI red on unrelated PRs; the list had already been extended in #28191, #28200 and #29477, and drifted again to "Sorry, I can't comply with that request" (11 of the 13 failed realtime_translation_testing runs since 2026-06-24, e.g. CircleCI job 2009316 on #32380). Record every frame the proxy sends to the backend through a RecordingBackendWebSocket wrapper and assert the invariant the product actually guarantees: the blocked phrase never reaches OpenAI, only the guardrail's own conversation.item.create and response.create are forwarded (the client's reflexive response.create is dropped), and the blocked phrase never appears in AI output. Replace the fixed 0.3s/3.0s sleeps with an event-driven wait for response.done; client frames are processed sequentially so no inter-message sleep is needed. Verified by mutation: disabling the response.create drop fails the response.create count assertion, and disabling the guardrail fails the guardrail_violation assertion.
Relevant issues
Linear ticket
Resolves LIT-4240
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Live LiteLLM proxy on localhost (
.venv/bin/python litellm/proxy/proxy_cli.py --config config.yaml --port <port> --detailed_debug), making real paid Vertex AI MiniMax MaaS calls (vertex_ai/minimaxai/minimax-m2-maas, locationglobal); no mocks anywhere. The config has two customapi_basemodels plus a control with noapi_baseminimax-psc-pathexercises a path-bearingapi_basethe way a PSC or gateway host in front of Vertex sees it: a local nginx listener on127.0.0.1:53159forwards/vertex/to the realhttps://aiplatform.googleapis.com/, so every request that reaches it hits the real Google API and returns real completionsBefore (litellm_internal_staging, commit b8248a2)
The path-bearing
api_basefails with a 404, non-streaming and streaming, because the URL builder splits the already-complete URL on:and grafts everything after the scheme back onto theapi_baseThe proxy log (
proxy-before.log) shows the malformed URLs LiteLLM actually called, with the port and path duplicated after a stray colon, plus the?alt=ssesuffix on the streaming callTo be fully transparent about the bare-host model:
minimax-pscsucceeds on the base branch for both non-streaming and streaming, because #32367 (already on the base branch) grafts the default path onto bare-host api_bases and because the Google endpoint happens to tolerate the extra?alt=ssequery param that the custom-api_base streaming path appends. The streaming URL on base still diverges from what the default no-api_base path sendsAfter (litellm_fix_vertex_psc_partner_api_base, commit 64c4cfe)
Same config and the exact same curls against the fix branch (proxy restarted on port 53279). The previously failing path-bearing calls now return real completions
The proxy log (
proxy-after.log) now shows a single clean host and path for every call, with no?alt=ssedivergence on streaming; these are the only outbound URL shapes across the whole after runThe bare-host
minimax-pscalso succeeded non-streaming and streaming on the fix branch, and theminimax-controlmodel with noapi_basesucceeded in both the before and the after runsType
🐛 Bug Fix
Changes
Users calling Vertex AI partner models served through the OpenAI-compatible route (Llama, DeepSeek, Qwen, MiniMax, Moonshot, ZAI, GPT-OSS, Gemma MaaS) with a custom
api_base, for example a GCP Private Service Connect host likehttps://aiplatform-myendpoint.p.googleapis.com, got a malformed request URL with the host duplicated (https://HOST://HOST/v1/projects/PROJ/locations/LOC/endpoints/openapi/chat/completions) and the call failed with a 404Root cause:
VertexBase.get_complete_vertex_urlfirst builds the complete, correct URL viacreate_vertex_url(which already honors the customapi_base), then splits that URL on:to extract a:rawPredictstyle verb and re-prefixes it onto the customapi_basethrough_check_custom_proxy. The OpenAI-compatible path has no:verbsuffix, so the split grabs everything after the scheme and the host gets duplicated. Streaming requests through this path also picked up a?alt=ssesuffix that the default no-custom-api_base path never sends to the openapi chat completions endpointThe fix returns the
create_vertex_urlresult directly for this partner path, mirroring whatlitellm/llms/vertex_ai/vertex_model_garden/main.pyalready does for the same reason. The rawPredict style partners (mistral, ai21, claude) are untouched and keep the existing{api_base}:{endpoint}custom proxy behaviorRegression tests in
tests/test_litellm/llms/vertex_ai/test_vertex_llm_base.pyassert the exact URL for a PSC style bare host, a path-bearingapi_base, streaming parity (no?alt=sse), the default no-api_base path, and the unchanged mistral{api_base}:rawPredictformat