Skip to content

feat: support Codex client-executed tool search - #148

Draft
haoshan98 wants to merge 11 commits into
vllm-project:mainfrom
EmbeddedLLM:codex-tool-search
Draft

feat: support Codex client-executed tool search#148
haoshan98 wants to merge 11 commits into
vllm-project:mainfrom
EmbeddedLLM:codex-tool-search

Conversation

@haoshan98

@haoshan98 haoshan98 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Support Codex client-executed tool search for function and namespace discovery

Summary

This PR fully supports the Codex client-executed tool-search flow covered by the six Codex cassettes. It does not claim complete public Responses API tool-search parity. Codex searches its catalog and executes the selected function; the gateway adapts the protocol, validates call/output correlation, and preserves the flow across transports and response continuation.

  • Add typed models for tool_search, tool_search_call, and tool_search_output that preserve extension fields and opaque loaded definitions, including the exact defer_loading wire field on function declarations.
  • Normalize a client tool_search declaration into an ordinary upstream function named tool_search, preserving its description and parameter schema and setting strict: false.
  • Restore a well-formed upstream fallback function_call(name="tool_search") as a canonical tool_search_call, and preserve the subsequent client tool_search_output. Blocking responses, HTTP streaming, and Responses WebSocket mode expose the canonical public items and restore the client-facing response.tools declarations, removing defer_loading only from functions validated as loaded.
  • Promote loaded top-level functions and namespace function members only from a completed tool_search_call and a matching completed tool_search_output with the same nonempty call ID. Ignore unmatched, incomplete, duplicate, conflicting, and ambiguous promotions; prefer existing top-level function names on collisions.
  • Restore the original namespace on dynamically loaded namespace-member calls before returning them to Codex.
  • Preserve tool-search items through accumulation, persistence, store: false execution, and response continuation.

The supported three-turn client flow is:

  1. Codex sends a client-executed tool_search declaration and a deferred function inside a Codex namespace. The gateway normalizes the search tool for the upstream provider, then restores the provider's fallback function call as a public tool_search_call.
  2. Codex returns the matching tool_search_output. The gateway validates the completed pair, promotes the selected namespace function for the next inference round, and restores the namespace on the resulting function call.
  3. Codex returns the function call output and receives the final assistant message.

The fallback function is private to the gateway-to-upstream boundary. Well-formed fallback calls with nonempty call IDs do not leak through blocking, HTTP streaming, or WebSocket public responses. Malformed fallbacks, including calls with missing or empty call IDs and invalid JSON arguments where applicable, pass through and are outside this PR.

Responses API compatibility

The implementation aligns with the [OpenAI tool-search guide][tool-search-guide] for this Codex client-executed flow, with these explicit boundaries:

  • Hosted or bare tool_search remains a native pass-through declaration and depends on upstream provider support. The gateway does not perform hosted discovery.
  • MCP defer_loading is not supported end to end: McpToolParam does not model the field and MCP discovery remains eager. The live MCP-backed fixture is exposed to Codex as a namespace function, so it validates the supported function/namespace path rather than native Responses type: "mcp" defer loading.
  • Client-output promotion supports top-level functions and namespace function members. It does not support every possible public Responses API loaded-tool type.
  • additional_tools input items are not modeled.
  • A synthesized gateway tool_search_call preserves call ID correlation semantics but does not reproduce the tsc_ item IDs observed from OpenAI.

Signed-off-by: haoshan98 <haoshanw@gmail.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7def4472d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +254 to +255
self.tool_search_name_owned |=
tool_search::loaded_function_names(input).contains(tool_search::TOOL_SEARCH_NAME);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not disable fallback for a skipped loaded tool_search

When a completed client search output contains a function named tool_search and the request still declares the native client search tool, promote_loaded_function_tools skips that function because the native declaration already reserves the same upstream name. These lines nevertheless mark the name as owned, disabling fallback restoration; if the provider subsequently emits its native search as the documented function_call fallback, Codex receives an ordinary function call and does not execute another search. Only treat a loaded name as owned when that function was actually promoted (or otherwise remains callable upstream).

Useful? React with 👍 / 👎.

Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Comment thread crates/agentic-server-core/src/executor/accumulator.rs Outdated
self.in_flight.insert(item_id.clone(), inflight);
}
(SSEEventType::OutputItemAdded, payload @ EventPayload::OutputItemAdded { .. }) => {
self.begin_output_item(payload);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

could you please put back the original code instead of refactoring in `self.begin_output_item. Any refactoring should be done in separate PR. it makes it difficult to review the changes only required for PR feature support.

Comment thread crates/agentic-server-core/src/types/request_response.rs Outdated
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
Signed-off-by: haoshan98 <haoshanw@gmail.com>
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.

2 participants