Compare commits

...

2 Commits

Author SHA1 Message Date
easong-openai
ee1a1ba096 Merge branch 'main' into easong/search-slash 2025-09-08 15:39:38 -07:00
easong-openai
c56b871369 slash command for enabling search for the session 2025-09-08 15:07:44 -07:00
7 changed files with 72 additions and 1 deletions

View File

@@ -1080,6 +1080,7 @@ async fn submission_loop(
model,
effort,
summary,
include_web_search_request,
} => {
// Recalculate the persistent turn context with provided overrides.
let prev = Arc::clone(&turn_context);
@@ -1123,13 +1124,16 @@ async fn submission_loop(
.unwrap_or(prev.sandbox_policy.clone());
let new_cwd = cwd.clone().unwrap_or_else(|| prev.cwd.clone());
let effective_web_search =
include_web_search_request.unwrap_or(prev.tools_config.web_search_request);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_family: &effective_family,
approval_policy: new_approval_policy,
sandbox_policy: new_sandbox_policy.clone(),
include_plan_tool: config.include_plan_tool,
include_apply_patch_tool: config.include_apply_patch_tool,
include_web_search_request: config.tools_web_search_request,
include_web_search_request: effective_web_search,
use_streamable_shell_tool: config.use_experimental_streamable_shell_tool,
include_view_image_tool: config.include_view_image_tool,
});

View File

@@ -389,6 +389,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() {
model: Some("o3".to_string()),
effort: Some(ReasoningEffort::High),
summary: Some(ReasoningSummary::Detailed),
include_web_search_request: None,
})
.await
.unwrap();

View File

@@ -117,6 +117,12 @@ pub enum Op {
/// Updated reasoning summary preference (honored only for reasoning-capable models).
#[serde(skip_serializing_if = "Option::is_none")]
summary: Option<ReasoningSummaryConfig>,
/// Toggle availability of the `web_search` tool in subsequent turns.
/// When `Some(true)`, include the web search tool; when `Some(false)`,
/// exclude it. When `None`, preserve the previous session state.
#[serde(skip_serializing_if = "Option::is_none")]
include_web_search_request: Option<bool>,
},
/// Approve a command execution

View File

@@ -299,6 +299,9 @@ impl App {
AppEvent::UpdateSandboxPolicy(policy) => {
self.chat_widget.set_sandbox_policy(policy);
}
AppEvent::UpdateWebSearchRequest(enabled) => {
self.chat_widget.set_web_search_enabled(enabled);
}
}
Ok(true)
}

View File

@@ -57,6 +57,9 @@ pub(crate) enum AppEvent {
/// Update the current sandbox policy in the running app and widget.
UpdateSandboxPolicy(SandboxPolicy),
/// Update whether the web_search tool is available for this session.
UpdateWebSearchRequest(bool),
/// Forwarded conversation history snapshot from the current conversation.
ConversationHistory(ConversationHistoryResponseEvent),
}

View File

@@ -836,6 +836,9 @@ impl ChatWidget {
SlashCommand::Approvals => {
self.open_approvals_popup();
}
SlashCommand::Search => {
self.open_search_popup();
}
SlashCommand::Quit => {
self.app_event_tx.send(AppEvent::ExitRequest);
}
@@ -1185,6 +1188,7 @@ impl ChatWidget {
model: Some(model_slug.clone()),
effort: Some(effort),
summary: None,
include_web_search_request: None,
}));
tx.send(AppEvent::UpdateModel(model_slug.clone()));
tx.send(AppEvent::UpdateReasoningEffort(effort));
@@ -1233,6 +1237,7 @@ impl ChatWidget {
model: None,
effort: None,
summary: None,
include_web_search_request: None,
}));
tx.send(AppEvent::UpdateAskForApprovalPolicy(approval));
tx.send(AppEvent::UpdateSandboxPolicy(sandbox.clone()));
@@ -1253,6 +1258,47 @@ impl ChatWidget {
);
}
pub(crate) fn open_search_popup(&mut self) {
let cur = self.config.tools_web_search_request;
let mut items: Vec<SelectionItem> = Vec::new();
for (name, enabled, desc) in [
("Search Disabled", false, None),
(
"Search Enabled for Session",
true,
Some("Makes the web_search tool available to the model".to_string()),
),
] {
let is_current = cur == enabled;
let actions: Vec<SelectionAction> = vec![Box::new(move |tx| {
tx.send(AppEvent::CodexOp(Op::OverrideTurnContext {
cwd: None,
approval_policy: None,
sandbox_policy: None,
model: None,
effort: None,
summary: None,
include_web_search_request: Some(enabled),
}));
tx.send(AppEvent::UpdateWebSearchRequest(enabled));
})];
items.push(SelectionItem {
name: name.to_string(),
description: desc.clone(),
is_current,
actions,
});
}
self.bottom_pane.show_selection_view(
"Select Web Search".to_string(),
Some("Toggle web search tool availability for this session".to_string()),
Some("Press Enter to confirm or Esc to go back".to_string()),
items,
);
}
/// Set the approval policy in the widget's config copy.
pub(crate) fn set_approval_policy(&mut self, policy: AskForApproval) {
self.config.approval_policy = policy;
@@ -1273,6 +1319,11 @@ impl ChatWidget {
self.config.model = model;
}
/// Set web search tool availability in the widget's config copy.
pub(crate) fn set_web_search_enabled(&mut self, enabled: bool) {
self.config.tools_web_search_request = enabled;
}
pub(crate) fn add_mcp_output(&mut self) {
if self.config.mcp_servers.is_empty() {
self.add_to_history(history_cell::empty_mcp_output());

View File

@@ -14,6 +14,7 @@ pub enum SlashCommand {
// more frequently used commands should be listed first.
Model,
Approvals,
Search,
New,
Init,
Compact,
@@ -40,6 +41,7 @@ impl SlashCommand {
SlashCommand::Status => "show current session configuration and token usage",
SlashCommand::Model => "choose what model and reasoning effort to use",
SlashCommand::Approvals => "choose what Codex can do without approval",
SlashCommand::Search => "enable or disable web search for this session",
SlashCommand::Mcp => "list configured MCP tools",
SlashCommand::Logout => "log out of Codex",
#[cfg(debug_assertions)]
@@ -61,6 +63,7 @@ impl SlashCommand {
| SlashCommand::Compact
| SlashCommand::Model
| SlashCommand::Approvals
| SlashCommand::Search
| SlashCommand::Logout => false,
SlashCommand::Diff
| SlashCommand::Mention