Commit graph

43278 commits

Author SHA1 Message Date
zhanghanduo
42dcfa12a2 fix(apodex): isolate chat routing 2026-08-16 19:47:24 +08:00
zhanghanduo
190a3e01ec fix(apodex): harden provider routing metadata 2026-08-16 18:14:53 +08:00
zhanghanduo
64dcb95268 Revert "drop the undocumented response.swarm.llm_delta mapping"
A live stream against apodex-1-1-deep-research shows the event is real and
load-bearing: 181 of the 193 events are response.swarm.llm_delta and
response.output_text.delta never appears, so without the mapping the answer
text only arrives in the final response.completed snapshot.

Restores the transform with the provenance recorded in a docstring, and
covers it with the payload shape captured off the wire, including the
reasoning channel that carries 176 of those deltas and must not be mistaken
for the answer.
2026-08-16 18:14:53 +08:00
zhanghanduo
c3f8e5aa67 fix(apodex): correct model metadata and the cancel-response rebuild
Cross-checked the provider against platform.apodex.ai/docs and a live
GET /v1/models call.

- apodex-1.1 and apodex-1.1-mini advertised 256K max output; /v1/models
  reports 65536, and max_tokens is the legacy alias of max_output_tokens
- apodex-1-1-deep-discover is Responses-API-only; /v1/chat/completions
  answers 400 unsupported_api for the Discover tiers
- core models do not support response_format, so state it explicitly
- transform_cancel_response_api_response carried Content-Encoding over to
  a response whose body it had already replaced, so httpx tried to
  decompress plain JSON on read. A non-JSON body (the gateway answers a
  timed-out cancel with an HTML 504) also escaped as a pydantic
  ValidationError instead of the provider error
- drop the undocumented response.swarm.llm_delta mapping
- only the Deep Research tiers default stream to true; the core models
  follow OpenAI and default it to false
2026-08-16 18:14:53 +08:00
zhanghanduo
4ace5c8db3 fix(apodex): route deep research through responses 2026-08-16 18:14:53 +08:00
zhanghanduo
eb42c9ea06 refactor(apodex): limit registration to 1.1 models 2026-08-16 18:14:53 +08:00
zhanghanduo
3b4ff9127b refactor(apodex): move to a Python provider with model-aware transformations
The JSON provider path applies one contract to a whole provider, which is wrong
for Apodex: its two model families take different parameters. Replaces the
providers.json entry with litellm/llms/apodex/, reverting the shared
openai_like machinery to its original state.

/v1/responses is now keyed off the model. Core models are a stateless subset,
so store is pinned false and previous_response_id / background are rejected
rather than passed upstream to fail with a 400. The deep research tiers keep
all three, so background survives a client disconnect.

/v1/messages resolves per model too. Apodex serves the protocol natively for
the core models only, so the deep research tiers get no native config and fall
back to translation instead of hitting a path that does not serve them.

Chat completions pin stream to false for both families, drop tool params on the
deep research tiers, and rename max_completion_tokens to max_tokens. The
responses config also stops inheriting OpenAI's OPENAI_API_KEY fallback, which
would otherwise forward an unrelated OpenAI key to Apodex.

Tests live under tests/test_litellm/llms/apodex/ and touch no existing test file.
2026-08-16 18:14:53 +08:00
zhanghanduo
20ef10e920 feat(providers): add Apodex as an OpenAI-compatible provider
Registers apodex via providers.json with /v1/chat/completions, /v1/responses
and native /v1/messages, plus price map entries for the two core models and
the six deep research tiers.

