Repository navigation
feat(decisions): backport /v1/systemone, OpenAI-format /v1/decisions and the OpenAI Decisions provider to stable/1.104.x for v1.104.2 (#44236, #45184, #45214) - #45190
Conversation
…e providers (#44236) Backport of #44236 to stable/1.104.x. Cherry-picked from 8b1990b (main). Adapted to this line: the lazy feature helpers are ported by hand, managed agent authorization and the integration suites are not on this line, and the lazy-disabled precedence test is dropped since attach_lazy_features takes no feature list here.
…format at /v1/decisions Backport of #45184 to stable/1.104.x. Cherry-picked from 3339469 (litellm_systemone_endpoint, #45184 is not merged yet). The playground System One tab and managed agent authorization are not on this line, so those hunks are dropped, and the lazy-disabled precedence test is dropped as in the #44236 pick.
|
|
d675b6d to
b765179
Compare
|
| request=request, | ||
| fastapi_response=fastapi_response, | ||
| user_api_key_dict=user_api_key_dict, | ||
| route_type="adecisions", |
There was a problem hiding this comment.
High: Decisions requests bypass TPM limits
An authenticated caller can repeatedly send large state/questions payloads through either new API without consuming their key/team TPM allowance: parallel_request_limiter_v3 does not estimate those input fields, and its success handler ignores DecisionsResponse, recording zero tokens and refunding any TPM reservation. Add decisions-specific input estimation and response-usage handling to the limiter before enabling this route.
There was a problem hiding this comment.
Decisions TPM accounting matches #44236 as merged on main. This backport mirrors it, and limiter support for decisions belongs in a main follow-up
PR overviewThis PR backports the /v1/systemone endpoint and the OpenAI-format /v1/decisions endpoint to stable/1.104.x for v1.104.2. One security issue remains open, with none addressed so far. Authenticated callers can repeatedly submit large decisions payloads without consuming their key or team token-per-minute allowance because the limiter does not account for decisions inputs or response usage. This permits token-limit bypass and potentially excessive resource consumption through either new endpoint. Open issues (1)
Fixed/addressed: 0 · PR risk: 6/10 |
b765179 to
ef6cbbc
Compare
|
@greptileai please re-review the new tip: non-JSON bodies now return 400 and OpenAI-format questions are capped at the System One limit |
System One requests to openai/ models are translated to OpenAI's Decisions shape on the way out and OpenAI's answers are translated back, so both /v1/systemone and /v1/decisions can route to gpt-6-luna. (cherry picked from commit b94bbbc)
(cherry picked from commit d209a85)
…s IR System One and OpenAI-format bodies now convert to one internal representation, and each provider translates it to its own wire format. OpenAI deployments receive the caller's messages, images, names, typed choice values, level descriptions and safety_identifier unchanged. System One providers return a 400 for image input. Cached and cache-write tokens are priced on both response shapes, and provider response extras survive the round trip. (cherry picked from commit 0dd207f)
…table/1.104.x The public hidden_params accessor comes from #44668, which is not on stable/1.104.x, so the backported test reads the private attribute like the rest of the file
|
@greptileai review this again: the branch now also carries the #45214 OpenAI Decisions provider and shared IR picks |
|
bugbot run |
…hout them and reject single-option questions
|
bugbot run |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 7446e02. Configure here.
…04.2) (#374) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/berriai/litellm](https://images.chainguard.dev/directory/image/wolfi-base/overview) ([source](https://github.com/BerriAI/litellm)) | patch | `v1.104.0` → `v1.104.2` | --- ### Release Notes <details> <summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary> ### [`v1.104.2`](https://github.com/BerriAI/litellm/releases/tag/v1.104.2) [Compare Source](BerriAI/litellm@v1.104.1...v1.104.2) #### Verify Docker Image Signature All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](BerriAI/litellm@0112e53). **Verify using the pinned commit hash (recommended):** A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \ ghcr.io/berriai/litellm:v1.104.2 ``` **Verify using the release tag (convenience):** Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/v1.104.2/cosign.pub \ ghcr.io/berriai/litellm:v1.104.2 ``` Expected output: ``` The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key ``` *** #### What's Changed - feat(decisions): backport /v1/systemone, OpenAI-format /v1/decisions and the OpenAI Decisions provider to stable/1.104.x for v1.104.2 ([#​44236](BerriAI/litellm#44236), [#​45184](BerriAI/litellm#45184), [#​45214](BerriAI/litellm#45214)) by [@​mateo-berri](https://github.com/mateo-berri) in [#​45190](BerriAI/litellm#45190) **Full Changelog**: <BerriAI/litellm@v1.104.1...v1.104.2> ### [`v1.104.1`](https://github.com/BerriAI/litellm/releases/tag/v1.104.1) [Compare Source](BerriAI/litellm@v1.104.0...v1.104.1) #### Verify Docker Image Signature All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](BerriAI/litellm@0112e53). **Verify using the pinned commit hash (recommended):** A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \ ghcr.io/berriai/litellm:v1.104.1 ``` **Verify using the release tag (convenience):** Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules: ```bash cosign verify \ --key https://raw.githubusercontent.com/BerriAI/litellm/v1.104.1/cosign.pub \ ghcr.io/berriai/litellm:v1.104.1 ``` Expected output: ``` The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key ``` *** #### What's Changed - chore(release): refresh dependencies on stable/1.104.x and cut 1.104.1 by [@​yuneng-berri](https://github.com/yuneng-berri) in [#​44777](BerriAI/litellm#44777) - chore(docker): bump pgbouncer to 1.26.0 by [@​devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#​45012](BerriAI/litellm#45012) **Full Changelog**: <BerriAI/litellm@v1.104.0...v1.104.1> </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMzMuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjEzMy4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.greyrock.io/todd/home-ops/pulls/374
TLDR
Backport of #44236, #45184 and #45214 to stable/1.104.x, one commit per source commit with the original author kept. #44236 is merged on main and comes from its squash 8b1990b. #45184 and #45214 are still open, so they come from their branch commits: 3339469, 29e7154, 7ef1329 and b6bb885 for #45184, then b94bbbc, d209a85, 0dd207f and 1348de8 for #45214. This PR needs a refresh if either changes before it merges. On this line the #44236 pick needed hand work: the lazy route helpers in
_lazy_features.pyare ported by hand, managed agent authorization and the integration suites undertests/integrationare not on the line so their hunks are dropped, and the lazy-disabled precedence test is dropped becauseattach_lazy_featurestakes no feature list here. The #45184 pick drops the playground System One tab files, which are not on the line, and thehidden_paramsproperty, which comes from #44668. The integration test follow-up 77872f6 is dropped since the decisions integration suites are not on the line, and the new 400 tests land without the lazy feature helper the line does not have. The #45214 picks keep this line's_get_httpx_clientand_hidden_paramsnames, with one small test commit so the new OpenAI provider test reads_hidden_paramstoo. One line-native fix, 36894a1, gives the IRextrafields adefault_factory, because Python 3.11 rejects the sharedMappingProxyTypedefault andimport litellmfailed there. 7df9c06 then picks #45214's 39012f1, which writes that default the same way #45214 does and bills cached and cache-write tokens once under custom pricing, since they were added on top ofinput_tokensthat already include them. Version is bumped to 1.104.2 in its own commit, one patch above v1.104.1. c65d369 and 5d2cc10 pick e465919 and a2951b3, so theopenaiprovider also readslitellm.api_key,litellm.openai_key,litellm.api_baseandOPENAI_API_BASElike other OpenAI calls. f0cb1d5 picks e4081f7, so System One choice or score questions withoutinstructionswork on OpenAI deployments and single-option questions get a clear 400 before any upstream call. 7446e02 picks the guardrail fix from #45184's 855f0bb, as merged into #45214 at c19ada6, so OpenAI-format responses keepguardrail_informationwhen the caller setsinclude_guardrail_responseProblem this solves:
/v1/decisionsgpt-6-luna, can't be calledHow it solves it:
/v1/systemone/v1/decisionsopenaias a Decisions provider, image input includedguardrail_informationon OpenAI-format responses when the caller setsinclude_guardrail_responsePicks:
User Flow
Before: a developer on stable/1.104.x who wants a yes/no, choice, or score decision from a decider model gets 404 on both routes
state,questionskeyed by name){"detail":"Not Found"}comes backinput,questionslist){"detail":"Not Found"}comes back, with or without aninput_imagepartAfter: both routes answer with real decisions from the upstream model, each in its own shape
answerskeyed by question name andusageinput and output tokensanswerslist andusage.total_tokensinput_imagepart, OpenAIgpt-6-lunaanswers 200, while text-only Jev returns a clear 400Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@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
Proxy started from a checkout of each commit with
python -m litellm.proxy.proxy_cli --port <port> --config decisions_config.yaml, port 4013 for Before and 4012 for After so both ran side by side, calling Perplexity, OpenAI and TypeSafe for realsystemone_body.json:{ "model": "decider", "state": "Customer wrote: I was charged twice for my order #4411 and want one of the charges refunded today.", "questions": { "is_refund_request": {"type": "noul", "instructions": "Is the customer asking for a refund?"}, "urgency": {"type": "choice", "instructions": "How urgent is this?", "criteria": {"low": "can wait a week", "high": "needs action today"}}, "frustration": {"type": "score", "instructions": "How frustrated is the customer?", "criteria": ["calm", "mildly annoyed", "angry"]} } }openai_body.json:{ "model": "decider", "input": [{"role": "user", "type": "message", "content": [{"type": "input_text", "text": "Customer wrote: I was charged twice for my order #4411 and want one of the charges refunded today."}]}], "questions": [ {"type": "predicate", "name": "is_refund_request", "instructions": "Is the customer asking for a refund?"}, {"type": "choice", "name": "needs_human", "instructions": "Should a human agent take over?", "choices": [{"value": true, "description": "billing dispute that needs a person"}, {"value": false}]}, {"type": "score", "name": "frustration", "instructions": "How frustrated is the customer?", "levels": [{"label": "calm"}, {"label": "mildly annoyed"}, {"label": "angry", "description": "uses hostile language"}]} ] }luna_openai_body.json,jev_systemone_body.jsonandjev_openai_body.jsonare the bodies above withmodelset tolunaorjevluna_image_body.json, withjev_image_body.jsonthe same except"model": "jev":{"model": "luna", "input": [{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "Here is the swatch the customer uploaded."}, {"type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAeUlEQVR4nO3PQQkAMAzAwEqof1WTNBF7HINABFzm7H7dcEEDWtCAFjSgBQ1oQQNa0IAWNKAFDWhBA1rQgBY0oAUNaEEDWtCAFjSgBQ1oQQNa0IAWNKAFDWhBA1rQgBY0oAUNaEEDWtCAFjSgBQ1oQQNa0IAWNKAFj11mTwDigCLoNgAAAABJRU5ErkJggg==", "detail": "low"}]}], "questions": [{"type": "predicate", "name": "is_red", "instructions": "Is the swatch mostly red?"}, {"type": "choice", "name": "color", "instructions": "Which color dominates the swatch?", "choices": [{"value": "red"}, {"value": "green"}, {"value": "blue"}]}], "safety_identifier": "proof-user-1"}Before (48963be)
System One format at /v1/systemone, Perplexity decider
curl -sS -i localhost:4013/v1/systemone -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @systemone_body.jsonOpenAI format at /v1/decisions, Perplexity decider
curl -sS -i localhost:4013/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @openai_body.jsonOpenAI format at /v1/decisions, OpenAI gpt-6-luna
curl -sS -i localhost:4013/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @luna_openai_body.jsonOpenAI format with an image at /v1/decisions, OpenAI gpt-6-luna
curl -sS -i localhost:4013/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @luna_image_body.jsonSystem One format at /v1/systemone, TypeSafe Jev
curl -sS -i localhost:4013/v1/systemone -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @jev_systemone_body.jsonOpenAI format at /v1/decisions, TypeSafe Jev
curl -sS -i localhost:4013/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @jev_openai_body.jsonOpenAI format with an image at /v1/decisions, TypeSafe Jev
curl -sS -i localhost:4013/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @jev_image_body.jsonAfter (7df9c06)
System One format at /v1/systemone, Perplexity decider
curl -sS -i localhost:4012/v1/systemone -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @systemone_body.jsonOpenAI format at /v1/decisions, Perplexity decider
curl -sS -i localhost:4012/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @openai_body.jsonOpenAI format at /v1/decisions, OpenAI gpt-6-luna
curl -sS -i localhost:4012/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @luna_openai_body.jsonOpenAI format with an image at /v1/decisions, OpenAI gpt-6-luna
curl -sS -i localhost:4012/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @luna_image_body.jsonSystem One format at /v1/systemone, TypeSafe Jev
curl -sS -i localhost:4012/v1/systemone -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @jev_systemone_body.jsonOpenAI format at /v1/decisions, TypeSafe Jev
curl -sS -i localhost:4012/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @jev_openai_body.jsonOpenAI format with an image at /v1/decisions, TypeSafe Jev
curl -sS -i localhost:4012/v1/decisions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' -d @jev_image_body.jsonCustom pricing (7df9c06)
A second proxy with per-token prices on the deployment, called twice with a ~1.8k-token input to see whether OpenAI reports cached tokens
Billing matches the configured rates. OpenAI returned 0 cached tokens on both calls
OpenAI key from
litellm_settings(c65d369)The proxy runs with no
OPENAI_API_KEYin its environment and the key set only throughlitellm_settings.openai_keyBefore the pick, the provider ignored
litellm.openai_keyand returned this (captured at #45214's 39012f1, which has the same provider code as 7df9c06)After, at c65d369
System One questions without
instructions(f0cb1d5)Same luna config as above, with the key from
litellm_settings.openai_key. Before, at 5d2cc10, OpenAI rejected criteria-only choice and score questions, and the single-option question cost an upstream round tripAfter, at f0cb1d5, which picks #45214's e4081f7, the criteria-only questions get real answers from OpenAI and the single-option question gets a litellm 400 that says why
guardrail_information on OpenAI-format responses
Same
both_config.yaml, withinclude_guardrail_response: trueadded toluna_openai_body.jsonandjev_openai_body.jsonasluna_guardrail_body.jsonandjev_guardrail_body.jsonBefore, at f0cb1d5, both responses drop
guardrail_informationeven though the caller asked for itAfter, at 7446e02, both responses carry
guardrail_information(empty here since no guardrails are configured)Type
New Feature
Caveats (if any)
Medium
Low
test_custom_token_pricing_bills_cached_decisions_input_tokens_once, which fails without itinput_imageparts still return 400 on text-only System One providerssafety_identifierreaches OpenAI but System One providers have no such field_prepare_requestraising directly and decisions TPM accounting both mirror feat(decisions): add unified /v1/decisions endpoint for Jev-compatible providers #44236 as merged on mainFinal Attestation
Note
Medium Risk
Introduces new authenticated data-plane endpoints and multi-provider request translation with billing and route-precedence changes; mistakes could mis-route traffic or mis-price tokens, but scope is additive with heavy test coverage.
Overview
Adds a Decisions / System One capability to stable 1.104.2: structured yes/no, choice, and score questions over state or OpenAI-style
input, routed through a shared internal representation to upstream decider APIs.SDK and routing: New
litellm.decisions(decisions/adecisions) with provider configs for Perplexity, TypeSafe, OpenRouter, Cloudflare Workers AI, Strands Decider, and OpenAI. The LiteLLM Router exposes the same call types; gateway and proxy allowlists include/v1/decisions,/decisions,/v1/systemone, and/systemone.Proxy: Lazy-loaded
decisions_endpointsserve System One bodies on/systemoneand OpenAI Decisions bodies on/decisions, wired throughProxyBaseLLMRequestProcessingasadecisions. Pass-through route registration now treats lazy stub routes as non-blocking so a config pass-through at/v1/decisionscan win precedence when the feature has not loaded.Cross-cutting: Pydantic types in
litellm.types.decisions, IR transforms inllms/base_llm/decisionsand per-provider modules, usage→litellm.Usagefor billing/logging,evaluationhealth checks viaadecisions, model pricing entries for decider models, and broad unit/proxy tests (CI shards updated). README/provider metadata list new providers; version bumped to 1.104.2.Reviewed by Cursor Bugbot for commit 7446e02. Bugbot is set up for automated code reviews on this repo. Configure here.