5.2 KiB
DOs
-
Emit Begin/End Pair: Fire
WebSearchBeginas a marker and include the actualqueryinWebSearchEndfor UI display.// Emission sess.tx_event.send(Event { id: sub_id.to_string(), msg: EventMsg::WebSearchBegin(WebSearchBeginEvent { call_id: call_id.clone() }), }).await.ok(); sess.tx_event.send(Event { id: sub_id.to_string(), msg: EventMsg::WebSearchEnd(WebSearchEndEvent { call_id, query }), }).await.ok(); -
Validate IDs Strongly: Require a non-empty
call_idonWebSearchCallbefore emittingWebSearchEnd; log and skip otherwise.match item { ResponseItem::WebSearchCall { id: Some(call_id), action: WebSearchAction::Search { query }, .. } if !call_id.is_empty() => { sess.tx_event.send(Event { id: sub_id.to_string(), msg: EventMsg::WebSearchEnd(WebSearchEndEvent { call_id, query }), }).await.ok(); } ResponseItem::WebSearchCall { .. } => { warn!("web_search_call missing call_id; skipping end event"); } _ => {} } -
Centralize Tool Mapping: Keep the
web_searchvsweb_search_previewmapping inOpenAiTool(serde rename), and letcreate_tools_json_for_responses_apionly serialize.#[derive(Serialize)] #[serde(tag = "type", rename_all = "snake_case")] enum OpenAiTool { #[serde(rename = "web_search_preview")] WebSearch {}, } pub fn create_tools_json_for_responses_api( tools: &[OpenAiTool], ) -> anyhow::Result<Vec<serde_json::Value>> { tools.iter().map(serde_json::to_value).collect::<Result<_, _>>().map_err(Into::into) } // Client let tools_json = create_tools_json_for_responses_api(&prompt.tools)?; -
Keep UI-Sensitive Fields Stable (or Plan Deprecations): If removing
queryfromWebSearchBegin, ensure UI is updated to read it fromWebSearchEndand communicate the change. -
Exclude From History/Rollouts: Treat
WebSearchCallas non-history, non-rollout.fn is_api_message(m: &ResponseItem) -> bool { match m { ResponseItem::WebSearchCall { .. } | ResponseItem::Other => false, _ => true, } } -
Simplify Ownership: Prefer moves and defaults over unnecessary clones in config.
let history = cfg.history.unwrap_or_default(); let shell_environment_policy = cfg.shell_environment_policy.into(); let tui = cfg.tui.unwrap_or_default(); let chatgpt_base_url = config_profile.chatgpt_base_url .or(cfg.chatgpt_base_url) .unwrap_or("https://chatgpt.com/backend-api/".to_string()); -
Route SSE Explicitly: Handle
"response.output_item.added"separately to detect"web_search_call"; avoid lumping with unrelated kinds.match event.kind.as_str() { "response.output_item.added" => { if let Some(item) = event.item.as_ref() { if item.get("type").and_then(|v| v.as_str()) == Some("web_search_call") { let call_id = item.get("id").and_then(|v| v.as_str()).unwrap_or("").to_string(); tx_event.send(Ok(ResponseEvent::WebSearchCallBegin { call_id })).await.ok(); } } } "response.output_text.done" | "response.in_progress" => {} _ => {} } -
Use Inline
format!Captures: Follow repo style for string formatting.// DO let msg = format!("Searched: {query}"); -
Update TUI on End: Flush stream on begin; append a “Searched: …” history cell on end.
fn on_web_search_begin(&mut self, _: WebSearchBeginEvent) { self.flush_answer_stream_with_separator(); } fn on_web_search_end(&mut self, ev: WebSearchEndEvent) { let query = ev.query; self.add_to_history(history_cell::new_web_search_call(format!("Searched: {query}"))); }
DON’Ts
-
Don’t Default Missing IDs: Avoid
let call_id = id.unwrap_or_else(|| "".to_string());which masks protocol issues.// DON'T let call_id = id.unwrap_or_else(|| "".to_string()); // DO if let Some(call_id) = id.filter(|s| !s.is_empty()) { /* ... */ } -
Don’t Show Query on Begin: The begin event may precede the finalized query; display it on
WebSearchEndonly. -
Don’t Duplicate Tool Logic: Don’t rewrite the tool
typein multiple layers (e.g., client). Keep it inOpenAiTool’s serde mapping. -
Don’t Persist WebSearchCall: Don’t add
WebSearchCallitems to conversation history or rollout logs. -
Don’t Over-Clone Config: Avoid
.clone()onOption/Copy-like config fields whenunwrap_or_default,.or(...), and.into()suffice. -
Don’t Use Unsupported Tool Names: Until the API accepts it reliably, don’t send
"web_search"; use"web_search_preview"via serde rename. -
Don’t Collapse SSE Branches: Don’t combine
"response.output_item.added"with other event kinds; precise routing prevents regressions. -
Don’t Violate
format!Style: Avoid positional formatting when implicit captures are possible.// DON'T let msg = format!("Searched: {}", query); // DO let msg = format!("Searched: {query}");