Apodex defaults `stream` to true on both /v1/chat/completions and /v1/responses,
so a non-streaming litellm call would get SSE back and fail to parse it. Adds a
`send_explicit_stream_false` special-handling flag that pins the field on the
wire, and rewrites the JSON provider param mapping to build its result instead
of mutating the caller's dict.
2026-08-16 18:14:53 +08:00
yuneng-jiang
992a8123ac
Merge pull request #37010 from BerriAI/litellm_shadcn_next_0814
fix(ui): de-duplicate the reset budget option and polish shadcn surfaces
2026-08-15 17:18:25 -07:00
yuneng-jiang
91aee78e78
Merge pull request #37065 from BerriAI/litellm_/nice-wilson-9fbed6
test(e2e): assert provider error shape instead of pinned prose
2026-08-15 17:15:30 -07:00
Mateo Wang
dddee7d848
Merge pull request #37063 from BerriAI/litellm_pr_template_proof_format
docs(github): proof-of-fix section shows only the latest run as Before/After with nested cases
2026-08-15 17:13:12 -07:00
yuneng-jiang
1968562733
Merge pull request #37059 from BerriAI/litellm_/circleci-pipeline-triage-9b92e5
test: unstick the suites CircleCI is failing on
2026-08-15 17:12:46 -07:00
Yuneng Jiang
4df93c713c
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/nice-wilson-9fbed6 2026-08-15 16:59:36 -07:00
Mateo Wang
26113da7dd
Merge pull request #37057 from BerriAI/litellm_claude_md_gate_slot_locks
docs(claude): tell agents to let heavy gates queue for machine-wide slots
2026-08-15 16:55:47 -07:00
mateo-berri
7b60504cc8 docs(github): numbered steps are required under every proof case 2026-08-15 16:51:24 -07:00
Mateo Wang
db5c846919 chore: add more clarity 2026-08-15 16:44:31 -07:00
Mateo Wang
082469ff3d
chore: make it more concise 2026-08-15 16:41:00 -07:00
mateo-berri
83335c2f39 docs(github): proof-of-fix template shows only the latest run as Before/After with nested cases 2026-08-15 16:37:21 -07:00
Mateo Wang
13d94ec546
Merge pull request #36869 from BerriAI/litellm_lit002_typeddict_dict_literals
feat(lint): exempt TypedDict-annotated dict literals from LIT002
2026-08-15 16:35:24 -07:00
devin-ai-integration[bot]
74a1beda77
fix(panw_prisma_airs): scan tool call args as plain text, not a tool_event (#37038)
* fix(panw_prisma_airs): scan tool call args as plain text, not a tool_event

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

* refactor(panw_prisma_airs): type the tool call argument extractor

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

* test(panw_prisma_airs): cover tool call error fallback and dict masking paths

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

* fix(panw_prisma_airs): scan tool names with args and tolerate custom tool calls

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

* fix(panw_prisma_airs): scan tool call arguments that arrive already parsed

The tool call slice types arguments as a string, so a client posting parsed JSON
failed validation and the whole tool call, name included, read as unscannable and
was skipped without ever reaching AIRS. The OpenAI request path forwards
client-supplied tool_calls verbatim, so that shape is reachable.

Coerce non-string arguments instead of rejecting them, so the content is scanned.

* fix(panw_prisma_airs): route tool-block masked data by scan side, not by key name

Merging #37036 (already on staging) with this PR produces no conflict and a
silent bug. #37036 withholds prompt_masked_data on response-side tool blocks,
which was right while tool calls went out as a request-side tool_event: AIRS
reported the model's arguments under that key. This PR scans tool calls as
ordinary prompt/response text, so the side of the scan now decides which key
holds what. The model's arguments arrive under response_masked_data, already
covered by _CLIENT_HIDDEN_SCAN_FIELDS, and prompt_masked_data goes back to
being the caller's own input -- one of the audit fields LIT-5638 asks for.

Left as merged, a response-side tool block drops that field with nothing to
flag it.

- Tool-path block branch calls _build_error_detail without also_hide
- also_hide parameter removed; after this change it has no callers
- Regression test asserts both directions: model output withheld, caller
  input preserved. It fails against the auto-merged combination.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(panw_prisma_airs): a wrong-typed tool name must not suppress the scan

_ToolCallFunctionSlice types name as str, and _get_tool_call_function turns any
ValidationError into (None, None), which _scan_tool_calls_for_guardrail reads as
an unscannable tool call and skips. So a client posting "name": 123 keeps its
arguments off the wire to AIRS entirely -- no error, no log, no block. The
OpenAI request path forwards client tool_calls verbatim, so this is reachable by
any caller holding a valid key.

_coerce_arguments already existed for exactly this failure mode on the sibling
field. Widening it to cover name closes the gap:

  name='transfer_funds'   AIRS called: 1x   args scanned: True
  name=123 (int)          AIRS called: 0x   args scanned: False   <- before
  name=123 (int)          AIRS called: 1x   args scanned: True    <- after

Reported by Cursor Bugbot on fd9f6396e5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Yucheng Zhu <yucheng@berri.ai>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 23:14:43 +00:00
Mateo Wang
a41c45a4bf
Merge pull request #37050 from BerriAI/litellm_batch_cost_accounted_once
fix(batches): account a managed batch's cost exactly once
2026-08-15 16:11:15 -07:00
Yuneng Jiang
481ab07ca7
test(e2e): skip the bedrock web search cell the stack cannot provision
This cell needs the websearch_interception callback and a declared search
backend, both listed in its own module docstring. The ephemeral e2e stack
ships neither, so the request falls through to the bedrock transformation
and takes the by-design 400 that tells you to enable interception.

The cell has never been green here: the error path merged about an hour and
a half before the cell did, and the last full suite to pass predates the
cell entirely. Skip it with the reason recorded so the run reports honestly
instead of carrying a permanent red, and unskip once the stack ships the
config the docstring already spells out.
2026-08-15 16:09:02 -07:00
Yuneng Jiang
bf93483b95
test(e2e): assert provider error shape instead of pinned prose
Both providers reworded the error strings these two cells pinned, so the
suite went red without any behavior changing. Anthropic's auth error is now
"API key is invalid." rather than "invalid x-api-key", and OpenAI rejects an
empty upload with "This model does not support the format you provided.",
which names neither "file" nor "audio".

Assert the durable shape instead. The otel cell pins the machine-readable
authentication_error type plus a non-empty message, and the embedded JSON
still has to parse, which is what proves the attribute survived untruncated.
The transcription cell pins that the 400 relays the provider's own rejection
and is typed as a client input error, so a regression that swallows the
provider reason or returns a 500 still fails.
2026-08-15 16:08:52 -07:00
mateo-berri
bb1c3366cf Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_batch_cost_accounted_once
# Conflicts:
#	tests/test_litellm/proxy/openai_files_endpoint/test_files_common_utils.py
2026-08-15 15:56:11 -07:00
Yuneng Jiang
45ea07cdb3
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_/circleci-pipeline-triage-9b92e5 2026-08-15 15:54:13 -07:00
Mateo Wang
9a96b9327c
Merge pull request #37058 from BerriAI/litellm_passthrough_accept_encoding
fix(passthrough): stop forwarding client Accept-Encoding upstream
2026-08-15 15:49:46 -07:00
Yuneng Jiang
6de68f18f8
test: refresh three suites that drifted from the code they cover
The google ai studio responses test still asserted tools == [], but the
transformation now pops empty tools and tool_choice before calling
completion, so assert the keys are absent.

test_openai_endpoints pinned claude-3-sonnet-20240229, which Bedrock has
retired; move it to us.anthropic.claude-sonnet-4-5-20250929-v1:0.

The AssemblyAI EU passthrough test depended on a credential that no longer
resolves in CI, and the US path plus the bad-key case already cover the
route; drop it rather than keep a permanently red case.
2026-08-15 15:37:09 -07:00
Yuneng Jiang
edc1d65558
test(e2e/ui): address the migrated dashboard controls by role, not antd classes
The playground, logs drawer and AI Hub modal moved off antd, so the specs
that reached for .ant-select, .ant-drawer-content, .ant-modal and
.ant-radio-button-wrapper no longer match anything and time out.

Address the same controls through their accessible role and name instead,
which holds across the component library swap and reads closer to what a
user does.
2026-08-15 15:37:09 -07:00
mateo-berri
90493a217f fix(passthrough): protect accept-encoding from x-pass- forwarding 2026-08-15 15:34:28 -07:00
devin-ai-integration[bot]
d7d10be063
fix(guardrails): return the full PANW AIRS scan response on blocked requests (#37036)
* fix(guardrails): return the full PANW AIRS scan response on blocked requests

The blocked-request error detail was assembled from a hardcoded allowlist, so audit fields like prompt_detection_details, prompt_masked_data, source, transaction_id and session_id never reached the client even though AIRS returned them.

Resolves LIT-5638

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

* style(guardrails): drop redundant comment in AIRS error detail

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

* fix(panw_prisma_airs): withhold response_masked_data from the blocked-response error

The full AIRS passthrough also reached the response-side block path, where
response_masked_data carries the model's own generation. That branch is only
reached when mask_response_content is False, so the operator had explicitly
declined to deliver that text, and the error body handed it back anyway.

Withhold response_masked_data from the client-visible detail. prompt_masked_data
stays: it is the caller's own input and one of the fields the ticket asks for.

Every other AIRS field, including prompt_detection_details, source,
transaction_id and session_id, is unchanged.

* fix(panw_prisma_airs): withhold generated tool args from response-side blocks

_scan_tool_calls_for_guardrail calls AIRS with is_response=False because
tool_event is request-side in the AIRS schema, so AIRS returns the scanned
tool arguments under prompt_masked_data. When the tool calls being scanned
are the model's own output, that key holds generated content, and the
_CLIENT_HIDDEN_SCAN_FIELDS default (response_masked_data, empty on this
path) does not cover it. With the default mask_response_content=False the
block branch then shipped the model's masked tool arguments in the 400 --
the same content channel this PR closed for response_masked_data.

_build_error_detail takes an extra_hidden_fields argument so the withholding
stays in one place, and the tool-call block branch passes prompt_masked_data
when is_response is True. Request-side blocks are unchanged and still carry
prompt_masked_data, which is what LIT-5638 asks for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* style(panw_prisma_airs): apply ruff format

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Yucheng Zhu <yucheng@berri.ai>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 15:31:23 -07:00
tin-berri
540caa6574
feat(ui): direction picker and reverse-mode display for shadow evals (#36994)
* feat(ui): direction picker and reverse-mode display for shadow evals

* fix(ui): include configured model groups in the shadow eval baseline picker
2026-08-15 15:30:28 -07:00
mateo-berri
d0be6eee8a fix(passthrough): stop forwarding client Accept-Encoding upstream 2026-08-15 15:22:06 -07:00
tin-berri
4eadf92ade
feat(mcp): scope gateway session bearers to the RFC 8707 resource (#35045) 2026-08-15 14:56:34 -07:00
mateo
c9697cbce5 refactor(make): stop queueing bootstrap for a machine-wide gate slot
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-15 21:53:28 +00:00
mateo
8e588cec8c docs(claude): correct which gate entrypoints are slot-aware
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-15 21:47:29 +00:00
mateo
2e996a120f docs(claude): tell agents to let heavy gates queue for machine-wide slots
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-15 21:41:54 +00:00
Mateo Wang
8035fb3d27
Merge pull request #36988 from BerriAI/litellm_make_check_slot_lock
feat(scripts): queue heavy gates behind a machine-wide slot lock
2026-08-15 14:39:40 -07:00
Mateo Wang
5e9adb9ed7
Merge pull request #37047 from BerriAI/litellm_bedrock_batch_output_bucket
fix(bedrock): resolve the managed-batch output bucket on every path that reads it
2026-08-15 14:37:25 -07:00
Mateo Wang
2962f1223b
Merge pull request #37048 from BerriAI/litellm_internal_copy_36633
fix(bedrock): register managed-batch litellm_params so they stop leaking to the provider (internal copy of #36633)
2026-08-15 14:35:01 -07:00
mateo-berri
d9e377f129 fix(batches): confirm poller batch_processed support at startup so no retrieve accounts inline before the first poll cycle
Probe the column before the scheduler registers CheckBatchCost, closing the window where a retrieve that decided the poller was inactive billed a batch the first poll cycle then billed again. Also drop narration docstrings and section banners from the new tests.
2026-08-15 12:56:53 -07:00
mateo-berri
4e1d50442c fix(batches): persist the creating key and tags on managed batches created via /v1/batches
The retrieve path now defers a managed batch's accounting to CheckBatchCost, which
bills the key, team, and tags stored on the managed object row. The /v1/batches
create hook never persisted api_key or request_tags there (only the passthrough
creates did), so the poller attributed the cost to the user alone and the creating
key's spend stayed at zero.
2026-08-15 12:45:00 -07:00
Mateo Wang
abddd64285
Merge pull request #34257 from heathriel/litellm_fireworks_router_slug_support
fix(fireworks_ai): support router slugs via routers/ prefix
2026-08-15 12:21:39 -07:00
mateo-berri
a10669b28c Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_batch_cost_accounted_once 2026-08-15 12:17:48 -07:00
yucheng-berri
873215572a
fix(ptu): stop a PTU deployment billing for grounded search (#37043)
* fix(ptu): stop a PTU deployment billing for grounded search

A PTU deployment is billed by the flat cost of its reserved capacity, so the
model write endpoints refuse a rate the caller supplies and zero the ones already
stored. search_context_cost_per_query escaped both: it holds its rates in a table
keyed by context size, and the guard only recognised a number as a price, so a
grounded request on a PTU deployment kept billing per search on top of the flat
cost.

A table now counts as a price when it holds a non-zero rate. It is zeroed in
place rather than emptied the way tiered_pricing is, because an absent table
means the provider's own default rate rather than free, so dropping it would
start a charge instead of stopping one. For the same reason an all-zero table is
not read as a price: it is how an operator expresses free.

* fix(ptu): zero the search rate on every PTU deployment

A deployment that never stored its own search table is the normal case, and an
absent table means the provider's default rate, so the zeroing has to be written
unconditionally the way the per-token zeros already are. Writing it only where a
table was already stored left the default path billing per grounded search, which
is the charge this set out to stop.

The predicate that reads a table is split out rather than recursing, since the
repo's recursion gate rejects an unignored recursive function and one level is
all a rate table needs.
2026-08-15 12:15:46 -07:00
tin-berri
f338cfb531
feat: shadow eval samples /v1/messages and /v1/responses traffic (#36830) 2026-08-15 12:15:23 -07:00
mateo-berri
f93098068e Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_do_36634
# Conflicts:
#	litellm/batches/batch_utils.py
2026-08-15 12:12:47 -07:00
mateo-berri
1524880dce fix(batches): sign the retrieve-path output read with the deployment's AWS credentials 2026-08-15 12:09:59 -07:00
Mateo Wang
3ac2fbe1b0
Merge pull request #36876 from cu-aaii/litellm_fix_batch_spend_log_constant_request_id
fix(spend): give a batch's cost row a primary key of its own
2026-08-15 12:05:43 -07:00
mateo-berri
71d951bfc0 chore(types): drop redundant comments around the bedrock batch params 2026-08-15 11:56:04 -07:00
Mateo Wang
5871ba22ca
Merge pull request #36943 from weigandconstruction/fix_databricks_streaming_cache_usage
fix(databricks): surface provider usage, including prompt-cache counts, in streaming chunks
2026-08-15 11:55:16 -07:00