Compare commits

...

1 Commits

Author SHA1 Message Date
jif-oai
1e7711a8ed Prefer gpt-5.3 explorer model 2026-02-16 08:52:56 +00:00
2 changed files with 73 additions and 11 deletions

View File

@@ -1,14 +1,16 @@
use crate::config::Config;
use crate::protocol::SandboxPolicy;
use codex_protocol::openai_models::ModelPreset;
use codex_protocol::openai_models::ReasoningEffort;
use serde::Deserialize;
use serde::Serialize;
/// Base instructions for the orchestrator role.
const ORCHESTRATOR_PROMPT: &str = include_str!("../../templates/agents/orchestrator.md");
/// Default model override used.
// TODO(jif) update when we have something smarter.
const EXPLORER_MODEL: &str = "gpt-5.1-codex-mini";
/// Preferred explorer model when available in the user's model list.
const EXPLORER_PREFERRED_MODEL: &str = "gpt-5.3-codex-spark";
/// Fallback explorer model override.
const EXPLORER_FALLBACK_MODEL: &str = "gpt-5.1-codex-mini";
/// Enumerated list of all supported agent roles.
const ALL_ROLES: [AgentRole; 3] = [
@@ -54,7 +56,7 @@ impl AgentRole {
ALL_ROLES
.iter()
.filter_map(|role| {
let description = role.profile().description;
let description = role.profile(&[]).description;
serde_json::to_string(role)
.map(|role| {
let description = if !description.is_empty() {
@@ -69,8 +71,8 @@ impl AgentRole {
.collect()
}
/// Returns the hard-coded profile for this role.
pub fn profile(self) -> AgentProfile {
/// Returns the role profile using the provided available-model list.
pub fn profile(self, available_models: &[ModelPreset]) -> AgentProfile {
match self {
AgentRole::Default => AgentProfile::default(),
AgentRole::Orchestrator => AgentProfile {
@@ -91,7 +93,16 @@ Rules:
..Default::default()
},
AgentRole::Explorer => AgentProfile {
model: Some(EXPLORER_MODEL),
model: Some(
if available_models
.iter()
.any(|model| model.model == EXPLORER_PREFERRED_MODEL)
{
EXPLORER_PREFERRED_MODEL
} else {
EXPLORER_FALLBACK_MODEL
},
),
reasoning_effort: Some(ReasoningEffort::Medium),
description: r#"Use `explorer` for all codebase questions.
Explorers are fast and authoritative.
@@ -108,9 +119,13 @@ Rules:
}
}
/// Applies this role's profile onto the provided config.
pub fn apply_to_config(self, config: &mut Config) -> Result<(), String> {
let profile = self.profile();
/// Applies this role's profile onto the provided config using available-model context.
pub fn apply_to_config(
self,
config: &mut Config,
available_models: &[ModelPreset],
) -> Result<(), String> {
let profile = self.profile(available_models);
if let Some(base_instructions) = profile.base_instructions {
config.base_instructions = Some(base_instructions.to_string());
}
@@ -130,3 +145,43 @@ Rules:
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use codex_protocol::openai_models::ReasoningEffortPreset;
use codex_protocol::openai_models::default_input_modalities;
use pretty_assertions::assert_eq;
fn model_preset(model: &str) -> ModelPreset {
ModelPreset {
id: model.to_string(),
model: model.to_string(),
display_name: model.to_string(),
description: String::new(),
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: vec![ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: String::new(),
}],
supports_personality: false,
is_default: false,
upgrade: None,
show_in_picker: true,
supported_in_api: true,
input_modalities: default_input_modalities(),
}
}
#[test]
fn explorer_uses_preferred_model_when_available() {
let profile = AgentRole::Explorer.profile(&[model_preset("gpt-5.3-codex-spark")]);
assert_eq!(profile.model, Some("gpt-5.3-codex-spark"));
}
#[test]
fn explorer_uses_fallback_model_when_preferred_unavailable() {
let profile = AgentRole::Explorer.profile(&[model_preset("gpt-5.1-codex-mini")]);
assert_eq!(profile.model, Some("gpt-5.1-codex-mini"));
}
}

View File

@@ -7,6 +7,7 @@ use crate::config::Constrained;
use crate::error::CodexErr;
use crate::features::Feature;
use crate::function_tool::FunctionCallError;
use crate::models_manager::manager::RefreshStrategy;
use crate::tools::context::ToolInvocation;
use crate::tools::context::ToolOutput;
use crate::tools::context::ToolPayload;
@@ -141,8 +142,14 @@ mod spawn {
turn.as_ref(),
child_depth,
)?;
// Offline refresh strategy is cache-only and does not hit the network.
let available_models = session
.services
.models_manager
.list_models(&config, RefreshStrategy::Offline)
.await;
agent_role
.apply_to_config(&mut config)
.apply_to_config(&mut config, &available_models)
.map_err(FunctionCallError::RespondToModel)?;
let result = session