mirror of
https://github.com/openai/codex.git
synced 2026-11-15 21:50:46 +00:00
async
This commit is contained in:
@@ -631,6 +631,36 @@ impl ConfigEditsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_pending_model_migration_notice(mut self, from_model: &str, to_model: &str) -> Self {
|
||||
self.edits.push(ConfigEdit::SetPath {
|
||||
segments: vec![
|
||||
Notice::TABLE_KEY.to_string(),
|
||||
"pending_model_migration".to_string(),
|
||||
"from_model".to_string(),
|
||||
],
|
||||
value: value(from_model.to_string()),
|
||||
});
|
||||
self.edits.push(ConfigEdit::SetPath {
|
||||
segments: vec![
|
||||
Notice::TABLE_KEY.to_string(),
|
||||
"pending_model_migration".to_string(),
|
||||
"to_model".to_string(),
|
||||
],
|
||||
value: value(to_model.to_string()),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear_pending_model_migration_notice(mut self) -> Self {
|
||||
self.edits.push(ConfigEdit::ClearPath {
|
||||
segments: vec![
|
||||
Notice::TABLE_KEY.to_string(),
|
||||
"pending_model_migration".to_string(),
|
||||
],
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_windows_wsl_setup_acknowledged(mut self, acknowledged: bool) -> Self {
|
||||
self.edits
|
||||
.push(ConfigEdit::SetWindowsWslSetupAcknowledged(acknowledged));
|
||||
|
||||
@@ -514,6 +514,12 @@ const fn default_true() -> bool {
|
||||
/// Settings for notices we display to users via the tui and app-server clients
|
||||
/// (primarily the Codex IDE extension). NOTE: these are different from
|
||||
/// notifications - notices are warnings, NUX screens, acknowledgements, etc.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PendingModelMigrationNotice {
|
||||
pub from_model: String,
|
||||
pub to_model: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
|
||||
pub struct Notice {
|
||||
/// Tracks whether the user has acknowledged the full access warning prompt.
|
||||
@@ -530,6 +536,8 @@ pub struct Notice {
|
||||
/// Tracks acknowledged model migrations as old->new model slug mappings.
|
||||
#[serde(default)]
|
||||
pub model_migrations: BTreeMap<String, String>,
|
||||
/// Persisted "show on next run" model migration notice.
|
||||
pub pending_model_migration: Option<PendingModelMigrationNotice>,
|
||||
}
|
||||
|
||||
impl Notice {
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::sync::TryLockError;
|
||||
use tracing::error;
|
||||
@@ -29,6 +30,7 @@ use crate::models_manager::model_presets::builtin_model_presets;
|
||||
|
||||
const MODEL_CACHE_FILE: &str = "models_cache.json";
|
||||
const DEFAULT_MODEL_CACHE_TTL: Duration = Duration::from_secs(300);
|
||||
const MODELS_REFRESH_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const OPENAI_DEFAULT_API_MODEL: &str = "gpt-5.1-codex-max";
|
||||
const OPENAI_DEFAULT_CHATGPT_MODEL: &str = "gpt-5.2-codex";
|
||||
const CODEX_AUTO_BALANCED_MODEL: &str = "codex-auto-balanced";
|
||||
@@ -39,6 +41,7 @@ pub struct ModelsManager {
|
||||
// todo(aibrahim) merge available_models and model family creation into one struct
|
||||
local_models: Vec<ModelPreset>,
|
||||
remote_models: RwLock<Vec<ModelInfo>>,
|
||||
refresh_lock: Mutex<()>,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
etag: RwLock<Option<String>>,
|
||||
codex_home: PathBuf,
|
||||
@@ -53,6 +56,7 @@ impl ModelsManager {
|
||||
Self {
|
||||
local_models: builtin_model_presets(auth_manager.get_auth_mode()),
|
||||
remote_models: RwLock::new(Self::load_remote_models_from_file().unwrap_or_default()),
|
||||
refresh_lock: Mutex::new(()),
|
||||
auth_manager,
|
||||
etag: RwLock::new(None),
|
||||
codex_home,
|
||||
@@ -68,6 +72,7 @@ impl ModelsManager {
|
||||
Self {
|
||||
local_models: builtin_model_presets(auth_manager.get_auth_mode()),
|
||||
remote_models: RwLock::new(Self::load_remote_models_from_file().unwrap_or_default()),
|
||||
refresh_lock: Mutex::new(()),
|
||||
auth_manager,
|
||||
etag: RwLock::new(None),
|
||||
codex_home,
|
||||
@@ -83,6 +88,11 @@ impl ModelsManager {
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Prevent duplicate `/models` refreshes when multiple callers try to refresh
|
||||
// concurrently during startup (or when multiple features request models).
|
||||
let _guard = self.refresh_lock.lock().await;
|
||||
|
||||
if self.try_load_cache().await {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -94,10 +104,23 @@ impl ModelsManager {
|
||||
let client = ModelsClient::new(transport, api_provider, api_auth);
|
||||
|
||||
let client_version = format_client_version_to_whole();
|
||||
let ModelsResponse { models, etag } = client
|
||||
.list_models(&client_version, HeaderMap::new())
|
||||
.await
|
||||
.map_err(map_api_error)?;
|
||||
let response = tokio::time::timeout(
|
||||
MODELS_REFRESH_TIMEOUT,
|
||||
client.list_models(&client_version, HeaderMap::new()),
|
||||
)
|
||||
.await;
|
||||
|
||||
let ModelsResponse { models, etag } = match response {
|
||||
Ok(response) => response.map_err(map_api_error)?,
|
||||
Err(_) => {
|
||||
error!(
|
||||
"timed out refreshing /models after {}s",
|
||||
MODELS_REFRESH_TIMEOUT.as_secs()
|
||||
);
|
||||
// Leave `remote_models` unchanged so the preloaded fallback remains available.
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let etag = (!etag.is_empty()).then_some(etag);
|
||||
|
||||
|
||||
@@ -84,6 +84,33 @@ pub struct ModelFamily {
|
||||
}
|
||||
|
||||
impl ModelFamily {
|
||||
/// Placeholder model family for UI startup before Codex has selected the real model.
|
||||
///
|
||||
/// This must not be treated as an actual model slug; it only exists to allow
|
||||
/// consumers (e.g. the TUI) to render while the session is still starting.
|
||||
pub fn placeholder(config: &Config) -> Self {
|
||||
let mf = Self {
|
||||
slug: String::new(),
|
||||
family: "unknown".to_string(),
|
||||
needs_special_apply_patch_instructions: false,
|
||||
context_window: None,
|
||||
auto_compact_token_limit: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_effort: None,
|
||||
reasoning_summary_format: ReasoningSummaryFormat::None,
|
||||
supports_parallel_tool_calls: false,
|
||||
apply_patch_tool_type: None,
|
||||
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
||||
experimental_supported_tools: Vec::new(),
|
||||
effective_context_window_percent: 95,
|
||||
support_verbosity: false,
|
||||
default_verbosity: None,
|
||||
shell_type: ConfigShellToolType::Default,
|
||||
truncation_policy: TruncationPolicy::Bytes(10_000),
|
||||
};
|
||||
mf.with_config_overrides(config)
|
||||
}
|
||||
|
||||
pub(super) fn with_config_overrides(mut self, config: &Config) -> Self {
|
||||
if let Some(supports_reasoning_summaries) = config.model_supports_reasoning_summaries {
|
||||
self.supports_reasoning_summaries = supports_reasoning_summaries;
|
||||
|
||||
Reference in New Issue
Block a user