Skip to content

fix: preserve parallel tool call setting#128

Open
harivilasp wants to merge 5 commits into
vllm-project:mainfrom
harivilasp:fix/preserve-parallel-tool-calls
Open

fix: preserve parallel tool call setting#128
harivilasp wants to merge 5 commits into
vllm-project:mainfrom
harivilasp:fix/preserve-parallel-tool-calls

Conversation

@harivilasp

Copy link
Copy Markdown
Contributor

Summary

  • parse parallel_tool_calls on /v1/responses requests
  • forward the field through RequestPayload::to_upstream_request after gateway tool normalization
  • add unit and route-level regression coverage proving parallel_tool_calls: false reaches the mock vLLM request

Closes #127.

Why

The gateway already preserves several Responses API generation/control fields (include, temperature, top_p, max_output_tokens, truncation, metadata) while normalizing tools for vLLM. parallel_tool_calls was missing from the typed request model, so clients that explicitly disabled parallel tool calls had that setting silently dropped on the executor path.

Validation

  • cargo test -p agentic-server-core
  • cargo test -p agentic-server --test responses_test
  • cargo test --workspace
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo fmt -- --check

Focused tests were written first and failed before the implementation because parallel_tool_calls serialized as absent/null instead of false.

Signed-off-by: harivilasp <harivilasp@gmail.com>
@jiahuei

jiahuei commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

We need to handle cases where built-in tools are allowed/specified by the user, in which case parallel_tool_calls needs to be validated such that only false is allowed. Otherwise it is impossible to handle cases where the model decides to call both built-in and custom tools in parallel.

This is the behaviour of OpenAI

@harivilasp

Copy link
Copy Markdown
Contributor Author

Addressed in 9844bc3. Built-in/gateway-owned tools now force upstream parallel_tool_calls: false when the client omits it, and explicitly reject parallel_tool_calls: true for built-in tools. Client function tools can still preserve parallel_tool_calls: true.

Verified with:

  • cargo fmt --check
  • cargo test -p agentic-server-core types::request_response
  • cargo test -p agentic-server-core --test web_search_tool_test

Signed-off-by: harivilasp <harivilasp@gmail.com>
@harivilasp
harivilasp force-pushed the fix/preserve-parallel-tool-calls branch from 9844bc3 to 79059b5 Compare July 16, 2026 05:42
@maralbahari

Copy link
Copy Markdown
Collaborator

@harivilasp could you please resolves merge conflict, thanks.

@harivilasp

Copy link
Copy Markdown
Contributor Author

@harivilasp could you please resolves merge conflict, thanks.
sure on it

…-tool-calls

# Conflicts:
#	crates/agentic-server-core/benches/executor_throughput.rs
#	crates/agentic-server-core/src/executor/modes/conversation.rs
#	crates/agentic-server-core/src/executor/modes/response.rs
#	crates/agentic-server-core/src/types/request_response.rs
#	crates/agentic-server-core/tests/dispatch_loop_cassette_test.rs
#	crates/agentic-server-core/tests/support/mod.rs
#	crates/agentic-server-core/tests/web_search_tool_test.rs
@harivilasp

Copy link
Copy Markdown
Contributor Author

resolved merge confilcts

Comment thread crates/agentic-server-core/src/types/request_response.rs
Comment thread crates/agentic-server-core/src/types/request_response.rs Outdated
Signed-off-by: harivilasp <harivilasp@gmail.com>
@harivilasp
harivilasp force-pushed the fix/preserve-parallel-tool-calls branch from 58a57f2 to cd11ad7 Compare July 17, 2026 02:51
| ResponsesTool::WebSearch(_)
| ResponsesTool::FileSearch(_)
| ResponsesTool::CodeInterpreter(_)
)

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.

declares_built_in_tool's matches!(Mcp | WebSearch | FileSearch | CodeInterpreter) is a hand-maintained copy of the logic in ToolType::is_gateway_owned (!matches!(Function | CodexNamespace)). When the next built-in variant lands, someone will update one and forget the other. another regression problem is codex read_mcp_resource from client owned is mapped to gateway owned. so would need a check like
matches!(tool, ResponsesTool::Function(p) if maybe_mcp_function(p).is_some())) to put everything together then a fix like below would resolve the issue:

impl ResponsesTool {
    /// The `ToolType` the registry would assign this declaration; `None` for `Unknown`.
    /// Mirrors the classification in `ToolRegistry::build_with_handlers`.
    fn tool_type(&self) -> Option<ToolType> {
        match self {
            // read_mcp_resource functions with valid MCP metadata register as
            // MCP entries (registry.rs), so classify them the same way here.
            Self::Function(p) if maybe_mcp_function(p).is_some_and(|params| !params.is_empty()) => {
                Some(ToolType::Mcp)
            }
            Self::Function(_) => Some(ToolType::Function),
            Self::Namespace(_) => Some(ToolType::CodexNamespace),
            Self::Mcp(_) => Some(ToolType::Mcp),
            Self::WebSearch(_) => Some(ToolType::WebSearch),
            Self::FileSearch(_) => Some(ToolType::FileSearch),
            Self::CodeInterpreter(_) => Some(ToolType::CodeInterpreter),
            Self::Unknown => None,
        }
    }

    pub fn is_gateway_owned(&self) -> bool {
        self.tool_type().is_some_and(ToolType::is_gateway_owned)
    }
}

Then declares_built_in_tool collapses to tools.iter().any(ResponsesTool::is_gateway_owned). One source of truth, no signature churn, and to_function_tools keeps its single job.

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.

agreed, on this note, perhaps the test can be made more comprehensive by randomly selecting a supported built-in tool in the request

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.

@harivilasp the test test_gateway_normalization_preserves_parallel_tool_calls covers only the happy path. as @jiahuei suggested, maybe could have a more comprehensive test? random selection of supported tools and both parallel bool type?

Signed-off-by: harivilasp <harivilasp@gmail.com>
@harivilasp
harivilasp force-pushed the fix/preserve-parallel-tool-calls branch from 3b64c53 to ffc823a Compare July 17, 2026 04:54
@harivilasp
harivilasp requested a review from maralbahari July 17, 2026 05:31
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.

Preserve parallel_tool_calls in Responses gateway requests

3 participants