feat: make reasoning effort/summaries configurable (#1199)
Previous to this PR, we always set `reasoning` when making a request using the Responses API:d7245cbbc9/codex-rs/core/src/client.rs (L108-L111)Though if you tried to use the Rust CLI with `--model gpt-4.1`, this would fail with: ```shell "Unsupported parameter: 'reasoning.effort' is not supported with this model." ``` We take a cue from the TypeScript CLI, which does a check on the model name:d7245cbbc9/codex-cli/src/utils/agent/agent-loop.ts (L786-L789)This PR does a similar check, though also adds support for the following config options: ``` model_reasoning_effort = "low" | "medium" | "high" | "none" model_reasoning_summary = "auto" | "concise" | "detailed" | "none" ``` This way, if you have a model whose name happens to start with `"o"` (or `"codex"`?), you can set these to `"none"` to explicitly disable reasoning, if necessary. (That said, it seems unlikely anyone would use the Responses API with non-OpenAI models, but we provide an escape hatch, anyway.) This PR also updates both the TUI and `codex exec` to show `reasoning effort` and `reasoning summaries` in the header.
This commit is contained in:
2
codex-rs/Cargo.lock
generated
2
codex-rs/Cargo.lock
generated
@@ -635,6 +635,8 @@ dependencies = [
|
|||||||
"seccompiler",
|
"seccompiler",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"strum 0.27.1",
|
||||||
|
"strum_macros 0.27.1",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"time",
|
"time",
|
||||||
|
|||||||
@@ -142,6 +142,34 @@ Users can specify config values at multiple levels. Order of precedence is as fo
|
|||||||
3. as an entry in `config.toml`, e.g., `model = "o3"`
|
3. as an entry in `config.toml`, e.g., `model = "o3"`
|
||||||
4. the default value that comes with Codex CLI (i.e., Codex CLI defaults to `codex-mini-latest`)
|
4. the default value that comes with Codex CLI (i.e., Codex CLI defaults to `codex-mini-latest`)
|
||||||
|
|
||||||
|
## model_reasoning_effort
|
||||||
|
|
||||||
|
If the model name starts with `"o"` (as in `"o3"` or `"o4-mini"`) or `"codex"`, reasoning is enabled by default when using the Responses API. As explained in the [OpenAI Platform documentation](https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning), this can be set to:
|
||||||
|
|
||||||
|
- `"low"`
|
||||||
|
- `"medium"` (default)
|
||||||
|
- `"high"`
|
||||||
|
|
||||||
|
To disable reasoning, set `model_reasoning_effort` to `"none"` in your config:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
model_reasoning_effort = "none" # disable reasoning
|
||||||
|
```
|
||||||
|
|
||||||
|
## model_reasoning_summary
|
||||||
|
|
||||||
|
If the model name starts with `"o"` (as in `"o3"` or `"o4-mini"`) or `"codex"`, reasoning is enabled by default when using the Responses API. As explained in the [OpenAI Platform documentation](https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries), this can be set to:
|
||||||
|
|
||||||
|
- `"auto"` (default)
|
||||||
|
- `"concise"`
|
||||||
|
- `"detailed"`
|
||||||
|
|
||||||
|
To disable reasoning summaries, set `model_reasoning_summary` to `"none"` in your config:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
model_reasoning_summary = "none" # disable reasoning summaries
|
||||||
|
```
|
||||||
|
|
||||||
## sandbox_permissions
|
## sandbox_permissions
|
||||||
|
|
||||||
List of permissions to grant to the sandbox that Codex uses to execute untrusted commands:
|
List of permissions to grant to the sandbox that Codex uses to execute untrusted commands:
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ rand = "0.9"
|
|||||||
reqwest = { version = "0.12", features = ["json", "stream"] }
|
reqwest = { version = "0.12", features = ["json", "stream"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
strum = "0.27.1"
|
||||||
|
strum_macros = "0.27.1"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
time = { version = "0.3", features = ["formatting", "local-offset", "macros"] }
|
time = { version = "0.3", features = ["formatting", "local-offset", "macros"] }
|
||||||
tokio = { version = "1", features = [
|
tokio = { version = "1", features = [
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ use tracing::warn;
|
|||||||
|
|
||||||
use crate::chat_completions::AggregateStreamExt;
|
use crate::chat_completions::AggregateStreamExt;
|
||||||
use crate::chat_completions::stream_chat_completions;
|
use crate::chat_completions::stream_chat_completions;
|
||||||
use crate::client_common::Payload;
|
|
||||||
use crate::client_common::Prompt;
|
use crate::client_common::Prompt;
|
||||||
use crate::client_common::Reasoning;
|
|
||||||
use crate::client_common::ResponseEvent;
|
use crate::client_common::ResponseEvent;
|
||||||
use crate::client_common::ResponseStream;
|
use crate::client_common::ResponseStream;
|
||||||
use crate::client_common::Summary;
|
use crate::client_common::ResponsesApiRequest;
|
||||||
|
use crate::client_common::create_reasoning_param_for_request;
|
||||||
|
use crate::config_types::ReasoningEffort as ReasoningEffortConfig;
|
||||||
|
use crate::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||||
use crate::error::CodexErr;
|
use crate::error::CodexErr;
|
||||||
use crate::error::EnvVarError;
|
use crate::error::EnvVarError;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
@@ -41,14 +42,23 @@ pub struct ModelClient {
|
|||||||
model: String,
|
model: String,
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
provider: ModelProviderInfo,
|
provider: ModelProviderInfo,
|
||||||
|
effort: ReasoningEffortConfig,
|
||||||
|
summary: ReasoningSummaryConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModelClient {
|
impl ModelClient {
|
||||||
pub fn new(model: impl ToString, provider: ModelProviderInfo) -> Self {
|
pub fn new(
|
||||||
|
model: impl ToString,
|
||||||
|
provider: ModelProviderInfo,
|
||||||
|
effort: ReasoningEffortConfig,
|
||||||
|
summary: ReasoningSummaryConfig,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
model: model.to_string(),
|
model: model.to_string(),
|
||||||
client: reqwest::Client::new(),
|
client: reqwest::Client::new(),
|
||||||
provider,
|
provider,
|
||||||
|
effort,
|
||||||
|
summary,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,17 +108,15 @@ impl ModelClient {
|
|||||||
|
|
||||||
let full_instructions = prompt.get_full_instructions();
|
let full_instructions = prompt.get_full_instructions();
|
||||||
let tools_json = create_tools_json_for_responses_api(prompt, &self.model)?;
|
let tools_json = create_tools_json_for_responses_api(prompt, &self.model)?;
|
||||||
let payload = Payload {
|
let reasoning = create_reasoning_param_for_request(&self.model, self.effort, self.summary);
|
||||||
|
let payload = ResponsesApiRequest {
|
||||||
model: &self.model,
|
model: &self.model,
|
||||||
instructions: &full_instructions,
|
instructions: &full_instructions,
|
||||||
input: &prompt.input,
|
input: &prompt.input,
|
||||||
tools: &tools_json,
|
tools: &tools_json,
|
||||||
tool_choice: "auto",
|
tool_choice: "auto",
|
||||||
parallel_tool_calls: false,
|
parallel_tool_calls: false,
|
||||||
reasoning: Some(Reasoning {
|
reasoning,
|
||||||
effort: "high",
|
|
||||||
summary: Some(Summary::Auto),
|
|
||||||
}),
|
|
||||||
previous_response_id: prompt.prev_id.clone(),
|
previous_response_id: prompt.prev_id.clone(),
|
||||||
store: prompt.store,
|
store: prompt.store,
|
||||||
stream: true,
|
stream: true,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::config_types::ReasoningEffort as ReasoningEffortConfig;
|
||||||
|
use crate::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::models::ResponseItem;
|
use crate::models::ResponseItem;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
@@ -52,25 +54,59 @@ pub enum ResponseEvent {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct Reasoning {
|
pub(crate) struct Reasoning {
|
||||||
pub(crate) effort: &'static str,
|
pub(crate) effort: OpenAiReasoningEffort,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub(crate) summary: Option<Summary>,
|
pub(crate) summary: Option<OpenAiReasoningSummary>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning
|
||||||
|
#[derive(Debug, Serialize, Default, Clone, Copy)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub(crate) enum OpenAiReasoningEffort {
|
||||||
|
Low,
|
||||||
|
#[default]
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReasoningEffortConfig> for Option<OpenAiReasoningEffort> {
|
||||||
|
fn from(effort: ReasoningEffortConfig) -> Self {
|
||||||
|
match effort {
|
||||||
|
ReasoningEffortConfig::Low => Some(OpenAiReasoningEffort::Low),
|
||||||
|
ReasoningEffortConfig::Medium => Some(OpenAiReasoningEffort::Medium),
|
||||||
|
ReasoningEffortConfig::High => Some(OpenAiReasoningEffort::High),
|
||||||
|
ReasoningEffortConfig::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A summary of the reasoning performed by the model. This can be useful for
|
/// A summary of the reasoning performed by the model. This can be useful for
|
||||||
/// debugging and understanding the model's reasoning process.
|
/// debugging and understanding the model's reasoning process.
|
||||||
#[derive(Debug, Serialize)]
|
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries
|
||||||
|
#[derive(Debug, Serialize, Default, Clone, Copy)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub(crate) enum Summary {
|
pub(crate) enum OpenAiReasoningSummary {
|
||||||
|
#[default]
|
||||||
Auto,
|
Auto,
|
||||||
#[allow(dead_code)] // Will go away once this is configurable.
|
|
||||||
Concise,
|
Concise,
|
||||||
#[allow(dead_code)] // Will go away once this is configurable.
|
|
||||||
Detailed,
|
Detailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ReasoningSummaryConfig> for Option<OpenAiReasoningSummary> {
|
||||||
|
fn from(summary: ReasoningSummaryConfig) -> Self {
|
||||||
|
match summary {
|
||||||
|
ReasoningSummaryConfig::Auto => Some(OpenAiReasoningSummary::Auto),
|
||||||
|
ReasoningSummaryConfig::Concise => Some(OpenAiReasoningSummary::Concise),
|
||||||
|
ReasoningSummaryConfig::Detailed => Some(OpenAiReasoningSummary::Detailed),
|
||||||
|
ReasoningSummaryConfig::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request object that is serialized as JSON and POST'ed when using the
|
||||||
|
/// Responses API.
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct Payload<'a> {
|
pub(crate) struct ResponsesApiRequest<'a> {
|
||||||
pub(crate) model: &'a str,
|
pub(crate) model: &'a str,
|
||||||
pub(crate) instructions: &'a str,
|
pub(crate) instructions: &'a str,
|
||||||
// TODO(mbolin): ResponseItem::Other should not be serialized. Currently,
|
// TODO(mbolin): ResponseItem::Other should not be serialized. Currently,
|
||||||
@@ -88,6 +124,40 @@ pub(crate) struct Payload<'a> {
|
|||||||
pub(crate) stream: bool,
|
pub(crate) stream: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_reasoning_param_for_request(
|
||||||
|
model: &str,
|
||||||
|
effort: ReasoningEffortConfig,
|
||||||
|
summary: ReasoningSummaryConfig,
|
||||||
|
) -> Option<Reasoning> {
|
||||||
|
let effort: Option<OpenAiReasoningEffort> = effort.into();
|
||||||
|
let effort = effort?;
|
||||||
|
|
||||||
|
if model_supports_reasoning_summaries(model) {
|
||||||
|
Some(Reasoning {
|
||||||
|
effort,
|
||||||
|
summary: summary.into(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn model_supports_reasoning_summaries(model: &str) -> bool {
|
||||||
|
// Currently, we hardcode this rule to decide whether enable reasoning.
|
||||||
|
// We expect reasoning to apply only to OpenAI models, but we do not want
|
||||||
|
// users to have to mess with their config to disable reasoning for models
|
||||||
|
// that do not support it, such as `gpt-4.1`.
|
||||||
|
//
|
||||||
|
// Though if a user is using Codex with non-OpenAI models that, say, happen
|
||||||
|
// to start with "o", then they can set `model_reasoning_effort = "none` in
|
||||||
|
// config.toml to disable reasoning.
|
||||||
|
//
|
||||||
|
// Ultimately, this should also be configurable in config.toml, but we
|
||||||
|
// need to have defaults that "just work." Perhaps we could have a
|
||||||
|
// "reasoning models pattern" as part of ModelProviderInfo?
|
||||||
|
model.starts_with("o") || model.starts_with("codex")
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct ResponseStream {
|
pub(crate) struct ResponseStream {
|
||||||
pub(crate) rx_event: mpsc::Receiver<Result<ResponseEvent>>,
|
pub(crate) rx_event: mpsc::Receiver<Result<ResponseEvent>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ impl Codex {
|
|||||||
let configure_session = Op::ConfigureSession {
|
let configure_session = Op::ConfigureSession {
|
||||||
provider: config.model_provider.clone(),
|
provider: config.model_provider.clone(),
|
||||||
model: config.model.clone(),
|
model: config.model.clone(),
|
||||||
|
model_reasoning_effort: config.model_reasoning_effort,
|
||||||
|
model_reasoning_summary: config.model_reasoning_summary,
|
||||||
instructions,
|
instructions,
|
||||||
approval_policy: config.approval_policy,
|
approval_policy: config.approval_policy,
|
||||||
sandbox_policy: config.sandbox_policy.clone(),
|
sandbox_policy: config.sandbox_policy.clone(),
|
||||||
@@ -554,6 +556,8 @@ async fn submission_loop(
|
|||||||
Op::ConfigureSession {
|
Op::ConfigureSession {
|
||||||
provider,
|
provider,
|
||||||
model,
|
model,
|
||||||
|
model_reasoning_effort,
|
||||||
|
model_reasoning_summary,
|
||||||
instructions,
|
instructions,
|
||||||
approval_policy,
|
approval_policy,
|
||||||
sandbox_policy,
|
sandbox_policy,
|
||||||
@@ -575,7 +579,12 @@ async fn submission_loop(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = ModelClient::new(model.clone(), provider.clone());
|
let client = ModelClient::new(
|
||||||
|
model.clone(),
|
||||||
|
provider.clone(),
|
||||||
|
model_reasoning_effort,
|
||||||
|
model_reasoning_summary,
|
||||||
|
);
|
||||||
|
|
||||||
// abort any current running session and clone its state
|
// abort any current running session and clone its state
|
||||||
let retain_zdr_transcript =
|
let retain_zdr_transcript =
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use crate::config_profile::ConfigProfile;
|
use crate::config_profile::ConfigProfile;
|
||||||
use crate::config_types::History;
|
use crate::config_types::History;
|
||||||
use crate::config_types::McpServerConfig;
|
use crate::config_types::McpServerConfig;
|
||||||
|
use crate::config_types::ReasoningEffort;
|
||||||
|
use crate::config_types::ReasoningSummary;
|
||||||
use crate::config_types::ShellEnvironmentPolicy;
|
use crate::config_types::ShellEnvironmentPolicy;
|
||||||
use crate::config_types::ShellEnvironmentPolicyToml;
|
use crate::config_types::ShellEnvironmentPolicyToml;
|
||||||
use crate::config_types::Tui;
|
use crate::config_types::Tui;
|
||||||
@@ -112,6 +114,14 @@ pub struct Config {
|
|||||||
///
|
///
|
||||||
/// When this program is invoked, arg0 will be set to `codex-linux-sandbox`.
|
/// When this program is invoked, arg0 will be set to `codex-linux-sandbox`.
|
||||||
pub codex_linux_sandbox_exe: Option<PathBuf>,
|
pub codex_linux_sandbox_exe: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// If not "none", the value to use for `reasoning.effort` when making a
|
||||||
|
/// request using the Responses API.
|
||||||
|
pub model_reasoning_effort: ReasoningEffort,
|
||||||
|
|
||||||
|
/// If not "none", the value to use for `reasoning.summary` when making a
|
||||||
|
/// request using the Responses API.
|
||||||
|
pub model_reasoning_summary: ReasoningSummary,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -281,6 +291,9 @@ pub struct ConfigToml {
|
|||||||
/// When set to `true`, `AgentReasoning` events will be hidden from the
|
/// When set to `true`, `AgentReasoning` events will be hidden from the
|
||||||
/// UI/output. Defaults to `false`.
|
/// UI/output. Defaults to `false`.
|
||||||
pub hide_agent_reasoning: Option<bool>,
|
pub hide_agent_reasoning: Option<bool>,
|
||||||
|
|
||||||
|
pub model_reasoning_effort: Option<ReasoningEffort>,
|
||||||
|
pub model_reasoning_summary: Option<ReasoningSummary>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_sandbox_permissions<'de, D>(
|
fn deserialize_sandbox_permissions<'de, D>(
|
||||||
@@ -444,6 +457,8 @@ impl Config {
|
|||||||
codex_linux_sandbox_exe,
|
codex_linux_sandbox_exe,
|
||||||
|
|
||||||
hide_agent_reasoning: cfg.hide_agent_reasoning.unwrap_or(false),
|
hide_agent_reasoning: cfg.hide_agent_reasoning.unwrap_or(false),
|
||||||
|
model_reasoning_effort: cfg.model_reasoning_effort.unwrap_or_default(),
|
||||||
|
model_reasoning_summary: cfg.model_reasoning_summary.unwrap_or_default(),
|
||||||
};
|
};
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
@@ -786,6 +801,8 @@ disable_response_storage = true
|
|||||||
tui: Tui::default(),
|
tui: Tui::default(),
|
||||||
codex_linux_sandbox_exe: None,
|
codex_linux_sandbox_exe: None,
|
||||||
hide_agent_reasoning: false,
|
hide_agent_reasoning: false,
|
||||||
|
model_reasoning_effort: ReasoningEffort::default(),
|
||||||
|
model_reasoning_summary: ReasoningSummary::default(),
|
||||||
},
|
},
|
||||||
o3_profile_config
|
o3_profile_config
|
||||||
);
|
);
|
||||||
@@ -826,6 +843,8 @@ disable_response_storage = true
|
|||||||
tui: Tui::default(),
|
tui: Tui::default(),
|
||||||
codex_linux_sandbox_exe: None,
|
codex_linux_sandbox_exe: None,
|
||||||
hide_agent_reasoning: false,
|
hide_agent_reasoning: false,
|
||||||
|
model_reasoning_effort: ReasoningEffort::default(),
|
||||||
|
model_reasoning_summary: ReasoningSummary::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected_gpt3_profile_config, gpt3_profile_config);
|
assert_eq!(expected_gpt3_profile_config, gpt3_profile_config);
|
||||||
@@ -881,6 +900,8 @@ disable_response_storage = true
|
|||||||
tui: Tui::default(),
|
tui: Tui::default(),
|
||||||
codex_linux_sandbox_exe: None,
|
codex_linux_sandbox_exe: None,
|
||||||
hide_agent_reasoning: false,
|
hide_agent_reasoning: false,
|
||||||
|
model_reasoning_effort: ReasoningEffort::default(),
|
||||||
|
model_reasoning_summary: ReasoningSummary::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected_zdr_profile_config, zdr_profile_config);
|
assert_eq!(expected_zdr_profile_config, zdr_profile_config);
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
// definitions that do not contain business logic.
|
// definitions that do not contain business logic.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use strum_macros::Display;
|
||||||
use wildmatch::WildMatchPattern;
|
use wildmatch::WildMatchPattern;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
||||||
pub struct McpServerConfig {
|
pub struct McpServerConfig {
|
||||||
@@ -175,3 +177,31 @@ impl From<ShellEnvironmentPolicyToml> for ShellEnvironmentPolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[strum(serialize_all = "lowercase")]
|
||||||
|
pub enum ReasoningEffort {
|
||||||
|
Low,
|
||||||
|
#[default]
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
/// Option to disable reasoning.
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A summary of the reasoning performed by the model. This can be useful for
|
||||||
|
/// debugging and understanding the model's reasoning process.
|
||||||
|
/// See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[strum(serialize_all = "lowercase")]
|
||||||
|
pub enum ReasoningSummary {
|
||||||
|
#[default]
|
||||||
|
Auto,
|
||||||
|
Concise,
|
||||||
|
Detailed,
|
||||||
|
/// Option to disable reasoning summaries.
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,3 +34,5 @@ mod rollout;
|
|||||||
mod safety;
|
mod safety;
|
||||||
mod user_notification;
|
mod user_notification;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
pub use client_common::model_supports_reasoning_summaries;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ use serde::Deserialize;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::config_types::ReasoningEffort as ReasoningEffortConfig;
|
||||||
|
use crate::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||||
use crate::message_history::HistoryEntry;
|
use crate::message_history::HistoryEntry;
|
||||||
use crate::model_provider_info::ModelProviderInfo;
|
use crate::model_provider_info::ModelProviderInfo;
|
||||||
|
|
||||||
@@ -37,6 +39,10 @@ pub enum Op {
|
|||||||
|
|
||||||
/// If not specified, server will use its default model.
|
/// If not specified, server will use its default model.
|
||||||
model: String,
|
model: String,
|
||||||
|
|
||||||
|
model_reasoning_effort: ReasoningEffortConfig,
|
||||||
|
model_reasoning_summary: ReasoningSummaryConfig,
|
||||||
|
|
||||||
/// Model instructions
|
/// Model instructions
|
||||||
instructions: Option<String>,
|
instructions: Option<String>,
|
||||||
/// When to escalate for approval for execution
|
/// When to escalate for approval for execution
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use codex_common::elapsed::format_elapsed;
|
use codex_common::elapsed::format_elapsed;
|
||||||
|
use codex_core::WireApi;
|
||||||
use codex_core::config::Config;
|
use codex_core::config::Config;
|
||||||
|
use codex_core::model_supports_reasoning_summaries;
|
||||||
use codex_core::protocol::AgentMessageEvent;
|
use codex_core::protocol::AgentMessageEvent;
|
||||||
use codex_core::protocol::BackgroundEventEvent;
|
use codex_core::protocol::BackgroundEventEvent;
|
||||||
use codex_core::protocol::ErrorEvent;
|
use codex_core::protocol::ErrorEvent;
|
||||||
@@ -127,16 +129,28 @@ impl EventProcessor {
|
|||||||
VERSION
|
VERSION
|
||||||
);
|
);
|
||||||
|
|
||||||
let entries = vec![
|
let mut entries = vec![
|
||||||
("workdir", config.cwd.display().to_string()),
|
("workdir", config.cwd.display().to_string()),
|
||||||
("model", config.model.clone()),
|
("model", config.model.clone()),
|
||||||
("provider", config.model_provider_id.clone()),
|
("provider", config.model_provider_id.clone()),
|
||||||
("approval", format!("{:?}", config.approval_policy)),
|
("approval", format!("{:?}", config.approval_policy)),
|
||||||
("sandbox", format!("{:?}", config.sandbox_policy)),
|
("sandbox", format!("{:?}", config.sandbox_policy)),
|
||||||
];
|
];
|
||||||
|
if config.model_provider.wire_api == WireApi::Responses
|
||||||
|
&& model_supports_reasoning_summaries(&config.model)
|
||||||
|
{
|
||||||
|
entries.push((
|
||||||
|
"reasoning effort",
|
||||||
|
config.model_reasoning_effort.to_string(),
|
||||||
|
));
|
||||||
|
entries.push((
|
||||||
|
"reasoning summaries",
|
||||||
|
config.model_reasoning_summary.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
for (key, value) in entries {
|
for (key, value) in entries {
|
||||||
println!("{} {}", format!("{key}: ").style(self.bold), value);
|
println!("{} {}", format!("{key}:").style(self.bold), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("--------");
|
println!("--------");
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ use crate::text_block::TextBlock;
|
|||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use codex_ansi_escape::ansi_escape_line;
|
use codex_ansi_escape::ansi_escape_line;
|
||||||
use codex_common::elapsed::format_duration;
|
use codex_common::elapsed::format_duration;
|
||||||
|
use codex_core::WireApi;
|
||||||
use codex_core::config::Config;
|
use codex_core::config::Config;
|
||||||
|
use codex_core::model_supports_reasoning_summaries;
|
||||||
use codex_core::protocol::FileChange;
|
use codex_core::protocol::FileChange;
|
||||||
use codex_core::protocol::SessionConfiguredEvent;
|
use codex_core::protocol::SessionConfiguredEvent;
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
@@ -147,13 +149,25 @@ impl HistoryCell {
|
|||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
|
|
||||||
let entries = vec![
|
let mut entries = vec![
|
||||||
("workdir", config.cwd.display().to_string()),
|
("workdir", config.cwd.display().to_string()),
|
||||||
("model", config.model.clone()),
|
("model", config.model.clone()),
|
||||||
("provider", config.model_provider_id.clone()),
|
("provider", config.model_provider_id.clone()),
|
||||||
("approval", format!("{:?}", config.approval_policy)),
|
("approval", format!("{:?}", config.approval_policy)),
|
||||||
("sandbox", format!("{:?}", config.sandbox_policy)),
|
("sandbox", format!("{:?}", config.sandbox_policy)),
|
||||||
];
|
];
|
||||||
|
if config.model_provider.wire_api == WireApi::Responses
|
||||||
|
&& model_supports_reasoning_summaries(&config.model)
|
||||||
|
{
|
||||||
|
entries.push((
|
||||||
|
"reasoning effort",
|
||||||
|
config.model_reasoning_effort.to_string(),
|
||||||
|
));
|
||||||
|
entries.push((
|
||||||
|
"reasoning summaries",
|
||||||
|
config.model_reasoning_summary.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
for (key, value) in entries {
|
for (key, value) in entries {
|
||||||
lines.push(Line::from(vec![format!("{key}: ").bold(), value.into()]));
|
lines.push(Line::from(vec![format!("{key}: ").bold(), value.into()]));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user