mirror of
https://github.com/openai/codex.git
synced 2026-04-28 16:45:54 +00:00
501 lines
20 KiB
Markdown
501 lines
20 KiB
Markdown
# PR #2852: Following up on #2371 post commit feedback
|
|
|
|
- URL: https://github.com/openai/codex/pull/2852
|
|
- Author: aibrahim-oai
|
|
- Created: 2025-08-28 20:35:14 UTC
|
|
- Updated: 2025-08-29 02:24:46 UTC
|
|
- Changes: +89/-49, Files changed: 13, Commits: 5
|
|
|
|
## Description
|
|
|
|
- Introduce websearch end to complement the begin
|
|
- Moves the logic of adding the sebsearch tool to create_tools_json_for_responses_api
|
|
- Making it the client responsibility to toggle the tool on or off
|
|
- Other misc in #2371 post commit feedback
|
|
- Show the query:
|
|
|
|
<img width="1392" height="151" alt="image" src="https://github.com/user-attachments/assets/8457f1a6-f851-44cf-bcca-0d4fe460ce89" />
|
|
|
|
## Full Diff
|
|
|
|
```diff
|
|
diff --git a/codex-rs/core/src/chat_completions.rs b/codex-rs/core/src/chat_completions.rs
|
|
index 641574074e..6eca119f3e 100644
|
|
--- a/codex-rs/core/src/chat_completions.rs
|
|
+++ b/codex-rs/core/src/chat_completions.rs
|
|
@@ -129,7 +129,9 @@ pub(crate) async fn stream_chat_completions(
|
|
"content": output,
|
|
}));
|
|
}
|
|
- ResponseItem::Reasoning { .. } | ResponseItem::Other => {
|
|
+ ResponseItem::Reasoning { .. }
|
|
+ | ResponseItem::WebSearchCall { .. }
|
|
+ | ResponseItem::Other => {
|
|
// Omit these items from the conversation history.
|
|
continue;
|
|
}
|
|
@@ -623,11 +625,8 @@ where
|
|
Poll::Ready(Some(Ok(ResponseEvent::ReasoningSummaryPartAdded))) => {
|
|
continue;
|
|
}
|
|
- Poll::Ready(Some(Ok(ResponseEvent::WebSearchCallBegin { .. }))) => {
|
|
- return Poll::Ready(Some(Ok(ResponseEvent::WebSearchCallBegin {
|
|
- call_id: String::new(),
|
|
- query: None,
|
|
- })));
|
|
+ Poll::Ready(Some(Ok(ResponseEvent::WebSearchCallBegin { call_id }))) => {
|
|
+ return Poll::Ready(Some(Ok(ResponseEvent::WebSearchCallBegin { call_id })));
|
|
}
|
|
}
|
|
}
|
|
diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs
|
|
index e0ab334fcf..b7bfeaa7ca 100644
|
|
--- a/codex-rs/core/src/client.rs
|
|
+++ b/codex-rs/core/src/client.rs
|
|
@@ -160,21 +160,7 @@ impl ModelClient {
|
|
let store = prompt.store && auth_mode != Some(AuthMode::ChatGPT);
|
|
|
|
let full_instructions = prompt.get_full_instructions(&self.config.model_family);
|
|
- let mut tools_json = create_tools_json_for_responses_api(&prompt.tools)?;
|
|
- // ChatGPT backend expects the preview name for web search.
|
|
- if auth_mode == Some(AuthMode::ChatGPT) {
|
|
- for tool in &mut tools_json {
|
|
- if let Some(map) = tool.as_object_mut()
|
|
- && map.get("type").and_then(|v| v.as_str()) == Some("web_search")
|
|
- {
|
|
- map.insert(
|
|
- "type".to_string(),
|
|
- serde_json::Value::String("web_search_preview".to_string()),
|
|
- );
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
+ let tools_json = create_tools_json_for_responses_api(&prompt.tools)?;
|
|
let reasoning = create_reasoning_param_for_request(
|
|
&self.config.model_family,
|
|
self.effort,
|
|
@@ -607,11 +593,9 @@ async fn process_sse<S>(
|
|
| "response.custom_tool_call_input.delta"
|
|
| "response.custom_tool_call_input.done" // also emitted as response.output_item.done
|
|
| "response.in_progress"
|
|
- | "response.output_item.added"
|
|
- | "response.output_text.done" => {
|
|
- if event.kind == "response.output_item.added"
|
|
- && let Some(item) = event.item.as_ref()
|
|
- {
|
|
+ | "response.output_text.done" => {}
|
|
+ "response.output_item.added" => {
|
|
+ if let Some(item) = event.item.as_ref() {
|
|
// Detect web_search_call begin and forward a synthetic event upstream.
|
|
if let Some(ty) = item.get("type").and_then(|v| v.as_str())
|
|
&& ty == "web_search_call"
|
|
@@ -621,7 +605,7 @@ async fn process_sse<S>(
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("")
|
|
.to_string();
|
|
- let ev = ResponseEvent::WebSearchCallBegin { call_id, query: None };
|
|
+ let ev = ResponseEvent::WebSearchCallBegin { call_id };
|
|
if tx_event.send(Ok(ev)).await.is_err() {
|
|
return;
|
|
}
|
|
diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs
|
|
index e2c191f4a5..2740105100 100644
|
|
--- a/codex-rs/core/src/client_common.rs
|
|
+++ b/codex-rs/core/src/client_common.rs
|
|
@@ -95,7 +95,6 @@ pub enum ResponseEvent {
|
|
ReasoningSummaryPartAdded,
|
|
WebSearchCallBegin {
|
|
call_id: String,
|
|
- query: Option<String>,
|
|
},
|
|
}
|
|
|
|
diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs
|
|
index 365969ac02..3df69c8590 100644
|
|
--- a/codex-rs/core/src/codex.rs
|
|
+++ b/codex-rs/core/src/codex.rs
|
|
@@ -100,6 +100,7 @@ use crate::protocol::Submission;
|
|
use crate::protocol::TaskCompleteEvent;
|
|
use crate::protocol::TurnDiffEvent;
|
|
use crate::protocol::WebSearchBeginEvent;
|
|
+use crate::protocol::WebSearchEndEvent;
|
|
use crate::rollout::RolloutRecorder;
|
|
use crate::safety::SafetyCheck;
|
|
use crate::safety::assess_command_safety;
|
|
@@ -118,6 +119,7 @@ use codex_protocol::models::ReasoningItemReasoningSummary;
|
|
use codex_protocol::models::ResponseInputItem;
|
|
use codex_protocol::models::ResponseItem;
|
|
use codex_protocol::models::ShellToolCallParams;
|
|
+use codex_protocol::models::WebSearchAction;
|
|
|
|
// A convenience extension trait for acquiring mutex locks where poisoning is
|
|
// unrecoverable and should abort the program. This avoids scattered `.unwrap()`
|
|
@@ -1746,13 +1748,12 @@ async fn try_run_turn(
|
|
.await?;
|
|
output.push(ProcessedResponseItem { item, response });
|
|
}
|
|
- ResponseEvent::WebSearchCallBegin { call_id, query } => {
|
|
- let q = query.unwrap_or_else(|| "Searching Web...".to_string());
|
|
+ ResponseEvent::WebSearchCallBegin { call_id } => {
|
|
let _ = sess
|
|
.tx_event
|
|
.send(Event {
|
|
id: sub_id.to_string(),
|
|
- msg: EventMsg::WebSearchBegin(WebSearchBeginEvent { call_id, query: q }),
|
|
+ msg: EventMsg::WebSearchBegin(WebSearchBeginEvent { call_id }),
|
|
})
|
|
.await;
|
|
}
|
|
@@ -2048,6 +2049,17 @@ async fn handle_response_item(
|
|
debug!("unexpected CustomToolCallOutput from stream");
|
|
None
|
|
}
|
|
+ ResponseItem::WebSearchCall { id, action, .. } => {
|
|
+ if let WebSearchAction::Search { query } = action {
|
|
+ let call_id = id.unwrap_or_else(|| "".to_string());
|
|
+ let event = Event {
|
|
+ id: sub_id.to_string(),
|
|
+ msg: EventMsg::WebSearchEnd(WebSearchEndEvent { call_id, query }),
|
|
+ };
|
|
+ sess.tx_event.send(event).await.ok();
|
|
+ }
|
|
+ None
|
|
+ }
|
|
ResponseItem::Other => None,
|
|
};
|
|
Ok(output)
|
|
diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs
|
|
index 4d623c3e5b..a39756a9cf 100644
|
|
--- a/codex-rs/core/src/config.rs
|
|
+++ b/codex-rs/core/src/config.rs
|
|
@@ -497,7 +497,6 @@ pub struct ProjectConfig {
|
|
|
|
#[derive(Deserialize, Debug, Clone, Default)]
|
|
pub struct ToolsToml {
|
|
- // Renamed from `web_search_request`; keep alias for backwards compatibility.
|
|
#[serde(default, alias = "web_search_request")]
|
|
pub web_search: Option<bool>,
|
|
|
|
@@ -663,7 +662,7 @@ impl Config {
|
|
})?
|
|
.clone();
|
|
|
|
- let shell_environment_policy = cfg.shell_environment_policy.clone().into();
|
|
+ let shell_environment_policy = cfg.shell_environment_policy.into();
|
|
|
|
let resolved_cwd = {
|
|
use std::env;
|
|
@@ -684,7 +683,7 @@ impl Config {
|
|
}
|
|
};
|
|
|
|
- let history = cfg.history.clone().unwrap_or_default();
|
|
+ let history = cfg.history.unwrap_or_default();
|
|
|
|
let tools_web_search_request = override_tools_web_search_request
|
|
.or(cfg.tools.as_ref().and_then(|t| t.web_search))
|
|
@@ -766,7 +765,7 @@ impl Config {
|
|
codex_home,
|
|
history,
|
|
file_opener: cfg.file_opener.unwrap_or(UriBasedFileOpener::VsCode),
|
|
- tui: cfg.tui.clone().unwrap_or_default(),
|
|
+ tui: cfg.tui.unwrap_or_default(),
|
|
codex_linux_sandbox_exe,
|
|
|
|
hide_agent_reasoning: cfg.hide_agent_reasoning.unwrap_or(false),
|
|
@@ -785,7 +784,7 @@ impl Config {
|
|
model_verbosity: config_profile.model_verbosity.or(cfg.model_verbosity),
|
|
chatgpt_base_url: config_profile
|
|
.chatgpt_base_url
|
|
- .or(cfg.chatgpt_base_url.clone())
|
|
+ .or(cfg.chatgpt_base_url)
|
|
.unwrap_or("https://chatgpt.com/backend-api/".to_string()),
|
|
|
|
experimental_resume,
|
|
diff --git a/codex-rs/core/src/conversation_history.rs b/codex-rs/core/src/conversation_history.rs
|
|
index a8995f57f7..ed15bd4155 100644
|
|
--- a/codex-rs/core/src/conversation_history.rs
|
|
+++ b/codex-rs/core/src/conversation_history.rs
|
|
@@ -72,7 +72,7 @@ fn is_api_message(message: &ResponseItem) -> bool {
|
|
| ResponseItem::CustomToolCallOutput { .. }
|
|
| ResponseItem::LocalShellCall { .. }
|
|
| ResponseItem::Reasoning { .. } => true,
|
|
- ResponseItem::Other => false,
|
|
+ ResponseItem::WebSearchCall { .. } | ResponseItem::Other => false,
|
|
}
|
|
}
|
|
|
|
diff --git a/codex-rs/core/src/openai_tools.rs b/codex-rs/core/src/openai_tools.rs
|
|
index f74188162c..59ca7a8361 100644
|
|
--- a/codex-rs/core/src/openai_tools.rs
|
|
+++ b/codex-rs/core/src/openai_tools.rs
|
|
@@ -47,7 +47,9 @@ pub(crate) enum OpenAiTool {
|
|
Function(ResponsesApiTool),
|
|
#[serde(rename = "local_shell")]
|
|
LocalShell {},
|
|
- #[serde(rename = "web_search")]
|
|
+ // TODO: Understand why we get an error on web_search although the API docs say it's supported.
|
|
+ // https://platform.openai.com/docs/guides/tools-web-search?api-mode=responses#:~:text=%7B%20type%3A%20%22web_search%22%20%7D%2C
|
|
+ #[serde(rename = "web_search_preview")]
|
|
WebSearch {},
|
|
#[serde(rename = "custom")]
|
|
Freeform(FreeformTool),
|
|
@@ -335,12 +337,12 @@ pub fn create_tools_json_for_responses_api(
|
|
let mut tools_json = Vec::new();
|
|
|
|
for tool in tools {
|
|
- tools_json.push(serde_json::to_value(tool)?);
|
|
+ let json = serde_json::to_value(tool)?;
|
|
+ tools_json.push(json);
|
|
}
|
|
|
|
Ok(tools_json)
|
|
}
|
|
-
|
|
/// Returns JSON values that are compatible with Function Calling in the
|
|
/// Chat Completions API:
|
|
/// https://platform.openai.com/docs/guides/function-calling?api-mode=chat
|
|
@@ -702,8 +704,8 @@ mod tests {
|
|
"number_property": { "type": "number" },
|
|
},
|
|
"required": [
|
|
- "string_property".to_string(),
|
|
- "number_property".to_string()
|
|
+ "string_property",
|
|
+ "number_property",
|
|
],
|
|
"additionalProperties": Some(false),
|
|
},
|
|
diff --git a/codex-rs/core/src/rollout.rs b/codex-rs/core/src/rollout.rs
|
|
index 46098c1637..3c7162e883 100644
|
|
--- a/codex-rs/core/src/rollout.rs
|
|
+++ b/codex-rs/core/src/rollout.rs
|
|
@@ -135,7 +135,7 @@ impl RolloutRecorder {
|
|
| ResponseItem::CustomToolCall { .. }
|
|
| ResponseItem::CustomToolCallOutput { .. }
|
|
| ResponseItem::Reasoning { .. } => filtered.push(item.clone()),
|
|
- ResponseItem::Other => {
|
|
+ ResponseItem::WebSearchCall { .. } | ResponseItem::Other => {
|
|
// These should never be serialized.
|
|
continue;
|
|
}
|
|
@@ -199,7 +199,7 @@ impl RolloutRecorder {
|
|
| ResponseItem::CustomToolCall { .. }
|
|
| ResponseItem::CustomToolCallOutput { .. }
|
|
| ResponseItem::Reasoning { .. } => items.push(item),
|
|
- ResponseItem::Other => {}
|
|
+ ResponseItem::WebSearchCall { .. } | ResponseItem::Other => {}
|
|
},
|
|
Err(e) => {
|
|
warn!("failed to parse item: {v:?}, error: {e}");
|
|
@@ -326,7 +326,7 @@ async fn rollout_writer(
|
|
| ResponseItem::Reasoning { .. } => {
|
|
writer.write_line(&item).await?;
|
|
}
|
|
- ResponseItem::Other => {}
|
|
+ ResponseItem::WebSearchCall { .. } | ResponseItem::Other => {}
|
|
}
|
|
}
|
|
}
|
|
diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs
|
|
index a3558f7407..6b680ddea9 100644
|
|
--- a/codex-rs/exec/src/event_processor_with_human_output.rs
|
|
+++ b/codex-rs/exec/src/event_processor_with_human_output.rs
|
|
@@ -25,6 +25,7 @@ use codex_core::protocol::TaskCompleteEvent;
|
|
use codex_core::protocol::TurnAbortReason;
|
|
use codex_core::protocol::TurnDiffEvent;
|
|
use codex_core::protocol::WebSearchBeginEvent;
|
|
+use codex_core::protocol::WebSearchEndEvent;
|
|
use owo_colors::OwoColorize;
|
|
use owo_colors::Style;
|
|
use shlex::try_join;
|
|
@@ -362,8 +363,9 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
|
}
|
|
}
|
|
}
|
|
- EventMsg::WebSearchBegin(WebSearchBeginEvent { call_id: _, query }) => {
|
|
- ts_println!(self, "🌐 {query}");
|
|
+ EventMsg::WebSearchBegin(WebSearchBeginEvent { call_id: _ }) => {}
|
|
+ EventMsg::WebSearchEnd(WebSearchEndEvent { call_id: _, query }) => {
|
|
+ ts_println!(self, "🌐 Searched: {query}");
|
|
}
|
|
EventMsg::PatchApplyBegin(PatchApplyBeginEvent {
|
|
call_id,
|
|
diff --git a/codex-rs/mcp-server/src/codex_tool_runner.rs b/codex-rs/mcp-server/src/codex_tool_runner.rs
|
|
index 3a26c26cd3..2f2245cc63 100644
|
|
--- a/codex-rs/mcp-server/src/codex_tool_runner.rs
|
|
+++ b/codex-rs/mcp-server/src/codex_tool_runner.rs
|
|
@@ -273,6 +273,7 @@ async fn run_codex_tool_session_inner(
|
|
| EventMsg::PatchApplyEnd(_)
|
|
| EventMsg::TurnDiff(_)
|
|
| EventMsg::WebSearchBegin(_)
|
|
+ | EventMsg::WebSearchEnd(_)
|
|
| EventMsg::GetHistoryEntryResponse(_)
|
|
| EventMsg::PlanUpdate(_)
|
|
| EventMsg::TurnAborted(_)
|
|
diff --git a/codex-rs/protocol/src/models.rs b/codex-rs/protocol/src/models.rs
|
|
index 31e61d8be3..6cdfd933b1 100644
|
|
--- a/codex-rs/protocol/src/models.rs
|
|
+++ b/codex-rs/protocol/src/models.rs
|
|
@@ -95,6 +95,22 @@ pub enum ResponseItem {
|
|
call_id: String,
|
|
output: String,
|
|
},
|
|
+ // Emitted by the Responses API when the agent triggers a web search.
|
|
+ // Example payload (from SSE `response.output_item.done`):
|
|
+ // {
|
|
+ // "id":"ws_...",
|
|
+ // "type":"web_search_call",
|
|
+ // "status":"completed",
|
|
+ // "action": {"type":"search","query":"weather: San Francisco, CA"}
|
|
+ // }
|
|
+ WebSearchCall {
|
|
+ #[serde(default, skip_serializing_if = "Option::is_none")]
|
|
+ id: Option<String>,
|
|
+ #[serde(default, skip_serializing_if = "Option::is_none")]
|
|
+ status: Option<String>,
|
|
+ action: WebSearchAction,
|
|
+ },
|
|
+
|
|
#[serde(other)]
|
|
Other,
|
|
}
|
|
@@ -162,6 +178,16 @@ pub struct LocalShellExecAction {
|
|
pub user: Option<String>,
|
|
}
|
|
|
|
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
+#[serde(tag = "type", rename_all = "snake_case")]
|
|
+pub enum WebSearchAction {
|
|
+ Search {
|
|
+ query: String,
|
|
+ },
|
|
+ #[serde(other)]
|
|
+ Other,
|
|
+}
|
|
+
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(tag = "type", rename_all = "snake_case")]
|
|
pub enum ReasoningItemReasoningSummary {
|
|
diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs
|
|
index 7f317bbfba..464dcf274c 100644
|
|
--- a/codex-rs/protocol/src/protocol.rs
|
|
+++ b/codex-rs/protocol/src/protocol.rs
|
|
@@ -439,6 +439,8 @@ pub enum EventMsg {
|
|
|
|
WebSearchBegin(WebSearchBeginEvent),
|
|
|
|
+ WebSearchEnd(WebSearchEndEvent),
|
|
+
|
|
/// Notification that the server is about to execute a command.
|
|
ExecCommandBegin(ExecCommandBeginEvent),
|
|
|
|
@@ -668,6 +670,11 @@ impl McpToolCallEndEvent {
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
pub struct WebSearchBeginEvent {
|
|
pub call_id: String,
|
|
+}
|
|
+
|
|
+#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
+pub struct WebSearchEndEvent {
|
|
+ pub call_id: String,
|
|
pub query: String,
|
|
}
|
|
|
|
diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs
|
|
index e687fc038f..2fb09900ed 100644
|
|
--- a/codex-rs/tui/src/chatwidget.rs
|
|
+++ b/codex-rs/tui/src/chatwidget.rs
|
|
@@ -30,6 +30,7 @@ use codex_core::protocol::TokenUsage;
|
|
use codex_core::protocol::TurnAbortReason;
|
|
use codex_core::protocol::TurnDiffEvent;
|
|
use codex_core::protocol::WebSearchBeginEvent;
|
|
+use codex_core::protocol::WebSearchEndEvent;
|
|
use codex_protocol::parse_command::ParsedCommand;
|
|
use crossterm::event::KeyCode;
|
|
use crossterm::event::KeyEvent;
|
|
@@ -355,9 +356,16 @@ impl ChatWidget {
|
|
self.defer_or_handle(|q| q.push_mcp_end(ev), |s| s.handle_mcp_end_now(ev2));
|
|
}
|
|
|
|
- fn on_web_search_begin(&mut self, ev: WebSearchBeginEvent) {
|
|
+ fn on_web_search_begin(&mut self, _ev: WebSearchBeginEvent) {
|
|
self.flush_answer_stream_with_separator();
|
|
- self.add_to_history(history_cell::new_web_search_call(ev.query));
|
|
+ }
|
|
+
|
|
+ fn on_web_search_end(&mut self, ev: WebSearchEndEvent) {
|
|
+ self.flush_answer_stream_with_separator();
|
|
+ self.add_to_history(history_cell::new_web_search_call(format!(
|
|
+ "Searched: {}",
|
|
+ ev.query
|
|
+ )));
|
|
}
|
|
|
|
fn on_get_history_entry_response(
|
|
@@ -969,6 +977,7 @@ impl ChatWidget {
|
|
EventMsg::McpToolCallBegin(ev) => self.on_mcp_tool_call_begin(ev),
|
|
EventMsg::McpToolCallEnd(ev) => self.on_mcp_tool_call_end(ev),
|
|
EventMsg::WebSearchBegin(ev) => self.on_web_search_begin(ev),
|
|
+ EventMsg::WebSearchEnd(ev) => self.on_web_search_end(ev),
|
|
EventMsg::GetHistoryEntryResponse(ev) => self.on_get_history_entry_response(ev),
|
|
EventMsg::McpListToolsResponse(ev) => self.on_list_mcp_tools(ev),
|
|
EventMsg::ShutdownComplete => self.on_shutdown_complete(),
|
|
```
|
|
|
|
## Review Comments
|
|
|
|
### codex-rs/core/src/client_common.rs
|
|
|
|
- Created: 2025-08-29 02:05:21 UTC | Link: https://github.com/openai/codex/pull/2852#discussion_r2308939873
|
|
|
|
```diff
|
|
@@ -95,7 +95,6 @@ pub enum ResponseEvent {
|
|
ReasoningSummaryPartAdded,
|
|
WebSearchCallBegin {
|
|
call_id: String,
|
|
- query: Option<String>,
|
|
```
|
|
|
|
> Should we keep this around? Might the UI want this?
|
|
|
|
### codex-rs/core/src/codex.rs
|
|
|
|
- Created: 2025-08-29 02:06:25 UTC | Link: https://github.com/openai/codex/pull/2852#discussion_r2308940855
|
|
|
|
```diff
|
|
@@ -2048,6 +2049,17 @@ async fn handle_response_item(
|
|
debug!("unexpected CustomToolCallOutput from stream");
|
|
None
|
|
}
|
|
+ ResponseItem::WebSearchCall { id, action, .. } => {
|
|
+ if let WebSearchAction::Search { query } = action {
|
|
+ let call_id = id.unwrap_or_else(|| "".to_string());
|
|
```
|
|
|
|
> Is this OK or a logical error? Can we make this a stronger check?
|
|
|
|
### codex-rs/protocol/src/models.rs
|
|
|
|
- Created: 2025-08-29 02:07:54 UTC | Link: https://github.com/openai/codex/pull/2852#discussion_r2308942364
|
|
|
|
```diff
|
|
@@ -95,6 +95,22 @@ pub enum ResponseItem {
|
|
call_id: String,
|
|
output: String,
|
|
},
|
|
+ // Emitted by the Responses API when the agent triggers a web search.
|
|
+ // Example payload (from SSE `response.output_item.done`):
|
|
+ // {
|
|
+ // "id":"ws_...",
|
|
+ // "type":"web_search_call",
|
|
+ // "status":"completed",
|
|
+ // "action": {"type":"search","query":"weather: San Francisco, CA"}
|
|
+ // }
|
|
+ WebSearchCall {
|
|
+ #[serde(default, skip_serializing_if = "Option::is_none")]
|
|
+ id: Option<String>,
|
|
```
|
|
|
|
> So this is not reliably supplied? |