mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Expose explicit dynamic tool namespaces in thread start (#27371)
Stacked on #27365. ## Stack note [#27365](https://github.com/openai/codex/pull/27365) kept `thread/start` unchanged and converted its input in `thread_processor`. This PR updates `thread/start` to accept explicit functions and namespaces directly. Legacy per-tool arrays are still accepted and converted while reading the request. As a result, `thread_processor` can validate and pass the tools through directly, which is why some code added in #27365 is removed here. ## Why `thread/start.dynamicTools` still repeats namespace data on each function even though core now stores explicit namespace groups. The request API should use the same shape so each namespace has one description and one member list. ## What changed - Accept top-level functions and explicit namespace objects in `dynamicTools`. - Continue accepting fully legacy flat arrays, including `exposeToContext`. - Reject arrays that mix legacy and canonical entries. - Reuse the protocol types directly and remove the temporary app-server adapter. - Update validation, docs, the test client, and generated schemas. ## Test plan - `just test -p codex-app-server-protocol` - `just test -p codex-app-server dynamic_tool_call_round_trip_sends_text_content_items_to_model` - `just test -p codex-app-server thread_start_normalizes_legacy_dynamic_tools_into_model_request` - `just test -p codex-app-server thread_start_rejects_mixed_dynamic_tool_formats` - `just test -p codex-app-server thread_start_rejects_hidden_dynamic_tools_without_namespace`
This commit is contained in:
committed by
GitHub
Unverified
parent
42ad752f36
commit
11faf9af94
@@ -3531,56 +3531,6 @@ fn dynamic_tool_response_serializes_text_and_image_content_items() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_tool_spec_deserializes_defer_loading() {
|
||||
let value = json!({
|
||||
"name": "lookup_ticket",
|
||||
"description": "Fetch a ticket",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"deferLoading": true,
|
||||
});
|
||||
|
||||
let actual: DynamicToolSpec = serde_json::from_value(value).expect("deserialize");
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
DynamicToolSpec {
|
||||
namespace: None,
|
||||
name: "lookup_ticket".to_string(),
|
||||
description: "Fetch a ticket".to_string(),
|
||||
input_schema: json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" }
|
||||
}
|
||||
}),
|
||||
defer_loading: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_tool_spec_legacy_expose_to_context_inverts_to_defer_loading() {
|
||||
let value = json!({
|
||||
"name": "lookup_ticket",
|
||||
"description": "Fetch a ticket",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"exposeToContext": false,
|
||||
});
|
||||
|
||||
let actual: DynamicToolSpec = serde_json::from_value(value).expect("deserialize");
|
||||
|
||||
assert!(actual.defer_loading);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_start_params_preserve_explicit_null_service_tier() {
|
||||
let params: ThreadStartParams =
|
||||
|
||||
@@ -16,6 +16,10 @@ pub use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
pub use codex_protocol::dynamic_tools::DynamicToolFunctionSpec;
|
||||
pub use codex_protocol::dynamic_tools::DynamicToolNamespaceSpec;
|
||||
pub use codex_protocol::dynamic_tools::DynamicToolNamespaceTool;
|
||||
pub use codex_protocol::dynamic_tools::DynamicToolSpec;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::protocol::ThreadGoalStatus as CoreThreadGoalStatus;
|
||||
@@ -38,55 +42,6 @@ pub enum ThreadStartSource {
|
||||
Clear,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct DynamicToolSpec {
|
||||
#[ts(optional)]
|
||||
pub namespace: Option<String>,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub input_schema: JsonValue,
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub defer_loading: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct DynamicToolSpecDe {
|
||||
namespace: Option<String>,
|
||||
name: String,
|
||||
description: String,
|
||||
input_schema: JsonValue,
|
||||
defer_loading: Option<bool>,
|
||||
expose_to_context: Option<bool>,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for DynamicToolSpec {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let DynamicToolSpecDe {
|
||||
namespace,
|
||||
name,
|
||||
description,
|
||||
input_schema,
|
||||
defer_loading,
|
||||
expose_to_context,
|
||||
} = DynamicToolSpecDe::deserialize(deserializer)?;
|
||||
|
||||
Ok(Self {
|
||||
namespace,
|
||||
name,
|
||||
description,
|
||||
input_schema,
|
||||
defer_loading: defer_loading
|
||||
.unwrap_or_else(|| expose_to_context.map(|visible| !visible).unwrap_or(false)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// === Threads, Turns, and Items ===
|
||||
// Thread APIs
|
||||
#[derive(
|
||||
@@ -153,6 +108,10 @@ pub struct ThreadStartParams {
|
||||
#[ts(optional = nullable)]
|
||||
pub environments: Option<Vec<TurnEnvironmentParams>>,
|
||||
#[experimental("thread/start.dynamicTools")]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "codex_protocol::dynamic_tools::deserialize_dynamic_tool_specs"
|
||||
)]
|
||||
#[ts(optional = nullable)]
|
||||
pub dynamic_tools: Option<Vec<DynamicToolSpec>>,
|
||||
/// Capability roots selected for this thread by the hosting platform.
|
||||
|
||||
Reference in New Issue
Block a user