litellm/tests/unit/responses
Kerry Lu 8207a57d84
fix(responses): keep the namespace == name shape for deferred top-level tools (#50602)
* fix(responses): keep the namespace == name shape for deferred top-level tools

The OpenAI Agents SDK matches a call to a deferred top-level function
(defer_loading=True) only when the function_call carries namespace == name,
the shape it reserves for deferred top-level tools. The Responses bridge
returned no namespace, so after a native tool search the SDK failed with
"Tool <name> not found". A client that adds namespace == name then hit the
echo path, which joined it into "<name>__<name>" for the provider.

- namespace_tool_name_map maps deferred top-level functions to (name, name),
  so non-streaming and streaming replies carry namespace == name
- the function_call echo does not qualify a name whose namespace equals it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(responses): keep the qualified name for a namespace member that shares its namespace's name

A namespace "search" with a member "search" is advertised as the chat tool
"search__search" and comes back as name == namespace == "search". The echo
rule from the previous commit skipped the join for every namespace == name,
so that replay reached the provider as "search", a tool it was never given.

The bare-name echo now applies only to the request's deferred top-level
tools (deferred_top_level_names), and the reply map uses the same set. A
deferred top-level name that a self-named namespace member also uses is left
out of both directions, so the namespace member keeps its round trip.

The lookup reads defer_loading is True instead of validating it, so
callers that only read the input (token counting under drop_params) do not
raise on a malformed flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(responses): keep the bare name for a namespace == name echo replayed without request tools

Stored history (previous_response_id) is rebuilt with no request tools, so the echo of a deferred
call carrying namespace == name used to join to name__name. Only a namespace that holds a member of
its own name still joins. The lookup also tolerates a tools value that is not a list, and the
comprehensions stay within one for and one if clause.

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

* fix(responses): read request tools through one validated accessor

The deferred-name lookup and the response-side tool maps take the request's tools from one
TypeAdapter-backed accessor, so a tools value that is not a list of objects reads as no tools and
the type gate does not grow.

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

* fix(responses): type the request mapping handed to the input transformer

transform_responses_api_input_to_messages takes dict[str, object] instead of a bare dict, so the
tools accessor's argument is known to the type gate and the instructions value is narrowed to str.

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

* fix(responses): build the namespace tool map from the validated member names

namespace_tool_name_map reads each namespace's members through the same validated accessor as the
self-named check, which keeps the type gate's per-rule counts at their merge-base values.

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

* fix(responses): replay stored turns with the tools they were sent with

Stored previous_response_id history is rebuilt with each turn's own tools, so a namespace member
that shares its namespace's name keeps the joined name__name form on replay, while a deferred
top-level call keeps the bare name. Adds the session handler regression.

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

* fix(responses): keep the object entries of a tools list and read them on the streaming path too

One accessor hands the request's tools to the request path and the streaming iterator, dropping only
the entries that are not objects instead of the whole list, so both paths see the same custom, namespace
and deferred tools. Trims the explanatory comments.

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

* fix(responses): count input tokens with the request's tools in hand

/v1/responses/input_tokens hands the request params, tools included, to the input transformer, so an
echoed call for a namespace member named after its namespace counts as name__name there too.

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

* fix(responses): hand the request tools to every input conversion that reads an echoed call

The guardrail provenance pass, the prompt caching pre-call check and the Rust precall token count convert
Responses input with the request's tools, so a namespace member named after its namespace keeps name__name
there as it does on the request path.

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

* fix(responses): keep namespace == name for deferred tools loaded by a client-side tool search

A tool the client's own tool search loaded is still a deferred top-level tool to the Agents SDK, so the bridge keeps its deferral for the response-side name map and the route strips it again before the provider sees the request.

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

---------

Co-authored-by: Homen Shum <317650355+homenshum-cheiron@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-11-10 14:24:15 -08:00
..
litellm_completion_transformation fix(responses): keep the namespace == name shape for deferred top-level tools (#50602) 2026-11-10 14:24:15 -08:00
mcp fix(tool_choice): accept the Responses API's flat allowed_tools layout on chat completions (#50467) 2026-11-10 13:04:06 -08:00
__init__.py ci: move provider-independent MCP tests into tests/unit and run mcp-integration from litellm-tests (#42904) 2026-09-24 23:07:48 +00:00
test_additional_tools.py feat(proxy): mixed Codex model picker and LiteLLM routing on native codex routes (#49408) 2026-11-06 11:14:47 -08:00
test_custom_tool_call.py test: move tests/test_litellm core utils, routing, responses, caching and rust_bridge into tests/unit (#43199) 2026-09-25 17:10:13 -07:00
test_metadata_codex_callback.py test: move tests/test_litellm core utils, routing, responses, caching and rust_bridge into tests/unit (#43199) 2026-09-25 17:10:13 -07:00
test_no_duplicate_spend_logs.py test: move tests/test_litellm core utils, routing, responses, caching and rust_bridge into tests/unit (#43199) 2026-09-25 17:10:13 -07:00
test_null_test_fix.py test: move tests/test_litellm core utils, routing, responses, caching and rust_bridge into tests/unit (#43199) 2026-09-25 17:10:13 -07:00
test_responses_api_bridge_flag.py fix(tool_choice): accept the Responses API's flat allowed_tools layout on chat completions (#50467) 2026-11-10 13:04:06 -08:00
test_responses_api_lifecycle.py fix(responses): resume background response streams with stream and starting_after (#48819) 2026-10-29 02:31:22 -07:00
test_responses_api_request_body.py fix(responses): forward top-level moderation param to OpenAI (#48708) 2026-10-28 10:33:11 -07:00
test_responses_prompt_management.py fix(sdk): decide MCP gateway routing without importing the proxy handler (#45961) 2026-10-20 01:44:54 +00:00
test_responses_router_cooldown.py refactor(router): delete the python router core, the rust core is the only core (#50178) 2026-11-05 10:40:46 -08:00
test_responses_streaming_iterator.py fix(responses): resume background response streams with stream and starting_after (#48819) 2026-10-29 02:31:22 -07:00
test_responses_supported_endpoints_passthrough.py test: move tests/test_litellm core utils, routing, responses, caching and rust_bridge into tests/unit (#43199) 2026-09-25 17:10:13 -07:00
test_responses_utils.py refactor(model-cost): move the cost map inputs, fetch and register_model overlay into Rust (#50501) 2026-11-10 02:37:35 +00:00
test_responses_websocket_all_providers.py fix(router): count a Responses WebSocket turn's tokens before the next turn is admitted (#50730) 2026-11-10 04:13:51 +00:00
test_rust_bridge_websocket.py refactor(rust-bridge): require the native extension (#48852) 2026-10-28 13:16:56 -07:00
test_sse_output_recovery.py test: move tests/test_litellm core utils, routing, responses, caching and rust_bridge into tests/unit (#43199) 2026-09-25 17:10:13 -07:00
test_streaming_iterator.py fix(prometheus): record litellm_overhead_latency_metric for streams, stamp plus the proxy's first-chunk work (#49920) 2026-11-09 18:45:00 -08:00
test_streaming_iterator_error_events.py fix(responses): surface mid-stream upstream errors that arrive without an event type (#50466) 2026-11-07 14:53:07 -08:00
test_text_format_conversion.py refactor: expose core private helpers under public names (#44871) 2026-10-07 00:05:27 +00:00