use codex_protocol::config_types::ReasoningSummary;use codex_protocol::openai_models::ConfigShellToolType;use codex_protocol::openai_models::ModelInfo;use codex_protocol::openai_models::ModelInstructionsVariables;use codex_protocol::openai_models::ModelMessages;use codex_protocol::openai_models::ModelVisibility;use codex_protocol::openai_models::TruncationMode;use codex_protocol::openai_models::TruncationPolicyConfig;use codex_protocol::openai_models::WebSearchToolType;use codex_protocol::openai_models::default_input_modalities;use crate::config::ModelsManagerConfig;use codex_utils_output_truncation::approx_bytes_for_tokens;use tracing::warn;pub const BASE_INSTRUCTIONS: &str = include_str!("../prompt.md");const DEFAULT_PERSONALITY_HEADER: &str = "You are Codex, a coding agent based on GPT-5. You and the user share the same workspace and collaborate to achieve the user's goals.";const LOCAL_FRIENDLY_TEMPLATE: &str = "You optimize for team morale and being a supportive teammate as much as code quality.";const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective software engineer.";const PERSONALITY_PLACEHOLDER: &str = "{{ personality }}";pub fn with_config_overrides(mut model: ModelInfo, config: &ModelsManagerConfig) -> ModelInfo { if let Some(supports_reasoning_summaries) = config.model_supports_reasoning_summaries && supports_reasoning_summaries { model.supports_reasoning_summaries = true; } if let Some(context_window) = config.model_context_window { model.context_window = Some( model .max_context_window .map_or(context_window, |max_context_window| { context_window.min(max_context_window) }), ); } if let Some(auto_compact_token_limit) = config.model_auto_compact_token_limit { model.auto_compact_token_limit = Some(auto_compact_token_limit); } if let Some(token_limit) = config.tool_output_token_limit { model.truncation_policy = match model.truncation_policy.mode { TruncationMode::Bytes => { let byte_limit = i64::try_from(approx_bytes_for_tokens(token_limit)).unwrap_or(i64::MAX); TruncationPolicyConfig::bytes(byte_limit) } TruncationMode::Tokens => { let limit = i64::try_from(token_limit).unwrap_or(i64::MAX); TruncationPolicyConfig::tokens(limit) } }; } if let Some(base_instructions) = &config.base_instructions { model.base_instructions = base_instructions.clone(); model.model_messages = None; } else if !config.personality_enabled { model.model_messages = None; } model}/// Build a minimal fallback model descriptor for missing/unknown slugs.pub fn model_info_from_slug(slug: &str) -> ModelInfo { warn!("Unknown model {slug} is used. This will use fallback model metadata."); ModelInfo { slug: slug.to_string(), display_name: slug.to_string(), description: None, default_reasoning_level: None, supported_reasoning_levels: Vec::new(), shell_type: ConfigShellToolType::Default, visibility: ModelVisibility::None, supported_in_api: true, priority: 99, additional_speed_tiers: Vec::new(), service_tiers: Vec::new(), default_service_tier: None, availability_nux: None, upgrade: None, base_instructions: BASE_INSTRUCTIONS.to_string(), model_messages: local_personality_messages_for_slug(slug), supports_reasoning_summaries: false, default_reasoning_summary: ReasoningSummary::Auto, support_verbosity: false, default_verbosity: None, apply_patch_tool_type: None, web_search_tool_type: WebSearchToolType::Text, truncation_policy: TruncationPolicyConfig::bytes(/*limit*/ 10_000), supports_parallel_tool_calls: false, supports_image_detail_original: false, context_window: Some(272_000), max_context_window: Some(272_000), auto_compact_token_limit: None, comp_hash: None, effective_context_window_percent: 95, experimental_supported_tools: Vec::new(), input_modalities: default_input_modalities(), used_fallback_model_metadata: true, // this is the fallback model metadata supports_search_tool: false, use_responses_lite: false, auto_review_model_override: None, tool_mode: None, multi_agent_version: None, }}fn local_personality_messages_for_slug(slug: &str) -> Option<ModelMessages> { match slug { "gpt-5.2-codex" | "exp-codex-personality" => Some(ModelMessages { instructions_template: Some(format!( "{DEFAULT_PERSONALITY_HEADER}\n\n{PERSONALITY_PLACEHOLDER}\n\n{BASE_INSTRUCTIONS}" )), instructions_variables: Some(ModelInstructionsVariables { personality_default: Some(String::new()), personality_friendly: Some(LOCAL_FRIENDLY_TEMPLATE.to_string()), personality_pragmatic: Some(LOCAL_PRAGMATIC_TEMPLATE.to_string()), }), }), _ => None, }}#[cfg(test)]#[path = "model_info_tests.rs"]mod tests;