Skip to content

feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 - #42595

Merged
mateo-berri merged 10 commits into
stable/1.102.xfrom
litellm_cherrypick_1_102_x
Sep 23, 2026
Merged

mateo-berri merged 10 commits into
stable/1.102.xfrom
litellm_cherrypick_1_102_x

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • TypeSafe Jev (classifier, passthroughs, guardrail) only exists from the 1.103 rc on
  • A customer runs the 1.103 rc in prod to get it and wants a stable line instead

How it solves it:

Included PRs, one commit per PR, cherry-picked with -m 1 from the main merge commit (plain pick for the #42301 squash), original author kept:

Two more commits on the branch:

Conflicts were resolved to what main has after each pick: #41615 in litellm/types/utils.py, complexity_router.py and config.py (this line lacks the capability and llm_v2 features, so the Jev hunks were applied on top of the line's code), auto_router_permissions.py and its test taken from main since they do not exist here. #41879 in auto_router_endpoints.py and its test around the member preview plumbing. #41886 in three python test files (formatting-only hunks kept as on the line, Jev additions applied) and about twelve dashboard files; StoredComplexityRouterConfig still lives in edit_auto_router_modal.tsx on this line, so the jev and per-turn fields went there. #42301 in gateway/routes/allowlist.py (added /openrouter/), success_handler.py (the typesafe branch also matches the openrouter decisions route) and _lazy_openapi_snapshot.json (only the added openrouter path block). schema.d.ts and the openapi snapshot only received the hunks each pick adds, nothing regenerated

Tests: every tests/test_litellm file the picks touch was run in the worktree, 1994 passed. The 14 requires_semantic_router tests in test_complexity_router.py fail on the line's tip before these picks too (semantic_router is not installed) and were left alone. The UI workflows do not run for stable/** bases, so the dashboard checks were run locally at 4bfac88281 under ui/litellm-dashboard: vitest on the touched test files (605 + 84 passed), eslint on every changed file (0 errors, local/no-large-inline-object-arg budget unchanged at 551), tsc --noEmit, npm run test:types, npm run build (the Dockerfile ui-builder stage), and make check at the repo root (PASS)

User Flow

Before: an admin on stable 1.102.x cannot route through TypeSafe Jev at all, so the only way to get it is running the 1.103 rc in prod

  1. The admin starts the proxy from stable/1.102.x with TYPESAFE_API_KEY set and an auto router auto-jev whose complexity_router_config has "classifier_type": "jev"
  2. A developer sends POST https://litellm-domain/v1/chat/completions with "model": "auto-jev" and gets 400 Invalid model name passed in model=auto-jev (the router dropped that deployment at boot)
  3. The admin tries POST https://litellm-domain/auto_router/test_routing with "classifier_type": "jev" and gets 422 Input should be 'heuristic', 'heuristic_v2', 'llm', 'custom', 'heuristic_first' or 'hybrid'
  4. They try POST https://litellm-domain/model/new with the same jev config and get 400 with the same message
  5. The developer sends POST https://litellm-domain/typesafe/v1/systemone with a jev-latest payload and gets 404 {"detail":"Not Found"}; POST https://litellm-domain/openrouter/alpha/decisions is 404 too
  6. On https://litellm-domain/ui/models-and-endpoints, Auto-Routers tab, Add Auto Router, the classification method picker shows Heuristic, Heuristic v2, LLM Classifier, Heuristic first and Hybrid, nothing for JEV
  7. The admin upgrades prod to v1.103.0-rc.1 to get any of this

After: the same admin gets the JEV classifier, the TypeSafe passthroughs and the compaction guardrail on stable 1.102.x (v1.102.1)

  1. The admin starts the proxy from this branch with the same config
  2. The developer sends POST https://litellm-domain/v1/chat/completions with "model": "auto-jev" and gets 200; the x-litellm-complexity-router-tier header says SIMPLE for the short prompt and COMPLEX for the long one, with x-litellm-complexity-router-cause: jev_classifier; POST https://litellm-domain/v1/messages and POST https://litellm-domain/v1/responses with "model": "auto-jev" route the same way
  3. The admin sends POST https://litellm-domain/auto_router/test_routing with "classifier_type": "jev" and gets 200 with routed_model, tier and the jev-classifier:SIMPLE / jev-confidence=... signals; with a key over budget the call is 400 budget_exceeded instead of a free dry run
  4. POST https://litellm-domain/model/new with the jev config is 200 and chats with "model": "db-auto-jev" route the same way
  5. POST https://litellm-domain/typesafe/v1/systemone is forwarded and comes back 200 with TypeSafe's answers, PUT is forwarded too (405 from TypeSafe), a bad key gets 401 LiteLLM Virtual Key expected; POST https://litellm-domain/openrouter/alpha/decisions with "model": "typesafe/jev-1.13" is 200 with "model":"typesafe/jev-1.13-20260917"
  6. The same UI picker now offers JEV Classifier with JEV Model and JEV Timeout (ms) fields
  7. A chat sent with "guardrails": ["typesafe-compaction"] over a long tool conversation is billed at 962 prompt tokens instead of 1119 for the same chat without it
  8. The admin stays on stable 1.102.x instead of the rc

Relevant issues

Backport of #41607, #41615, #41723, #41757, #41879, #41886 and #42301 to stable/1.102.x

Affected release

Linear ticket

Resolves LIT-8369

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<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
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to 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

Two proxies from the same config, each with its own empty Postgres, real provider calls (Anthropic, TypeSafe, OpenRouter) with real spend. Before is the stable/1.102.x merge base 95293834e8 on port 16390, After is the PR tip 4bfac88281 on port 16391 (its process runs the Python tree of d0687347a7, which is byte-identical to the tip outside ui/litellm-dashboard; the dashboard screenshots come from the bundle built at 4bfac88281). Both started with

python litellm/proxy/proxy_cli.py --config config.yaml --port <port> --num_workers 2 --detailed_debug

config.yaml (LITELLM_MASTER_KEY, DATABASE_URL, ANTHROPIC_API_KEY, TYPESAFE_API_KEY in the environment):

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
  database_url: os.environ/DATABASE_URL
litellm_settings:
  drop_params: true
model_list:
  - model_name: claude-haiku-4-5
    litellm_params:
      model: anthropic/claude-haiku-4-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: claude-sonnet-5
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: auto-llm
    litellm_params:
      model: auto_router/complexity_router
      complexity_router_default_model: claude-sonnet-5
      complexity_router_config:
        tiers: {SIMPLE: claude-haiku-4-5, MEDIUM: claude-sonnet-5, COMPLEX: claude-sonnet-5, REASONING: claude-sonnet-5}
        classifier_type: llm
        classifier_llm_config: {model: claude-haiku-4-5, timeout_ms: 8000}
  - model_name: auto-heuristic
    litellm_params:
      model: auto_router/complexity_router
      complexity_router_default_model: claude-sonnet-5
      complexity_router_config:
        tiers: {SIMPLE: claude-haiku-4-5, MEDIUM: claude-sonnet-5, COMPLEX: claude-sonnet-5, REASONING: claude-sonnet-5}
        classifier_type: heuristic
  - model_name: auto-jev
    litellm_params:
      model: auto_router/complexity_router
      complexity_router_default_model: claude-sonnet-5
      complexity_router_config:
        tiers: {SIMPLE: claude-haiku-4-5, MEDIUM: claude-sonnet-5, COMPLEX: claude-sonnet-5, REASONING: claude-sonnet-5}
        classifier_type: jev
        jev_classifier_config: {model: jev-latest, timeout_ms: 8000}
guardrails:
  - guardrail_name: typesafe-compaction
    litellm_params:
      guardrail: typesafe
      mode: pre_call
      default_on: false
      api_key: os.environ/TYPESAFE_API_KEY

Keys used below, generated the same way on both sides: $MASTER_KEY is the config's master key; $KEY comes from POST /key/generate {"key_alias":"ab-plain","max_budget":10} (an internal-user key); $ADMIN_KEY and $ADMIN_KEY_EXHAUSTED are keys of a proxy_admin user created with POST /user/new {"user_id":"ab-admin","user_role":"proxy_admin","auto_create_key":false}, generated with max_budget 10 and max_budget 0 ("metadata":{"throttle_on_budget_exceeded":true}) respectively. $BASE is http://localhost:16390 for Before and http://localhost:16391 for After. The compaction case sends this 9-message tool conversation:

The /v1/messages and /v1/responses cases ran last, against the same two trees booted from the same config minus database_url (ports 17490 and 17491, master key only), after the databases above were torn down

tool conversation payload
{
  "model": "claude-haiku-4-5",
  "max_tokens": 60,
  "guardrails": [
    "typesafe-compaction"
  ],
  "messages": [
    {
      "role": "system",
      "content": "You are a deployment assistant."
    },
    {
      "role": "user",
      "content": "What is the weather in Paris?"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "c1",
          "type": "function",
          "function": {
            "name": "get_weather",
            "arguments": "{\"city\":\"Paris\"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "c1",
      "content": "Paris: 18C, light rain, humidity 72 percent, wind 12 km/h from the west, UV index 2, sunrise 07:14, sunset 19:52, pollen low, air quality index 31, visibility 9 km, pressure 1013 hPa, dew point 13C, cloud cover 80 percent, chance of rain 60 percent through the evening."
    },
    {
      "role": "user",
      "content": "And the ACME stock price?"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "c2",
          "type": "function",
          "function": {
            "name": "get_quote",
            "arguments": "{\"ticker\":\"ACME\"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "c2",
      "content": "ACME: 142.17 USD, change +1.3 percent, open 140.02, high 143.10, low 139.80, volume 2.1M, market cap 48B, PE 31.2, dividend yield 0.8 percent, 52 week high 155.00, 52 week low 98.40, next earnings 2026-10-21, analyst consensus hold, average target 150.00."
    },
    {
      "role": "user",
      "content": "Show me the deployment runbook for the payments service."
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "c3",
          "type": "function",
          "function": {
            "name": "get_runbook",
            "arguments": "{\"service\":\"payments\"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "c3",
      "content": "Payments runbook: 1) freeze deploys in #payments-ops, 2) run db migration payments-2026-09 with --dry-run first, 3) scale canary to 5 percent, 4) watch p99 latency and error rate for 15 minutes, 5) if error rate exceeds 0.5 percent roll back with deploy rollback payments, 6) otherwise promote to 100 percent and unfreeze, 7) post the summary in #payments-ops with the deploy id."
    },
    {
      "role": "user",
      "content": "What is step 5 of the runbook?"
    }
  ]
}

Before (9529383, port 16390)

Chat through the JEV auto router

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/chat/completions \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"auto-jev","messages":[{"role":"user","content":"What is the capital of France? Answer in one word."}],"max_tokens":40}'
  2. HTTP 400, {"error":{"message":"/chat/completions: Invalid model name passed in model=auto-jev. Call /v1/models to view available models for your key.","type":"invalid_request_error","param":null,"code":"400","provider_specific_f...
  3. Same call with the long design prompt ("Design a distributed rate limiter for a multi-region API gateway...")
  4. HTTP 400, {"error":{"message":"/chat/completions: Invalid model name passed in model=auto-jev. Call /v1/models to view available models for your key.","type":"invalid_request_error","param":null,"code":"400","provider_specific_f...

Messages and Responses through the JEV auto router

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/messages \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"model":"auto-jev","max_tokens":40,"messages":[{"role":"user","content":"What is the capital of France? Answer in one word."}]}'
  2. HTTP 400, {"type":"error","error":{"type":"invalid_request_error","message":"anthropic_messages: Invalid model name passed in model=auto-jev. Call /v1/models to view available models for your key.","provider_...
  3. Same call with the long design prompt and max_tokens 200
  4. HTTP 400, {"type":"error","error":{"type":"invalid_request_error","message":"anthropic_messages: Invalid model name passed in model=auto-jev. Call /v1/models to view available models for your key.","provider_...
  5. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/responses \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"model":"auto-jev","max_output_tokens":40,"input":"What is the capital of France? Answer in one word."}'
  6. HTTP 400, {"error":{"message":"/responses: Invalid model name passed in model=auto-jev. Call /v1/models to view available models for your key.","type":"invalid_request_error","param":null,"code":"400","provid...
  7. Same call with the long design prompt and max_output_tokens 200
  8. HTTP 400, {"error":{"message":"/responses: Invalid model name passed in model=auto-jev. Call /v1/models to view available models for your key.","type":"invalid_request_error","param":null,"code":"400","provid...

Dry-run routing with the JEV classifier

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/auto_router/test_routing \
         -H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
         -d '{"prompt":"What is the capital of France? Answer in one word.","complexity_router_config":{"tiers":{"SIMPLE":"claude-haiku-4-5","MEDIUM":"claude-sonnet-5","COMPLEX":"claude-sonnet-5","REASONING":"claude-sonnet-5"},"classifier_type":"jev","jev_classifier_config":{"model":"jev-latest"}}}'
  2. HTTP 422, {"detail":[{"type":"literal_error","loc":["body","complexity_router_config","classifier_type"],"msg":"Input should be 'heuristic', 'heuristic_v2', 'llm', 'custom', 'heuristic_first' or 'hybrid'","input":"jev","ctx":{"expected":"'heuristic', 'heuristic_v2', 'll...
  3. Same call with $ADMIN_KEY_EXHAUSTED (max_budget 0)
  4. HTTP 422, {"detail":[{"type":"literal_error","loc":["body","complexity_router_config","classifier_type"],"msg":"Input should be 'heuristic', 'heuristic_v2', 'llm', 'custom', 'heuristic_first' or 'hybrid'","input":"jev","ctx":{"expected":"'heuristic', 'heuristic_v2', 'll...
  5. Same call with the master key
  6. HTTP 422
  7. Same call with $KEY (a plain internal-user key)
  8. HTTP 422, {"detail":[{"type":"literal_error","loc":["body","complexity_router_config","classifier_type"],"msg":"Input should be 'heuristic', 'heuristic_v2', 'llm', 'custom', 'heuristic_first' or 'hybrid'","input":"jev","ctx":{"expected":"'heuristic',...

TypeSafe passthrough

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/typesafe/v1/systemone \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"jev-latest","state":"The sky is blue on a clear day.","questions":{"blue":{"type":"noul","instructions":"Is the sky described as blue?"}}}'
  2. HTTP 404, {"detail":"Not Found"}

  3. PUT on the same route with {"state":"x"}

  4. HTTP 404, {"detail":"Not Found"}

  5. Same POST with Authorization: Bearer not-a-key

  6. HTTP 404, {"detail":"Not Found"}

  7. Same POST with odd payloads:

    payload HTTP body
    "state": 7 404 {"detail":"Not Found"}
    "state": ["a","b"] 404 {"detail":"Not Found"}
    "state": "" 404 {"detail":"Not Found"}
    a 5 KB state 404 {"detail":"Not Found"}
    the step 1 payload again 404 {"detail":"Not Found"}

OpenRouter decisions passthrough

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/openrouter/alpha/decisions \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"typesafe/jev-1.13","state":"The sky is blue on a clear day.","questions":{"blue":{"type":"noul","instructions":"Is the sky described as blue?"}}}'
  2. HTTP 404, {"detail":"Not Found"}

TypeSafe compaction guardrail

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/chat/completions \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"claude-haiku-4-5","max_tokens":60,"guardrails":["typesafe-compaction"],"messages":<the 9-message tool conversation from the setup>}'
  2. HTTP 200, usage.prompt_tokens 1119
  3. Same conversation without guardrails
  4. HTTP 200, usage.prompt_tokens 1119

JEV auto router created through the API

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/model/new \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"model_name":"db-auto-jev","litellm_params":{"model":"auto_router/complexity_router","complexity_router_default_model":"claude-sonnet-5","complexity_router_config":{"tiers":{"SIMPLE":"claude-haiku-4-5","MEDIUM":"claude-sonnet-5","COMPLEX":"claude-sonnet-5","REASONING":"claude-sonnet-5"},"classifier_type":"jev","jev_classifier_config":{"model":"jev-latest"}}}}'
  2. HTTP 400, {"error":{"message":"complexity_router_config is invalid at classifier_type: Input should be 'heuristic', 'heuristic_v2', 'llm', 'custom', 'heuristic_first' or 'hybrid'. The router would drop this deployment at load time, so the write is rejected instead.","type":"validation_error","param":"litellm_...
  3. POST $BASE/v1/chat/completions with "model": "db-auto-jev" and the capital-of-France prompt
  4. HTTP 400, {"error":{"message":"/chat/completions: Invalid model name passed in model=db-auto-jev. Call /v1/models to view available models for your key.","type":"invalid_request_error","param":null,"code":"400","provider_specifi...

TypeSafe guardrail created through the API

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/guardrails \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"guardrail":{"guardrail_name":"typesafe-db","litellm_params":{"guardrail":"typesafe","mode":"pre_call","default_on":false,"api_key":"os.environ/TYPESAFE_API_KEY"}}}'
  2. HTTP 500, {"detail":"400: Guardrail configuration error: Unsupported guardrail: typesafe"}

Admin UI classifier picker

  1. Open $BASE/ui/models-and-endpoints, click the Auto-Routers tab, click Add Auto Router, expand Detailed Configuration, then Advanced: Classification Method

  2. The picker offers Heuristic, Heuristic v2, LLM Classifier, Heuristic first and Hybrid; there is no JEV option

    pr42595-4bfac88281-lit8369_ui_before_02_classifier_picker.png

Existing surfaces (regression sweep)

  1. Each row below is one curl of the form used above (same $BASE, the key named in the row, the same simple and hard prompts), run in the listed order on this side

  2. Observed, one row per call:

    call HTTP detail
    POST /v1/chat/completions auto-llm, simple prompt 200 model auto-llm, tier SIMPLE
    POST /v1/chat/completions auto-llm, hard prompt 200 model auto-llm, tier COMPLEX
    POST /v1/chat/completions auto-heuristic 200 model auto-heuristic, tier SIMPLE
    POST /v1/chat/completions claude-haiku-4-5 200 model claude-haiku-4-5
    POST /v1/messages claude-haiku-4-5 200 model claude-haiku-4-5
    POST /v1/responses auto-llm 200 model auto-llm, tier SIMPLE
    POST /v1/chat/completions with the max_budget 0 key 429 {"error":{"message":"Budget has been exceeded! Key=ab-exhausted (sk-...) Current cost:...
    POST /auto_router/test_routing llm, admin key 200
    POST /auto_router/test_routing heuristic, admin key 200
    POST /auto_router/test_routing llm, exhausted admin key 400 {"error":{"message":"Budget has been exceeded! Key=ab-admin-exhausted (sk-...) Current...
    POST /auto_router/test_routing heuristic, exhausted admin key 200
    POST /auto_router/test_routing llm, master key 200
    POST /auto_router/test_routing llm, internal-user key 403 {"detail":{"error":"User does not have permission to dry-run an auto router. Your role=int...
    POST /auto_router/test_routing heuristic, internal-user key 403 {"detail":{"error":"User does not have permission to dry-run an auto router. Your role=int...
    POST /model/new db-auto-llm (llm classifier) 200
    POST /v1/chat/completions db-auto-llm 200 model db-auto-llm, tier SIMPLE
    GET /model/info 200
    GET /v1/models 200
    GET /model_group/info 200
    GET /guardrails/list 200
    GET /guardrails/ui/provider_specific_params 200
    POST /user/new (proxy_admin user) 200
    GET /health 200
    GET /health/readiness 200
    GET /health after all of the above 200

After (4bfac88, port 16391)

The tip is d1cd2dccaf; it adds only the regenerated OpenAPI snapshot and the dashboard .d.ts types on top of 4bfac88281, neither of which is executed on any route or screen below, so this run stands for the tip

Chat through the JEV auto router

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/chat/completions \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"auto-jev","messages":[{"role":"user","content":"What is the capital of France? Answer in one word."}],"max_tokens":40}'
  2. HTTP 200, model auto-jev, header x-litellm-complexity-router-tier: SIMPLE, x-litellm-complexity-router-cause: jev_classifier, prompt_tokens 19 completion_tokens 4
  3. Same call with the long design prompt ("Design a distributed rate limiter for a multi-region API gateway...")
  4. HTTP 200, model auto-jev, header x-litellm-complexity-router-tier: COMPLEX, x-litellm-complexity-router-cause: jev_classifier, prompt_tokens 67 completion_tokens 6903

Messages and Responses through the JEV auto router

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/messages \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"model":"auto-jev","max_tokens":40,"messages":[{"role":"user","content":"What is the capital of France? Answer in one word."}]}'
  2. HTTP 200, model auto-jev, header x-litellm-complexity-router-tier: SIMPLE, x-litellm-complexity-router-cause: jev_classifier, input_tokens 19 output_tokens 4
  3. Same call with the long design prompt and max_tokens 200
  4. HTTP 200, model auto-jev, header x-litellm-complexity-router-tier: COMPLEX, x-litellm-complexity-router-cause: jev_classifier, input_tokens 67 output_tokens 4906
  5. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/responses \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"model":"auto-jev","max_output_tokens":40,"input":"What is the capital of France? Answer in one word."}'
  6. HTTP 200, model auto-jev, header x-litellm-complexity-router-tier: SIMPLE, x-litellm-complexity-router-cause: jev_classifier, input_tokens 19 output_tokens 4
  7. Same call with the long design prompt and max_output_tokens 200
  8. HTTP 200, model auto-jev, header x-litellm-complexity-router-tier: COMPLEX, x-litellm-complexity-router-cause: jev_classifier, input_tokens 67 output_tokens 6586

Dry-run routing with the JEV classifier

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/auto_router/test_routing \
         -H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
         -d '{"prompt":"What is the capital of France? Answer in one word.","complexity_router_config":{"tiers":{"SIMPLE":"claude-haiku-4-5","MEDIUM":"claude-sonnet-5","COMPLEX":"claude-sonnet-5","REASONING":"claude-sonnet-5"},"classifier_type":"jev","jev_classifier_config":{"model":"jev-latest"}}}'
  2. HTTP 200, {"routed_model":"claude-haiku-4-5","tier":"SIMPLE","cause":"jev_classifier","classifier_model":"typesafe/jev-1.13.0","signals":["jev-classifier:SIMPLE","jev-confidence=1.000000","tier-probability:REASONING=0.000000","tier-probability:MEDIUM=0.000000","tier-probability:SIMPLE=1.000000","tier-probability:COMPLEX=0.000000"]}
  3. Same call with $ADMIN_KEY_EXHAUSTED (max_budget 0)
  4. HTTP 400, {"error":{"message":"Budget has been exceeded! Key=ab-admin-exhausted (sk-...) Current cost: 0.0, Max budget: 0.0","type":"budget_exceeded","param":null,"code":"400"}}
  5. Same call with the master key
  6. HTTP 200
  7. Same call with $KEY (a plain internal-user key)
  8. HTTP 403, {"detail":{"error":"User does not have permission to dry-run an auto router. Your role=internal_user. Call as a PROXY_ADMIN, or as a team admin by specifying a team_id."}}

TypeSafe passthrough

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/typesafe/v1/systemone \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"jev-latest","state":"The sky is blue on a clear day.","questions":{"blue":{"type":"noul","instructions":"Is the sky described as blue?"}}}'
  2. HTTP 200, {"model":"jev-1.13.0","answers":{"blue":{"type":"noul","noul":0.99}},"usage":{"input_tokens":282,"output_tokens":20}}

  3. PUT on the same route with {"state":"x"}

  4. HTTP 405, {"detail":"Method Not Allowed"}

  5. Same POST with Authorization: Bearer not-a-key

  6. HTTP 401, {"error":{"message":"LiteLLM Virtual Key expected. Received=not-****-key, expected to start with 'sk-'.","type":"auth_error","param":"None","code":"401"}}

  7. Same POST with odd payloads:

    payload HTTP body
    "state": 7 422 {"detail":[{"type":"string_type","loc":["body","state","str"],"msg":"Input should be a valid string","input":7...
    "state": ["a","b"] 200 {"model":"jev-1.13.0","answers":{"q":{"type":"noul","noul":0.97}},"usage":{"input_tokens":278,"output_tokens":...
    "state": "" 200 {"model":"jev-1.13.0","answers":{"q":{"type":"noul","noul":0.8}},"usage":{"input_tokens":270,"output_tokens":2...
    a 5 KB state 200 {"model":"jev-1.13.0","answers":{"q":{"type":"noul","noul":0.96}},"usage":{"input_tokens":1832,"output_tokens"...
    the step 1 payload again 200 {"model":"jev-1.13.0","answers":{"blue":{"type":"noul","noul":0.99}},"usage":{"input_tokens":282,"output_token...

OpenRouter decisions passthrough

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/openrouter/alpha/decisions \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"typesafe/jev-1.13","state":"The sky is blue on a clear day.","questions":{"blue":{"type":"noul","instructions":"Is the sky described as blue?"}}}'
  2. HTTP 200, {"model":"typesafe/jev-1.13-20260917","answers":{"blue":{"type":"noul","noul":0.99}},"usage":{"input_tokens":282,"output_tokens":20,"cost":1.1844e-05},"id":"gen-dec-1790127449-uSzKbgQH085seSsxNoWK","provider":"TypeSafe"}

TypeSafe compaction guardrail

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/v1/chat/completions \
         -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
         -d '{"model":"claude-haiku-4-5","max_tokens":60,"guardrails":["typesafe-compaction"],"messages":<the 9-message tool conversation from the setup>}'
  2. HTTP 200, usage.prompt_tokens 962
  3. Same conversation without guardrails
  4. HTTP 200, usage.prompt_tokens 1119

JEV auto router created through the API

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/model/new \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"model_name":"db-auto-jev","litellm_params":{"model":"auto_router/complexity_router","complexity_router_default_model":"claude-sonnet-5","complexity_router_config":{"tiers":{"SIMPLE":"claude-haiku-4-5","MEDIUM":"claude-sonnet-5","COMPLEX":"claude-sonnet-5","REASONING":"claude-sonnet-5"},"classifier_type":"jev","jev_classifier_config":{"model":"jev-latest"}}}}'
  2. HTTP 200, model_id c746f2c7-6a20-4d9b-8536-5d3baab25654
  3. POST $BASE/v1/chat/completions with "model": "db-auto-jev" and the capital-of-France prompt
  4. HTTP 200, model db-auto-jev, header x-litellm-complexity-router-tier: SIMPLE, x-litellm-complexity-router-cause: jev_classifier, prompt_tokens 19 completion_tokens 4

TypeSafe guardrail created through the API

  1. curl -s -w '\nHTTP %{http_code}\n' -X POST $BASE/guardrails \
         -H "Authorization: Bearer $MASTER_KEY" -H "Content-Type: application/json" \
         -d '{"guardrail":{"guardrail_name":"typesafe-db","litellm_params":{"guardrail":"typesafe","mode":"pre_call","default_on":false,"api_key":"os.environ/TYPESAFE_API_KEY"}}}'
  2. HTTP 200, guardrail_name typesafe-db (body holds the guardrail id and params, not shown)

Admin UI classifier picker

  1. Open $BASE/ui/models-and-endpoints, click the Auto-Routers tab, click Add Auto Router, expand Detailed Configuration, then Advanced: Classification Method

  2. The picker adds JEV Classifier ("uses TypeSafe System One Choice to decide the tier")

    pr42595-4bfac88281-lit8369_ui_after_02_classifier_picker.png

  3. Selecting it shows the JEV Model (jev-latest) and JEV Timeout (ms) (3000) fields plus the classifier circuit breaker toggle

    pr42595-4bfac88281-lit8369_ui_after_03_jev_fields.png

Existing surfaces (regression sweep)

  1. Each row below is one curl of the form used above (same $BASE, the key named in the row, the same simple and hard prompts), run in the listed order on this side

  2. Observed, one row per call:

    call HTTP detail
    POST /v1/chat/completions auto-llm, simple prompt 200 model auto-llm, tier SIMPLE
    POST /v1/chat/completions auto-llm, hard prompt 200 model auto-llm, tier COMPLEX
    POST /v1/chat/completions auto-heuristic 200 model auto-heuristic, tier SIMPLE
    POST /v1/chat/completions claude-haiku-4-5 200 model claude-haiku-4-5
    POST /v1/messages claude-haiku-4-5 200 model claude-haiku-4-5
    POST /v1/responses auto-llm 200 model auto-llm, tier SIMPLE
    POST /v1/chat/completions with the max_budget 0 key 429 {"error":{"message":"Budget has been exceeded! Key=ab-exhausted (sk-...) Current cost:...
    POST /auto_router/test_routing llm, admin key 200
    POST /auto_router/test_routing heuristic, admin key 200
    POST /auto_router/test_routing llm, exhausted admin key 400 {"error":{"message":"Budget has been exceeded! Key=ab-admin-exhausted (sk-...) Current...
    POST /auto_router/test_routing heuristic, exhausted admin key 200
    POST /auto_router/test_routing llm, master key 200
    POST /auto_router/test_routing llm, internal-user key 403 {"detail":{"error":"User does not have permission to dry-run an auto router. Your role=int...
    POST /auto_router/test_routing heuristic, internal-user key 403 {"detail":{"error":"User does not have permission to dry-run an auto router. Your role=int...
    POST /model/new db-auto-llm (llm classifier) 200
    POST /v1/chat/completions db-auto-llm 200 model db-auto-llm, tier SIMPLE
    GET /model/info 200
    GET /v1/models 200
    GET /model_group/info 200
    GET /guardrails/list 200
    GET /guardrails/ui/provider_specific_params 200
    POST /user/new (proxy_admin user) 200
    GET /health 200
    GET /health/readiness 200
    GET /health after all of the above 200

Live PR risk

Verdict: no breaking or backward-incompatible change on an existing surface. Every call that existed before (25 in the sweep above, plus the delete-then-re-add of a DB auto router) returns the same status and the same routing tier on both sides

Breaking: none found. The base of every existing route, the config keys, the DB schema and the dashboard routes are unchanged; the picks only add the jev classifier literal, the typesafe guardrail, the /typesafe/* and /openrouter/* pass-through routes and the dashboard picker entry

Backward incompatible: POST /auto_router/test_routing with "classifier_type": "jev" moves from 422 (unknown literal) to the same permission and budget checks the other classifiers already get (403 for an internal-user key, 400 budget_exceeded for an exhausted key, 200 otherwise). Nothing that returned 200 before returns anything else now

Regression risk:

  • Delete-then-re-add of a DB auto router (POST /model/delete, then POST /model/new with the same name) works on both sides: delete 200, /model/info empty, re-add 200, chat 200. One head re-add during QA returned 500 not live in this pod's router after the reload; the cause was orphan DB rows created while the rig was overloaded (rows saved to the DB but refused by the router's duplicate-name guard, which holds the name until the orphan is deleted). litellm/router.py is byte-identical between the merge base and the tip and the guard exists on main, so it is not something this PR changes
  • Spend logging for the new routes was read back from LiteLLM_SpendLogs at the tip: typesafe/jev-1.13.0 and openrouter/typesafe/jev-1.13-20260917 rows carry token counts and non-zero spend, and the classifier call of every JEV-routed chat is its own row next to the routed model's row

Dependency graph:

  • tested: complexity router classifier dispatch (jev next to llm and heuristic), test_routing permission and budget path, /model/new validation of complexity_router_config, guardrail registry (typesafe), pass-through route registration and auth (/typesafe/*, /openrouter/alpha/decisions), pass-through spend logging, the dashboard add-auto-router modal
  • untested: streaming through a JEV router, the edit-auto-router modal, assigning the typesafe guardrail to a key or team, the other three lines (their own PRs)
  • verified live: Anthropic, TypeSafe and OpenRouter upstreams, all with real spend
  • unreachable here: a rolling upgrade from a running v1.102.0 with existing DB auto routers and keys; the schema is unchanged and the config types only gain a literal, so an old row loads the same way, but it was not simulated

Not verified: streaming through a JEV router, the edit modal, rolling upgrade

Type

🆕 New Feature

Caveats (if any)

Medium

  • The UI workflows do not run for stable/** bases, so the dashboard checks ran locally at the tip (vitest, eslint, tsc, test:types, npm run build, make check)
  • Streaming through a JEV router and the edit-auto-router modal were not driven; the code paths are the ones the picks carry from main unchanged

Low

  • Against the pre-pick base (95293834e8), scripts/type_discipline_gate.py counts LIT001 at 22188 vs the line's limit 22174 (+15, in the picked auth_checks.py and guardrail_hooks/typesafe/) and scripts/test_quality_gate.py counts TQ008 at 11151 vs 10993 (+73, in pick-modified test files). The code is main's verbatim, the workflows enforcing these budgets do not run for stable/** bases, and budget JSON files are never edited on a branch, so the counts are left as they are rather than refactoring main's code on a stable line
  • test_routing with an internal-user key is 403 before and after (needs PROXY_ADMIN or a team admin), so the JEV dry run has the same audience as the other classifiers
  • The 14 requires_semantic_router tests in test_complexity_router.py fail on this line with or without the picks (semantic_router is not installed here)
  • A complexity router's tier params override the caller's max_tokens (the hard prompts come back with thousands of completion tokens at max_tokens 40); this is how the router already behaves on the merge base with the llm classifier, not something the picks change
  • feat(ui): configure capability and Fuse v2 classifiers #41315 (forecast classifier and the classifier tabs) is not in the set; the two dashboard pieces of feat(auto-router): add JEV classifier alongside LLM classifier #41886 that imported it were adapted in 4bfac88281 instead of pulling it in

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Link to Devin session: https://app.devin.ai/sessions/b6d5b6c20aec42babbd9213ec27ef3bd
Open in Devin Desktop: https://app.devin.ai/desktop/session/b6d5b6c20aec42babbd9213ec27ef3bd?variant=devin
Requested by: @mateo-berri


Note

Medium Risk
Broad additive changes across routing, auth, guardrails, and external TypeSafe/OpenRouter calls; existing routes stay compatible but misconfiguration or upstream outages can block or alter requests on new JEV paths.

Overview
This backport brings TypeSafe Jev onto the stable proxy line: new /typesafe/* and /openrouter/* pass-through routes (with token/cost logging), a typesafe guardrail that compacts stale tool results via Jev /v1/systemone, and classifier_type: "jev" on complexity auto-routers (tier choice, circuit breaker, classifier probabilities in routing logs).

Auto-router dry-runs and member-managed routers gain stricter gates: /auto_router/manage team permission, dependency/model checks for non-admins, optional saved_model_id on test routing, and virtual-key budget enforcement for JEV test routing. Model writes merge stored Jev credentials when patching configs; router health sweeps skip evaluation dependencies so Jev classifiers do not mark deployments unhealthy.

Pricing/schema updates add evaluation mode and Jev model entries; allowlists and lazy route loading register the new pass-through prefixes.

Reviewed by Cursor Bugbot for commit d1cd2dc. Bugbot is set up for automated code reviews on this repo. Configure here.

Link to Devin session: https://app.devin.ai/sessions/dcedce8035d64a199713bae4b2b90b7f
Open in Devin Desktop: https://app.devin.ai/desktop/session/dcedce8035d64a199713bae4b2b90b7f?variant=devin

…acking

Backport of #41607 to stable/1.102.x.
Cherry-picked from deb9d8a (main).

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@greptile-apps

greptile-apps Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with no outstanding findings after the generated artifacts were synchronized to the stable branch

Findings

  1. P1 Security JEV Key Stored Unencrypted ▶

Summary

This PR backports TypeSafe JEV routing, passthrough, guardrail, pricing, permissions, and dashboard support to stable/1.102.x. The latest commit regenerates the OpenAPI snapshot and dashboard API types so they describe this stable branch rather than main-only functionality

  • Adds JEV as a complexity-router classifier with dry-run, budget, and permission handling
  • Adds authenticated TypeSafe and OpenRouter passthrough routes with spend logging
  • Adds TypeSafe conversation compaction and dashboard configuration support
  • Synchronizes generated OpenAPI and TypeScript definitions with the stable-line backend

Reviews (4) · Last reviewed commit: "chore(backport): regenerate the openapi ..."

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

tin-berri and others added 7 commits September 22, 2026 22:49
Prerequisite for #41615 and #41879 on stable/1.102.x.
Cherry-picked from 109ca70 (main).
Backport of #41615 to stable/1.102.x.
Cherry-picked from cf42b60 (main).
Backport of #41723 to stable/1.102.x.
Cherry-picked from 34718f0 (main).
Backport of #41757 to stable/1.102.x.
Cherry-picked from 2edda5a (main).
Backport of #41879 to stable/1.102.x.
Cherry-picked from 1e161f5 (main).
Backport of #41886 to stable/1.102.x.
Cherry-picked from a83773c (main).
…ions pass-through (#42301)

Backport of #42301 to stable/1.102.x.
Cherry-picked from 1106b16 (main).
@CLAassistant

CLAassistant commented Sep 22, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
4 out of 6 committers have signed the CLA.

✅ yuneng-berri
✅ mateo-berri
✅ moe-berri
✅ tin-berri
❌ yassin-berriai
❌ devin-ai-integration[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

@mateo-berri mateo-berri changed the title feat(typesafe): backport #41607 to stable/1.102.x for v1.102.1 feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 Sep 23, 2026
@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

Comment on lines 769 to +775
encrypted_params: Final = {
k: encrypt_value_helper(v) for k, v in updated_patch.litellm_params.model_dump(exclude_none=True).items()
k: (
_effective_complexity_router_config(updated_patch.litellm_params, db_model.litellm_params)
if k == "complexity_router_config"
else encrypt_value_helper(v)
)
for k, v in updated_patch.litellm_params.model_dump(exclude_none=True).items()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security JEV Key Stored Unencrypted

When a caller supplies jev_classifier_config.api_key, this branch persists the complexity-router configuration without encrypting the nested key. The create path also encrypts only the outer mapping, leaving nested strings unchanged. Creating or updating a JEV router with an inline key therefore stores the provider credential in plaintext instead of protecting it like other model credentials.

How this was verified: The accepted nested API-key field flows unchanged through the model create and update serialization paths into the database JSON.

Knowledge Base Used: Management APIs and resources

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same lines as main since #41886: the backport carries them verbatim, and a stable line never redesigns persistence. Main fix tracked as LIT-8389

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. This persistence behavior is intentionally carried over from #41886, and redesigning nested JEV credential encryption is out of scope for this stable-line backport. I’ll treat the issue as deferred to the main-branch fix tracked by LIT-8389 rather than a blocker for this PR.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

return validated
except ValidationError as exc:
location: Final = ".".join(str(part) for part in exc.errors()[0]["loc"])
raise HTTPException(status_code=400, detail=f"Invalid member auto-router configuration at {location}.") from exc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Member saved JEV probe rejects credentials

Medium Severity

Member test_routing with saved_model_id revalidates the stored JEV config through _MemberJevClassifierConfig, which forbids api_key and api_base. A saved router that has TypeSafe credentials therefore fails member validation instead of using the server-side config.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4bfac88. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is byte-identical to main (#41615 and #41886 picked verbatim), so a saved router's member validation matches main. A stable line never redesigns it

Comment thread litellm/proxy/management_endpoints/model_management_endpoints.py
…pes for stable/1.102.x

The committed schema.d.ts and lazy OpenAPI snapshot were taken verbatim from main by the #41615 and #41757 picks, so they described classifier types, routing causes, and a guardrail that this line does not ship and lacked the /auto_router/manage permission. Regenerated under Python 3.12, the interpreter the check-ui-api-types workflow uses.
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

1 issue from previous review remains unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit d1cd2dc. Configure here.

@mateo-berri mateo-berri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mateo-berri
mateo-berri merged commit 188c9d1 into stable/1.102.x Sep 23, 2026
10 checks passed
@mateo-berri
mateo-berri deleted the litellm_cherrypick_1_102_x branch September 23, 2026 03:01
hbjydev pushed a commit to hbjydev/phoebe that referenced this pull request Sep 23, 2026
…02.1) (#736)

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.102.0` → `v1.102.1` |

---

### Release Notes

<details>
<summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary>

### [`v1.102.1`](https://github.com/BerriAI/litellm/releases/tag/v1.102.1)

[Compare Source](BerriAI/litellm@v1.102.0...v1.102.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.102.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.102.1/cosign.pub \
  ghcr.io/berriai/litellm:v1.102.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

- fix(anthropic): backport [#&#8203;42152](BerriAI/litellm#42152) and [#&#8203;42288](BerriAI/litellm#42288) to stable/1.102.x for v1.102.1 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42538](BerriAI/litellm#42538)
- feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42595](BerriAI/litellm#42595)
- chore(release): backport [#&#8203;42388](BerriAI/litellm#42388) and [#&#8203;41462](BerriAI/litellm#41462) to stable/1.102.x by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42618](BerriAI/litellm#42618)

**Full Changelog**: <BerriAI/litellm@v1.102.0...v1.102.1>

</details>

---

### Configuration

📅 **Schedule**: (in timezone Europe/London)

- 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 these updates 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDguMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEwOC4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.hayden.moe/hayden/phoebe/pulls/736
GiorgioAresu pushed a commit to GiorgioAresu/home-ops that referenced this pull request Sep 23, 2026
…02.1) (#2200)

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.102.0` → `v1.102.1` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/6) for more information.

---

### Release Notes

<details>
<summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary>

### [`v1.102.1`](https://github.com/BerriAI/litellm/releases/tag/v1.102.1)

[Compare Source](BerriAI/litellm@v1.102.0...v1.102.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.102.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.102.1/cosign.pub \
  ghcr.io/berriai/litellm:v1.102.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

- fix(anthropic): backport [#&#8203;42152](BerriAI/litellm#42152) and [#&#8203;42288](BerriAI/litellm#42288) to stable/1.102.x for v1.102.1 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42538](BerriAI/litellm#42538)
- feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42595](BerriAI/litellm#42595)
- chore(release): backport [#&#8203;42388](BerriAI/litellm#42388) and [#&#8203;41462](BerriAI/litellm#41462) to stable/1.102.x by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42618](BerriAI/litellm#42618)

**Full Changelog**: <BerriAI/litellm@v1.102.0...v1.102.1>

</details>

---

### Configuration

📅 **Schedule**: (in timezone Europe/Rome)

- 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDQuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEwNC4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.aresu.eu/GiorgioAresu/home-ops/pulls/2200
doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request Sep 23, 2026
…02.1) (#267)

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.102.0` → `v1.102.1` |

---

### Release Notes

<details>
<summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary>

### [`v1.102.1`](https://github.com/BerriAI/litellm/releases/tag/v1.102.1)

[Compare Source](BerriAI/litellm@v1.102.0...v1.102.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.102.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.102.1/cosign.pub \
  ghcr.io/berriai/litellm:v1.102.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

- fix(anthropic): backport [#&#8203;42152](BerriAI/litellm#42152) and [#&#8203;42288](BerriAI/litellm#42288) to stable/1.102.x for v1.102.1 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42538](BerriAI/litellm#42538)
- feat(typesafe): backport the jev change set to stable/1.102.x for v1.102.1 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42595](BerriAI/litellm#42595)
- chore(release): backport [#&#8203;42388](BerriAI/litellm#42388) and [#&#8203;41462](BerriAI/litellm#41462) to stable/1.102.x by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;42618](BerriAI/litellm#42618)

**Full Changelog**: <BerriAI/litellm@v1.102.0...v1.102.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:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDUuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjEwNS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.greyrock.io/todd/home-ops/pulls/267
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants