Repository navigation
Conversation
|
| assert "y-api" in litellm.provider_list | ||
|
|
||
| def test_y_api_json_config_exists(self): | ||
| """Test that y-api is configured in providers.json""" | ||
| from litellm.llms.openai_like.json_loader import JSONProviderRegistry | ||
|
|
||
| assert JSONProviderRegistry.exists("y-api") | ||
|
|
||
| y_api = JSONProviderRegistry.get("y-api") | ||
| assert y_api is not None | ||
| assert y_api.base_url == BASE_URL |
There was a problem hiding this comment.
These assertions, along with the endpoint matrix checks, inspect static configuration instead of provider behavior, violating the repository's functional-testing requirement before merge
Context Used: CLAUDE.md (source)
| assert JSONProviderRegistry.exists("y-api") | ||
|
|
There was a problem hiding this comment.
Comments restate simple assertions
These comments restate configuration and vendor behavior, violating the repository rule that source comments must explain necessary complex logic or direct tools
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Docs page opened: BerriAI/litellm-docs#1515 So the |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
a59a593 to
7a898ec
Compare
|
Fixed the one real failure from the previous run. Pushed as a single amended commit (
|
| Check | Clean tree (a59a593) |
With fix (7a898ec) |
|---|---|---|
test_every_backend_provider_is_listed_in_add_model_or_frozen_as_unlisted |
fail | pass |
tests/test_litellm/llms/openai_like/test_y_api.py |
8 pass | 10 pass |
tests/code_coverage_tests/check_provider_folders_documented.py |
pass | pass |
I confirmed the delta by stashing the fix and re-running: the drift test is the only test
that changes state. The other five failures in that file
(test_autorouter_presets_*, test_fetch_remote_autorouter_presets_*) fail identically on
the clean tree and are unrelated to providers.
Not mine
Validate PR title shows red on the earlier commit, but that run was created at 03:59:41 with
the previous title — it logged "(feat) add Y-API...". A re-run at 04:00:49 against the
current title passed. The new commit triggers a fresh run either way.
7a898ec to
162625f
Compare
|
All three findings addressed in P1 —
|
| caller sends | body that left litellm |
|---|---|
max_tokens=16 |
{"max_tokens": 16} |
max_completion_tokens=16 |
{"max_completion_tokens": 16} |
The reason is the one the finding implies: these model ids are not in the OpenAI cost map, so nothing translates the parameter on this path. The four openai/* models reject the legacy name outright —
Unsupported parameter: 'max_tokens' is not supported with this model.
Use 'max_completion_tokens' instead.
— so a caller using max_tokens got a 400 on 4 of the 15 models. Fixed by declaring the mapping the schema exists for:
"param_mappings": { "max_tokens": "max_completion_tokens" }After: max_tokens=16 → {"max_completion_tokens": 16}, max_completion_tokens=16 unchanged, no cap → neither key present. The eleven non-openai/* models are unaffected, because both spellings now arrive under the one name the relay accepts.
This is the mirror of what the eighteen existing JSON providers declare (max_completion_tokens → max_tokens) — their upstreams are older; this relay is the other way round.
P2 — "tests inspect structure only" (addressed)
Fair, and worth noting a config-only assertion could not have caught the P1 above, because the config was the thing that was wrong. Added TestYApiTokenParamMapping, which drives map_openai_params and asserts the outgoing parameter name for all three cases. The rest of the file is structural by nature — provider resolution, base-URL override and the two endpoint matrices are configuration surfaces with no behaviour to exercise.
P2 — "comments restate simple assertions" (fixed)
Removed. The comment sitting above assert y_api.param_mappings == {} was also factually obsolete once the mapping was added, so it was the wrong thing to keep either way.
Verification
tests/test_litellm/llms/openai_like/test_y_api.py— 13 passedtests/test_litellm/proxy/public_endpoints/test_public_endpoints.py— 15 passed (drift test included)tests/code_coverage_tests/check_provider_folders_documented.py— passedtests/test_litellm/llms/openai_like/— 187 passed, 17 failed; the 17 aretest_model_info.py(15) andtest_cognition_provider.py(2), unchanged from a clean checkout of this branch
|
@greptileai review The previous review was on
All CI on the current head is green. |
…vider Y-API is an OpenAI-compatible relay fronting DeepSeek, Z.ai, Moonshot, Tencent, Xiaomi, Qwen and OpenAI models behind one key. It also serves the Anthropic Messages API and the OpenAI Responses API. Registered through the declarative JSON registry, so no Python provider module is needed: - litellm/llms/openai_like/providers.json: base URL, YAPI_API_KEY / YAPI_API_BASE, a max_tokens -> max_completion_tokens mapping, and the three supported endpoints. - litellm/types/utils.py: LlmProviders.Y_API. - litellm/constants.py: added to openai_compatible_providers, openai_compatible_endpoints and openai_text_completion_compatible_providers. - provider_endpoints_support.json + the runtime backup: chat_completions, messages and responses true; embeddings false. - provider_create_fields.json: an Add Model form entry, without which the provider would be invisible in the dashboard. - tests: resolution, api_base override, router config, both endpoint matrices, dashboard registration, and the token-parameter rewrite. Model ids keep the upstream organization prefix, so every model string has two slashes (`y-api/deepseek/deepseek-v4-flash`). Provider resolution splits on the first slash and forwards the rest unchanged; there is a test for it. The `openai/*` models this relay fronts reject `max_tokens` outright and require `max_completion_tokens`. LiteLLM does not translate the parameter for this provider, so the mapping rewrites the caller's `max_tokens` to the name the relay expects. Both spellings then arrive identically, and a caller who sets no cap still sends none.
162625f to
3c6a6c0
Compare
| "cognition", | ||
| "scx-ai", | ||
| "sail", | ||
| "y-api", # Y-API - JSON-configured provider |
There was a problem hiding this comment.
Low: OpenAI credential forwarded to Y-API
Adding y-api here also adds it to OPENAI_AUDIO_TRANSCRIPTION_PROVIDERS and the speech dispatch. An authenticated caller can submit an audio request for a direct y-api/... model without a Y-API key; transcription() and speech() then fall back to OPENAI_API_KEY while retaining Y-API's base URL, sending the deployment's OpenAI credential to that third party. Keep Y-API out of the generic audio-capable set, or gate these transports using its declared supported_endpoints and never use OpenAI credential fallbacks for another provider.
There was a problem hiding this comment.
Reproduced this against a loopback echo server (base_url pointed at 127.0.0.1, so nothing left the machine). The finding is real — with one correction on where the fix has to land.
Repro, as reported. YAPI_API_KEY unset, OPENAI_API_KEY set:
| call | what the echo server received |
|---|---|
litellm.transcription("y-api/whisper-1", …) |
POST /v1/audio/transcriptions, Authorization: Bearer <OPENAI_API_KEY> |
litellm.speech("y-api/tts-1", …) |
POST /v1/audio/speech, same header |
Both resolved Y-API's own base URL from providers.json and attached the OpenAI credential, so the mechanism you describe is correct.
But deleting this line closes the audio surface, not the credential fallback. With "y-api" removed from openai_compatible_providers and the environment unchanged:
| call | outcome |
|---|---|
transcription() |
ValueError: Unmapped provider passed in. — no request leaves |
speech() |
Unable to map the custom llm provider=y-api to a known provider= — no request leaves |
completion("y-api/deepseek/…") |
POST /v1/chat/completions, Authorization: Bearer <OPENAI_API_KEY> |
responses("y-api/deepseek/…") |
POST /v1/responses reaches the third-party base with no Authorization header at all |
So the fallback itself lives in the shared openai_like credential resolution, not in this list entry: a JSON provider with no configured key still sends whatever OPENAI_API_KEY holds to that provider's base on the chat path. Opting Y-API out of the audio set makes the flagged case go away; it does not make credential forwarding impossible.
Scope. Of the 30 entries in providers.json, 17 are in openai_compatible_providers, and 16 of those declare no audio endpoint in supported_endpoints yet still land in OPENAI_AUDIO_TRANSCRIPTION_PROVIDERS through the same derivation — publicai, helicone, cognition, chutes, poe, nano-gpt, darkbloom, libertai, tensormesh, parasail among them. Adding y-api to the list follows the existing convention rather than introducing a new one.
Where I think a real fix belongs, and I would rather send it separately so this PR stays a plain provider addition:
- Derive
OPENAI_AUDIO_TRANSCRIPTION_PROVIDERSfrom each JSON provider's declaredsupported_endpointsinstead of wholesale fromopenai_compatible_providers— that closes the audio surface for all 17 providers at once, and theproviders.jsondata for it is already there. - Skip the
OPENAI_API_KEYfallback when the resolved base URL belongs to a different provider — this is the credential-forwarding step proper, and without it the chat path above still reproduces.
Say the word on which you want and I'll open it as its own PR. If you'd rather Y-API just not inherit the audio channel while (1) is pending, deleting line 1027 is a one-line change here — the four calls above show it costs nothing on the paths this provider declares (/v1/chat/completions, /v1/completions, /v1/responses, /v1/messages all still dispatched correctly).
PR overviewThis pull request adds Y-API as a JSON-configured, OpenAI-compatible provider in LiteLLM, including provider registration and request dispatch integration. One security issue remains open: an authenticated caller could route an audio request through Y-API and cause the deployment’s OpenAI API key to be sent to Y-API because of credential fallback behavior. Exploitation requires authenticated request access and use of the affected audio paths, but it could disclose a sensitive third-party credential. Open issues (1)
Fixed/addressed: 0 · PR risk: 6/10 |
|
Re: the open Three corrections to what I wrote on the inline thread here on 09-17, because two of them change where the fix has to land:
Scope of #43784: audio capability is derived from each provider's declared What #43784 deliberately does not change: the chat path. A JSON provider with no key of its own still resolves to Also noting one CI item that is not ours to fix: |
# Conflicts: # litellm/constants.py # litellm/llms/openai_like/providers.json # litellm/types/utils.py
|
Rebased onto
Net diff against Local verification, using the repo's own lockfile ( Two notes on things that are not from this PR, so a reviewer doesn't have to re-derive them:
On the earlier security review: the credential fallback it flagged is still live on current main — |
What
Adds Y-API as a JSON-configured OpenAI-compatible provider.
Y-API (https://y-api.bestvirtualgoods.com) is a relay that fronts DeepSeek / Z.ai / Moonshot / Tencent / Xiaomi / OpenAI models behind a single key, exposing the OpenAI, Anthropic Messages, and Responses APIs.
It fits the JSON-configured provider path, so this PR is configuration plus the registration points that path requires — no Python provider module.
Model ids keep the upstream organization prefix, so they contain a slash of their own (
y-api/deepseek/deepseek-v4-flash). Resolution splits on the first slash; the test below covers that case specifically.Files
litellm/llms/openai_like/providers.jsonlitellm/types/utils.pyLlmProviders.Y_APIlitellm/constants.pyopenai_compatible_endpoints,openai_compatible_providers,openai_text_completion_compatible_providersprovider_endpoints_support.jsonlitellm/provider_endpoints_support_backup.jsonGET /public/endpointstests/test_litellm/llms/openai_like/test_y_api.pyEndpoints
Declared from live calls against the API rather than from the vendor's docs:
/v1/chat/completions/v1/completions/v1/responses/v1/messages/v1/embeddingsNo available channel for model … under group y-api (distributor)The relay serves no embedding model, so
embeddingsis advertisedfalse. Everything else above istrue.param_mappings:max_tokens→max_completion_tokens. The relay acceptsmax_completion_tokens, and the fouropenai/*models it fronts require it — they reject the legacy name outright withUnsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead.LiteLLM's
openai_gptbase class does not translate the parameter here, because these model ids are not in the OpenAI cost map, so a caller'smax_tokensis forwarded verbatim. Captured by pointing the provider at a local echo server and reading the outbound body:max_tokens=16{"max_tokens": 16}{"max_completion_tokens": 16}max_completion_tokens=16{"max_completion_tokens": 16}{"max_completion_tokens": 16}So the mapping repairs the four
openai/*models without touching the other eleven: both caller spellings arrive under the name the relay expects, and a caller who sets no cap still sends none. It is the mirror of what the eighteen existing JSON providers declare — they rewrite the modern name down to the legacy one because their upstreams are older; this relay is the other way round.Verification
Repo checks:
tests/test_litellm/llms/openai_like/test_y_api.py— 13 passed, including three that drivemap_openai_paramsand assert the outgoing parameter name rather than the configtests/code_coverage_tests/check_provider_folders_documented.py— passed (29openai_likeproviders, 178 documented entries)tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py— 15 passed (the Add Model dropdown drift test included)tests/test_litellm/llms/openai_like/— 187 passed, 17 failed. The 17 are pre-existing:test_model_info.py(15) andtest_cognition_provider.py(2) fail identically on a clean checkout of this commit (verified by stashing the change and re-running).Also exercised end to end through a local proxy started from this branch, with the real key against the real endpoint:
Notes for the reviewer
provider_endpoints_support.jsonpointsurlathttps://docs.litellm.ai/docs/providers/y-api. The docs site lives inBerriAI/litellm-docs, so that page is not in this PR — say the word and I'll open the companion PR there, or drop theurlfield (aschutesandpoedo) if you'd rather keep the matrix link-free for now.litellm/provider_endpoints_support_backup.jsoneven though it is 29 providers behindprovider_endpoints_support.json(missingmeta,aihubmix,nvidia_riva,mongodb, and the most recent JSON providerempiriolabs). I added the entry because the omission is user-visible: without itGET /public/endpointsdoes not list y-api at all. Happy to drop that file from the PR if the backup is deliberately frozen.openai_text_completion_compatible_providers. Included because/v1/completionsreturns 200 for the DeepSeek models. If you'd prefer to advertise the legacy completions route only for providers you've verified more broadly, this is the line to drop.