mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Add web search provider metadata and configuration (#59033)
## What changed - Add optional `ModelInfo.web_search_provider` metadata with `standalone` and `search_mcp` values. - Add `tools.client_search_provider` and expose its value as `client_search_provider` in app-server configuration responses, with updated JSON schemas, TypeScript types, and Python bindings. - Register the `hosted_search_policy` feature flag as under development and disabled by default. GitOrigin-RevId: 263c3f81a61b50f8bb53ff338b9de8186db316e0
This commit is contained in:
+19
@@ -9743,6 +9743,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"client_search_provider": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/WebSearchProvider"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Effective tools.client_search_provider hint. Model-selected providers take precedence."
|
||||
},
|
||||
"compact_prompt": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -28047,6 +28058,14 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchProvider": {
|
||||
"description": "Provider preference selected by the resolved model catalog entry.",
|
||||
"enum": [
|
||||
"standalone",
|
||||
"search_mcp"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchToolConfig": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
||||
+19
@@ -5524,6 +5524,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"client_search_provider": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WebSearchProvider"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Effective tools.client_search_provider hint. Model-selected providers take precedence."
|
||||
},
|
||||
"compact_prompt": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -25750,6 +25761,14 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchProvider": {
|
||||
"description": "Provider preference selected by the resolved model catalog entry.",
|
||||
"enum": [
|
||||
"standalone",
|
||||
"search_mcp"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchToolConfig": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
||||
@@ -511,6 +511,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"client_search_provider": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WebSearchProvider"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Effective tools.client_search_provider hint. Model-selected providers take precedence."
|
||||
},
|
||||
"compact_prompt": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -1116,6 +1127,14 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchProvider": {
|
||||
"description": "Provider preference selected by the resolved model catalog entry.",
|
||||
"enum": [
|
||||
"standalone",
|
||||
"search_mcp"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchToolConfig": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Provider preference selected by the resolved model catalog entry.
|
||||
*/
|
||||
export type WebSearchProvider = "standalone" | "search_mcp";
|
||||
@@ -93,5 +93,6 @@ export type { WebSearchContextSize } from "./WebSearchContextSize";
|
||||
export type { WebSearchItem } from "./WebSearchItem";
|
||||
export type { WebSearchLocation } from "./WebSearchLocation";
|
||||
export type { WebSearchMode } from "./WebSearchMode";
|
||||
export type { WebSearchProvider } from "./WebSearchProvider";
|
||||
export type { WebSearchToolConfig } from "./WebSearchToolConfig";
|
||||
export * as v2 from "./v2";
|
||||
|
||||
+5
-1
@@ -7,6 +7,7 @@ import type { ReasoningEffort } from "../ReasoningEffort";
|
||||
import type { ReasoningSummary } from "../ReasoningSummary";
|
||||
import type { Verbosity } from "../Verbosity";
|
||||
import type { WebSearchMode } from "../WebSearchMode";
|
||||
import type { WebSearchProvider } from "../WebSearchProvider";
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
import type { AnalyticsConfig } from "./AnalyticsConfig";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
@@ -22,4 +23,7 @@ export type Config = {model: string | null, review_model: string | null, model_c
|
||||
* [UNSTABLE] Optional default for where approval requests are routed for
|
||||
* review.
|
||||
*/
|
||||
approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, browser_use: BrowserUseConfig | null, computer_use: ComputerUseConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
|
||||
approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, /**
|
||||
* Effective tools.client_search_provider hint. Model-selected providers take precedence.
|
||||
*/
|
||||
client_search_provider: WebSearchProvider | null, tools: ToolsV2 | null, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, browser_use: BrowserUseConfig | null, computer_use: ComputerUseConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
|
||||
|
||||
@@ -297,6 +297,8 @@ pub struct Config {
|
||||
pub forced_chatgpt_workspace_id: Option<ForcedChatgptWorkspaceIds>,
|
||||
pub forced_login_method: Option<ForcedLoginMethod>,
|
||||
pub web_search: Option<WebSearchMode>,
|
||||
/// Effective tools.client_search_provider hint. Model-selected providers take precedence.
|
||||
pub client_search_provider: Option<codex_protocol::openai_models::WebSearchProvider>,
|
||||
pub tools: Option<ToolsV2>,
|
||||
pub instructions: Option<String>,
|
||||
pub developer_instructions: Option<String>,
|
||||
|
||||
@@ -2130,6 +2130,7 @@ fn config_granular_approval_policy_is_marked_experimental() {
|
||||
forced_chatgpt_workspace_id: None,
|
||||
forced_login_method: None,
|
||||
web_search: None,
|
||||
client_search_provider: None,
|
||||
tools: None,
|
||||
instructions: None,
|
||||
developer_instructions: None,
|
||||
@@ -2165,6 +2166,7 @@ fn config_approvals_reviewer_is_marked_experimental() {
|
||||
forced_chatgpt_workspace_id: None,
|
||||
forced_login_method: None,
|
||||
web_search: None,
|
||||
client_search_provider: None,
|
||||
tools: None,
|
||||
instructions: None,
|
||||
developer_instructions: None,
|
||||
|
||||
@@ -161,8 +161,12 @@ impl ConfigManager {
|
||||
|
||||
let json_value = serde_json::to_value(&effective_config_toml)
|
||||
.map_err(|err| ConfigManagerError::json("failed to serialize configuration", err))?;
|
||||
let config: ApiConfig = serde_json::from_value(json_value)
|
||||
let mut config: ApiConfig = serde_json::from_value(json_value)
|
||||
.map_err(|err| ConfigManagerError::json("failed to deserialize configuration", err))?;
|
||||
config.client_search_provider = effective_config_toml
|
||||
.tools
|
||||
.as_ref()
|
||||
.and_then(|tools| tools.client_search_provider);
|
||||
|
||||
let mut origins = layers.origins_with_path_filter(|segments| {
|
||||
layers
|
||||
|
||||
@@ -56,6 +56,7 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo {
|
||||
availability_nux: preset.availability_nux.clone(),
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -148,6 +148,7 @@ async fn models_client_hits_models_endpoint() {
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -706,6 +706,8 @@ pub struct RealtimeAudioToml {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub struct ToolsToml {
|
||||
/// Client provider preference when the model does not select a provider.
|
||||
pub client_search_provider: Option<codex_protocol::openai_models::WebSearchProvider>,
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "deserialize_optional_web_search_tool_config"
|
||||
|
||||
@@ -1104,6 +1104,9 @@
|
||||
"hooks": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hosted_search_policy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"image_detail_original": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -5056,6 +5059,14 @@
|
||||
"ToolsToml": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"client_search_provider": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WebSearchProvider"
|
||||
}
|
||||
],
|
||||
"description": "Client provider preference when the model does not select a provider."
|
||||
},
|
||||
"experimental_request_user_input": {
|
||||
"$ref": "#/definitions/ExperimentalRequestUserInput"
|
||||
},
|
||||
@@ -7342,6 +7353,14 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchProvider": {
|
||||
"description": "Provider preference selected by the resolved model catalog entry.",
|
||||
"enum": [
|
||||
"standalone",
|
||||
"search_mcp"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WebSearchToolConfig": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -7845,6 +7864,9 @@
|
||||
"hooks": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hosted_search_policy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"image_detail_original": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
@@ -493,8 +493,7 @@ web_search = true
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: None,
|
||||
update_plan: None,
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -513,8 +512,7 @@ web_search = false
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: None,
|
||||
update_plan: None,
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -531,9 +529,8 @@ fn tools_experimental_request_user_input_defaults_to_enabled() {
|
||||
assert_eq!(
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: Some(ExperimentalRequestUserInput { enabled: true }),
|
||||
update_plan: None,
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -551,9 +548,8 @@ enabled = false
|
||||
assert_eq!(
|
||||
cfg.tools,
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
experimental_request_user_input: Some(ExperimentalRequestUserInput { enabled: false }),
|
||||
update_plan: None,
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -569,6 +565,7 @@ async fn load_config_resolves_experimental_request_user_input_enabled() -> std::
|
||||
enabled: false,
|
||||
}),
|
||||
update_plan: None,
|
||||
..Default::default()
|
||||
}),
|
||||
..ConfigToml::default()
|
||||
},
|
||||
|
||||
@@ -673,6 +673,7 @@ fn remote_model_with_auto_review_override(slug: &str, review_model: &str) -> Mod
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: Some(ApplyPatchToolType::Freeform),
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -152,6 +152,7 @@ fn test_model_info(
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
@@ -1286,6 +1287,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result<
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(large_context_window),
|
||||
|
||||
@@ -668,6 +668,7 @@ fn test_remote_model(slug: &str, priority: i32) -> ModelInfo {
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -608,6 +608,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
@@ -885,6 +886,7 @@ async fn remote_models_apply_legacy_instructions(auth: CodexAuth) -> Result<()>
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
@@ -1470,6 +1472,7 @@ fn test_remote_model_with_policy(
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy,
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -3662,6 +3662,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -119,6 +119,7 @@ fn test_model_info(
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -1650,6 +1650,7 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an
|
||||
availability_nux: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -195,6 +195,8 @@ pub enum Feature {
|
||||
WebSearchCached,
|
||||
/// Expose the extension-backed standalone web search tool.
|
||||
StandaloneWebSearch,
|
||||
/// Let the host authorize MCP search independently of native search through per-turn policy.
|
||||
HostedSearchPolicy,
|
||||
/// Use the legacy Landlock Linux sandbox fallback instead of the default
|
||||
/// bubblewrap pipeline.
|
||||
UseLegacyLandlock,
|
||||
@@ -1393,6 +1395,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::HostedSearchPolicy,
|
||||
key: "hosted_search_policy",
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::SearchTool,
|
||||
key: "search_tool",
|
||||
|
||||
@@ -123,6 +123,7 @@ pub fn model_info_from_slug(slug: &str) -> ModelInfo {
|
||||
default_verbosity: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: WebSearchToolType::Text,
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: Some(272_000),
|
||||
|
||||
@@ -34,6 +34,7 @@ fn test_model(model_messages: Option<ModelMessages>) -> ModelInfo {
|
||||
default_verbosity: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: Default::default(),
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: None,
|
||||
|
||||
@@ -348,6 +348,14 @@ pub enum WebSearchToolType {
|
||||
TextAndImage,
|
||||
}
|
||||
|
||||
/// Provider preference selected by the resolved model catalog entry.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, TS, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WebSearchProvider {
|
||||
Standalone,
|
||||
SearchMcp,
|
||||
}
|
||||
|
||||
/// Server-provided truncation policy metadata for a model.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, TS, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -460,6 +468,8 @@ pub struct ModelInfo {
|
||||
pub apply_patch_tool_type: Option<ApplyPatchToolType>,
|
||||
#[serde(default)]
|
||||
pub web_search_tool_type: WebSearchToolType,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub web_search_provider: Option<WebSearchProvider>,
|
||||
pub truncation_policy: TruncationPolicyConfig,
|
||||
#[serde(default)]
|
||||
pub supports_image_detail_original: bool,
|
||||
@@ -1213,6 +1223,7 @@ mod tests {
|
||||
default_verbosity: None,
|
||||
apply_patch_tool_type: None,
|
||||
web_search_tool_type: WebSearchToolType::Text,
|
||||
web_search_provider: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000),
|
||||
supports_image_detail_original: false,
|
||||
context_window: None,
|
||||
|
||||
@@ -7081,6 +7081,11 @@ class WebSearchMode(Enum):
|
||||
live = "live"
|
||||
|
||||
|
||||
class WebSearchProvider(Enum):
|
||||
standalone = "standalone"
|
||||
search_mcp = "search_mcp"
|
||||
|
||||
|
||||
class WebSearchToolConfig(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
@@ -11595,6 +11600,12 @@ class Config(BaseModel):
|
||||
),
|
||||
] = None
|
||||
browser_use: BrowserUseConfig | None = None
|
||||
client_search_provider: Annotated[
|
||||
WebSearchProvider | None,
|
||||
Field(
|
||||
description="Effective tools.client_search_provider hint. Model-selected providers take precedence."
|
||||
),
|
||||
] = None
|
||||
compact_prompt: str | None = None
|
||||
computer_use: ComputerUseConfig | None = None
|
||||
desktop: dict[str, Any] | None = None
|
||||
|
||||
Reference in New Issue
Block a user