mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Introduce ModelsManager and migrate app-server to use it. (#7552)
This commit is contained in:
committed by
GitHub
Unverified
parent
70b97790be
commit
00cc00ead8
@@ -1862,8 +1862,7 @@ impl CodexMessageProcessor {
|
||||
|
||||
async fn list_models(&self, request_id: RequestId, params: ModelListParams) {
|
||||
let ModelListParams { limit, cursor } = params;
|
||||
let auth_mode = self.auth_manager.auth().map(|auth| auth.mode);
|
||||
let models = supported_models(auth_mode);
|
||||
let models = supported_models(self.conversation_manager.clone()).await;
|
||||
let total = models.len();
|
||||
|
||||
if total == 0 {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_app_server_protocol::Model;
|
||||
use codex_app_server_protocol::ReasoningEffortOption;
|
||||
use codex_core::openai_models::model_presets::builtin_model_presets;
|
||||
use codex_core::ConversationManager;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ReasoningEffortPreset;
|
||||
|
||||
pub fn supported_models(auth_mode: Option<AuthMode>) -> Vec<Model> {
|
||||
builtin_model_presets(auth_mode)
|
||||
pub async fn supported_models(conversation_manager: Arc<ConversationManager>) -> Vec<Model> {
|
||||
conversation_manager
|
||||
.list_models()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(model_from_preset)
|
||||
.collect()
|
||||
|
||||
@@ -1201,4 +1201,8 @@ impl AuthManager {
|
||||
self.reload();
|
||||
Ok(removed)
|
||||
}
|
||||
|
||||
pub fn get_auth_mode(&self) -> Option<AuthMode> {
|
||||
self.auth().map(|a| a.mode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ use crate::user_notification::UserNotifier;
|
||||
use crate::util::error_or_panic;
|
||||
use async_channel::Receiver;
|
||||
use async_channel::Sender;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_protocol::ConversationId;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
@@ -643,14 +642,6 @@ impl Session {
|
||||
Ok(sess)
|
||||
}
|
||||
|
||||
pub(crate) fn get_auth_mode(&self) -> AuthMode {
|
||||
self.services
|
||||
.auth_manager
|
||||
.auth()
|
||||
.map(|a| a.mode)
|
||||
.unwrap_or(AuthMode::ApiKey)
|
||||
}
|
||||
|
||||
pub(crate) fn get_tx_event(&self) -> Sender<Event> {
|
||||
self.tx_event.clone()
|
||||
}
|
||||
@@ -1486,9 +1477,6 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
|
||||
Op::Review { review_request } => {
|
||||
handlers::review(&sess, &config, sub.id.clone(), review_request).await;
|
||||
}
|
||||
Op::ListModels => {
|
||||
handlers::list_models(&sess, sub.id.clone(), Some(sess.get_auth_mode())).await;
|
||||
}
|
||||
_ => {} // Ignore unknown ops; enum is non_exhaustive to allow extensions.
|
||||
}
|
||||
}
|
||||
@@ -1505,15 +1493,12 @@ mod handlers {
|
||||
use crate::config::Config;
|
||||
use crate::mcp::auth::compute_auth_statuses;
|
||||
use crate::mcp::collect_mcp_snapshot_from_manager;
|
||||
use crate::openai_models::model_presets::builtin_model_presets;
|
||||
use crate::review_prompts::resolve_review_request;
|
||||
use crate::tasks::CompactTask;
|
||||
use crate::tasks::RegularTask;
|
||||
use crate::tasks::UndoTask;
|
||||
use crate::tasks::UserShellCommandTask;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_protocol::custom_prompts::CustomPrompt;
|
||||
use codex_protocol::openai_models::AvailableModelsEvent;
|
||||
use codex_protocol::protocol::CodexErrorInfo;
|
||||
use codex_protocol::protocol::ErrorEvent;
|
||||
use codex_protocol::protocol::Event;
|
||||
@@ -1828,15 +1813,6 @@ mod handlers {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_models(sess: &Arc<Session>, sub_id: String, auth_mode: Option<AuthMode>) {
|
||||
let models = builtin_model_presets(auth_mode);
|
||||
let event = Event {
|
||||
id: sub_id,
|
||||
msg: EventMsg::ListModelsResponse(AvailableModelsEvent { models }),
|
||||
};
|
||||
sess.send_event_raw(event).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a review thread using the given prompt.
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::codex_conversation::CodexConversation;
|
||||
use crate::config::Config;
|
||||
use crate::error::CodexErr;
|
||||
use crate::error::Result as CodexResult;
|
||||
use crate::openai_models::models_manager::ModelsManager;
|
||||
use crate::protocol::Event;
|
||||
use crate::protocol::EventMsg;
|
||||
use crate::protocol::SessionConfiguredEvent;
|
||||
@@ -14,6 +15,7 @@ use crate::rollout::RolloutRecorder;
|
||||
use codex_protocol::ConversationId;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::protocol::InitialHistory;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
@@ -35,6 +37,7 @@ pub struct NewConversation {
|
||||
pub struct ConversationManager {
|
||||
conversations: Arc<RwLock<HashMap<ConversationId, Arc<CodexConversation>>>>,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
models_manager: Arc<ModelsManager>,
|
||||
session_source: SessionSource,
|
||||
}
|
||||
|
||||
@@ -42,8 +45,9 @@ impl ConversationManager {
|
||||
pub fn new(auth_manager: Arc<AuthManager>, session_source: SessionSource) -> Self {
|
||||
Self {
|
||||
conversations: Arc::new(RwLock::new(HashMap::new())),
|
||||
auth_manager,
|
||||
auth_manager: auth_manager.clone(),
|
||||
session_source,
|
||||
models_manager: Arc::new(ModelsManager::new(auth_manager.get_auth_mode())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,6 +197,10 @@ impl ConversationManager {
|
||||
|
||||
self.finalize_spawn(codex, conversation_id).await
|
||||
}
|
||||
|
||||
pub async fn list_models(&self) -> Vec<ModelPreset> {
|
||||
self.models_manager.available_models.read().await.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a prefix of `items` obtained by cutting strictly before the nth user message
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod model_presets;
|
||||
pub mod models_manager;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::openai_models::model_presets::builtin_model_presets;
|
||||
|
||||
pub struct ModelsManager {
|
||||
pub available_models: RwLock<Vec<ModelPreset>>,
|
||||
pub etag: String,
|
||||
pub auth_mode: Option<AuthMode>,
|
||||
}
|
||||
|
||||
impl ModelsManager {
|
||||
pub fn new(auth_mode: Option<AuthMode>) -> Self {
|
||||
Self {
|
||||
available_models: RwLock::new(builtin_model_presets(auth_mode)),
|
||||
etag: String::new(),
|
||||
auth_mode,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn refresh_available_models(&self) {
|
||||
let models = builtin_model_presets(self.auth_mode);
|
||||
*self.available_models.write().await = models;
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,6 @@ pub(crate) fn should_persist_event_msg(ev: &EventMsg) -> bool {
|
||||
| EventMsg::Warning(_)
|
||||
| EventMsg::TaskStarted(_)
|
||||
| EventMsg::TaskComplete(_)
|
||||
| EventMsg::ListModelsResponse(_)
|
||||
| EventMsg::AgentMessageDelta(_)
|
||||
| EventMsg::AgentReasoningDelta(_)
|
||||
| EventMsg::AgentReasoningRawContentDelta(_)
|
||||
|
||||
@@ -1,51 +1,30 @@
|
||||
use anyhow::Result;
|
||||
use codex_core::CodexAuth;
|
||||
use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::Op;
|
||||
use codex_core::ConversationManager;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::openai_models::ReasoningEffortPreset;
|
||||
use core_test_support::responses::start_mock_server;
|
||||
use core_test_support::test_codex::test_codex;
|
||||
use core_test_support::wait_for_event_match;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn list_models_returns_api_key_models() -> Result<()> {
|
||||
let server = start_mock_server().await;
|
||||
let mut builder = test_codex().with_auth(CodexAuth::from_api_key("sk-test"));
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
test.codex.submit(Op::ListModels).await?;
|
||||
|
||||
let event = wait_for_event_match(&test.codex, |event| match event {
|
||||
EventMsg::ListModelsResponse(models) => Some(models.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.await;
|
||||
let manager = ConversationManager::with_auth(CodexAuth::from_api_key("sk-test"));
|
||||
let models = manager.list_models().await;
|
||||
|
||||
let expected_models = expected_models_for_api_key();
|
||||
assert_eq!(expected_models, event.models);
|
||||
assert_eq!(expected_models, models);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn list_models_returns_chatgpt_models() -> Result<()> {
|
||||
let server = start_mock_server().await;
|
||||
let mut builder = test_codex().with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
test.codex.submit(Op::ListModels).await?;
|
||||
|
||||
let event = wait_for_event_match(&test.codex, |event| match event {
|
||||
EventMsg::ListModelsResponse(models) => Some(models.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.await;
|
||||
let manager =
|
||||
ConversationManager::with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
let models = manager.list_models().await;
|
||||
|
||||
let expected_models = expected_models_for_chatgpt();
|
||||
assert_eq!(expected_models, event.models);
|
||||
assert_eq!(expected_models, models);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -583,7 +583,6 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
| EventMsg::ReasoningContentDelta(_)
|
||||
| EventMsg::ReasoningRawContentDelta(_)
|
||||
| EventMsg::UndoCompleted(_)
|
||||
| EventMsg::ListModelsResponse(_)
|
||||
| EventMsg::UndoStarted(_) => {}
|
||||
}
|
||||
CodexStatus::Running
|
||||
|
||||
@@ -307,7 +307,6 @@ async fn run_codex_tool_session_inner(
|
||||
| EventMsg::UndoCompleted(_)
|
||||
| EventMsg::ExitedReviewMode(_)
|
||||
| EventMsg::ContextCompacted(_)
|
||||
| EventMsg::ListModelsResponse(_)
|
||||
| EventMsg::DeprecationNotice(_) => {
|
||||
// For now, we do not do anything extra for these
|
||||
// events. Note that
|
||||
|
||||
@@ -35,11 +35,6 @@ pub enum ReasoningEffort {
|
||||
XHigh,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)]
|
||||
pub struct AvailableModelsEvent {
|
||||
pub models: Vec<ModelPreset>,
|
||||
}
|
||||
|
||||
/// A reasoning effort option that can be surfaced for a model.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq)]
|
||||
pub struct ReasoningEffortPreset {
|
||||
|
||||
@@ -19,7 +19,6 @@ use crate::message_history::HistoryEntry;
|
||||
use crate::models::ContentItem;
|
||||
use crate::models::ResponseItem;
|
||||
use crate::num_format::format_with_separators;
|
||||
use crate::openai_models::AvailableModelsEvent;
|
||||
use crate::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use crate::parse_command::ParsedCommand;
|
||||
use crate::plan_tool::UpdatePlanArgs;
|
||||
@@ -582,7 +581,6 @@ pub enum EventMsg {
|
||||
AgentMessageContentDelta(AgentMessageContentDeltaEvent),
|
||||
ReasoningContentDelta(ReasoningContentDeltaEvent),
|
||||
ReasoningRawContentDelta(ReasoningRawContentDeltaEvent),
|
||||
ListModelsResponse(AvailableModelsEvent),
|
||||
}
|
||||
|
||||
/// Codex errors that we expose to clients.
|
||||
|
||||
@@ -1842,7 +1842,6 @@ impl ChatWidget {
|
||||
| EventMsg::ItemCompleted(_)
|
||||
| EventMsg::AgentMessageContentDelta(_)
|
||||
| EventMsg::ReasoningContentDelta(_)
|
||||
| EventMsg::ListModelsResponse(_)
|
||||
| EventMsg::ReasoningRawContentDelta(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user