Support incremental tool declarations on GCP (#60617)

## What changed

Allow GCP Responses requests to use `incremental_tools_legacy`. When enabled, tool declarations are sent through `additional_tools` inputs instead of the top-level `tools` array.

## Testing

Extend Vertex request coverage to check incremental tool declarations, including namespaces, tool search, and `exec_command`. Add GCP cases to the scenario that verifies tool catalog updates are appended without rewriting history, with incremental tools enabled and disabled.

GitOrigin-RevId: 8f4c02f8d62575e24e695d6fae0472a09625409f
This commit is contained in:
Alex Kira
2026-11-10 16:13:17 +00:00
committed by copyberry
parent 039d0f27d5
commit 0d37c062de
4 changed files with 33 additions and 12 deletions
+2 -1
View File
@@ -16,6 +16,7 @@ use codex_file_system::EnvironmentAccess;
use codex_mcp::McpBinding;
use codex_model_provider_info::AMAZON_BEDROCK_PROVIDER_ID;
use codex_model_provider_info::AMAZON_BEDROCK_RUNTIME_PROVIDER_ID;
use codex_model_provider_info::GCP_PROVIDER_ID;
use codex_otel::SessionTelemetry;
use codex_plugin::ExecutorPluginHookSource;
use codex_protocol::items::ModelInvocationContext;
@@ -63,7 +64,7 @@ impl StepContext {
} else {
matches!(
self.turn.config.model_provider_id.as_str(),
AMAZON_BEDROCK_PROVIDER_ID | AMAZON_BEDROCK_RUNTIME_PROVIDER_ID
AMAZON_BEDROCK_PROVIDER_ID | AMAZON_BEDROCK_RUNTIME_PROVIDER_ID | GCP_PROVIDER_ID
)
.then_some(Feature::IncrementalToolsLegacy)
}
+27 -10
View File
@@ -94,11 +94,13 @@ async fn vertex_credential_file_is_retained_when_refreshing_after_401() -> Resul
Ok(())
}
#[test_case("gpt-6.1-sol"; "sol")]
#[test_case("gpt-6.1-terra"; "terra")]
#[test_case("gpt-6.1-sol", false; "sol")]
#[test_case("gpt-6.1-terra", false; "terra")]
#[test_case("gpt-6.1-sol", true; "sol_incremental")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn vertex_requests_use_command_auth_with_namespaces_and_tool_search(
model: &str,
incremental_tools: bool,
) -> Result<()> {
skip_if_no_network!(Ok(()));
@@ -132,6 +134,10 @@ async fn vertex_requests_use_command_auth_with_namespaces_and_tool_search(
config.model_provider_id = GCP_PROVIDER_ID.to_string();
config.model_provider = provider;
config.base_instructions = Some("Vertex test instructions".to_string());
config
.features
.set_enabled(Feature::IncrementalToolsLegacy, incremental_tools)
.expect("configure incremental tools on Vertex");
})
.build_with_auto_env(&server)
.await?;
@@ -183,16 +189,22 @@ async fn vertex_requests_use_command_auth_with_namespaces_and_tool_search(
request.header("x-openai-internal-codex-responses-lite"),
None
);
assert!(
request.inputs_of_type("additional_tools").is_empty(),
"Vertex request must omit Responses Lite additional_tools inputs"
);
let additional_tools = request.inputs_of_type("additional_tools");
assert_eq!(additional_tools.len(), usize::from(incremental_tools));
let body = request.body_json();
assert_eq!(body["model"], model);
assert_eq!(request.instructions_text(), "Vertex test instructions");
let tools = body["tools"]
let top_level_tools = body["tools"]
.as_array()
.context("Vertex request should include model tools")?;
let tools = if incremental_tools {
assert!(top_level_tools.is_empty());
additional_tools[0]["tools"]
.as_array()
.context("Vertex request should include incremental model tools")?
} else {
top_level_tools
};
assert_eq!(
test.codex.multi_agent_version(),
Some(MultiAgentVersion::V2)
@@ -221,9 +233,14 @@ async fn vertex_requests_use_command_auth_with_namespaces_and_tool_search(
"Vertex request should expose client-side tool search: {tools:?}"
);
assert!(
tools
.iter()
.any(|tool| tool.get("name").and_then(Value::as_str) == Some("exec_command")),
if incremental_tools {
responses::namespace_child_tool(&additional_tools[0], "functions", "exec_command")
.is_some()
} else {
tools
.iter()
.any(|tool| tool.get("name").and_then(Value::as_str) == Some("exec_command"))
},
"Vertex request should preserve the exec_command function tool: {tools:?}"
);
@@ -7,6 +7,7 @@ use codex_core::compact::AutoCompactionDisabled;
use codex_features::Feature;
use codex_model_provider_info::AMAZON_BEDROCK_PROVIDER_ID;
use codex_model_provider_info::AMAZON_BEDROCK_RUNTIME_PROVIDER_ID;
use codex_model_provider_info::GCP_PROVIDER_ID;
use codex_model_provider_info::OPENAI_PROVIDER_ID;
use codex_otel::MetricsClient;
use codex_otel::MetricsConfig;
@@ -158,6 +159,8 @@ async fn prediction_fork_keeps_inherited_tools() -> Result<()> {
#[test_case(false, None, true, AMAZON_BEDROCK_PROVIDER_ID; "mantle_provider_id_incremental")]
#[test_case(false, None, false, AMAZON_BEDROCK_RUNTIME_PROVIDER_ID; "runtime_provider_id")]
#[test_case(false, None, true, AMAZON_BEDROCK_RUNTIME_PROVIDER_ID; "runtime_provider_id_incremental")]
#[test_case(false, None, false, GCP_PROVIDER_ID; "gcp_provider_id")]
#[test_case(false, None, true, GCP_PROVIDER_ID; "gcp_provider_id_incremental")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn incremental_tools_append_changed_catalog_without_rewriting_history(
use_responses_lite: bool,
+1 -1
View File
@@ -308,7 +308,7 @@ pub enum Feature {
CommandSessionsWorldState,
/// Track top-level tool definitions in world state and emit incremental context updates.
IncrementalTools,
/// Publish incremental tool declarations on regular Amazon Bedrock Responses.
/// Publish incremental tool declarations on regular Amazon Bedrock and GCP Responses.
IncrementalToolsLegacy,
/// Suppress the model ID announcement in incremental context after a model switch.
SuppressModelChangeAnnouncements,