From bae53415851cf58f6b16f45dfbc4abf90a299a5a Mon Sep 17 00:00:00 2001 From: jif-oai Date: Thu, 30 Oct 2025 16:24:41 +0000 Subject: [PATCH] V6 --- codex-rs/Cargo.lock | 5 + codex-rs/api-client/src/lib.rs | 1 + codex-rs/api-client/src/stream.rs | 17 +- codex-rs/common/Cargo.toml | 1 + codex-rs/common/src/config_summary.rs | 2 +- codex-rs/core/src/client.rs | 164 ++++++++++++------ codex-rs/core/src/client_common.rs | 19 +- codex-rs/core/src/codex.rs | 96 +++++----- codex-rs/core/src/config/mod.rs | 6 +- codex-rs/core/src/lib.rs | 11 +- codex-rs/core/src/sandboxing/assessment.rs | 2 +- .../core/tests/chat_completions_payload.rs | 4 +- codex-rs/core/tests/chat_completions_sse.rs | 4 +- codex-rs/core/tests/common/Cargo.toml | 1 + codex-rs/core/tests/common/test_codex.rs | 4 +- codex-rs/core/tests/responses_headers.rs | 4 +- codex-rs/core/tests/suite/client.rs | 6 +- codex-rs/core/tests/suite/compact.rs | 4 +- .../core/tests/suite/compact_resume_fork.rs | 4 +- .../core/tests/suite/fork_conversation.rs | 4 +- codex-rs/core/tests/suite/model_tools.rs | 4 +- codex-rs/core/tests/suite/prompt_caching.rs | 4 +- codex-rs/core/tests/suite/review.rs | 4 +- codex-rs/core/tests/suite/rmcp_client.rs | 2 +- .../suite/stream_error_allows_next_turn.rs | 4 +- .../core/tests/suite/stream_no_completed.rs | 4 +- codex-rs/exec/Cargo.toml | 1 + codex-rs/exec/src/lib.rs | 2 +- codex-rs/ollama/Cargo.toml | 1 + codex-rs/ollama/src/client.rs | 8 +- codex-rs/tui/Cargo.toml | 1 + codex-rs/tui/src/lib.rs | 2 +- 32 files changed, 236 insertions(+), 160 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index baa98b401f..da7ca5223f 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1063,6 +1063,7 @@ name = "codex-common" version = "0.0.0" dependencies = [ "clap", + "codex-api-client", "codex-app-server-protocol", "codex-core", "codex-protocol", @@ -1156,6 +1157,7 @@ dependencies = [ "anyhow", "assert_cmd", "clap", + "codex-api-client", "codex-arg0", "codex-common", "codex-core", @@ -1321,6 +1323,7 @@ dependencies = [ "assert_matches", "async-stream", "bytes", + "codex-api-client", "codex-core", "futures", "reqwest", @@ -1462,6 +1465,7 @@ dependencies = [ "chrono", "clap", "codex-ansi-escape", + "codex-api-client", "codex-app-server-protocol", "codex-arg0", "codex-common", @@ -1695,6 +1699,7 @@ version = "0.0.0" dependencies = [ "anyhow", "assert_cmd", + "codex-api-client", "codex-core", "codex-protocol", "notify", diff --git a/codex-rs/api-client/src/lib.rs b/codex-rs/api-client/src/lib.rs index 1b135bee00..f57ac46c8f 100644 --- a/codex-rs/api-client/src/lib.rs +++ b/codex-rs/api-client/src/lib.rs @@ -26,6 +26,7 @@ pub use crate::prompt::Prompt; pub use crate::responses::ResponsesApiClient; pub use crate::responses::ResponsesApiClientConfig; pub use crate::responses::stream_from_fixture; +pub use crate::stream::EventStream; pub use crate::stream::Reasoning; pub use crate::stream::ResponseEvent; pub use crate::stream::ResponseStream; diff --git a/codex-rs/api-client/src/stream.rs b/codex-rs/api-client/src/stream.rs index 6054aa62f5..ac76b282fa 100644 --- a/codex-rs/api-client/src/stream.rs +++ b/codex-rs/api-client/src/stream.rs @@ -61,14 +61,23 @@ pub enum ResponseEvent { RateLimits(RateLimitSnapshot), } -pub struct ResponseStream { - pub(crate) rx_event: mpsc::Receiver>, +#[derive(Debug)] +pub struct EventStream { + pub(crate) rx_event: mpsc::Receiver, } -impl Stream for ResponseStream { - type Item = Result; +impl EventStream { + pub fn from_receiver(rx_event: mpsc::Receiver) -> Self { + Self { rx_event } + } +} + +impl Stream for EventStream { + type Item = T; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.rx_event.poll_recv(cx) } } + +pub type ResponseStream = EventStream>; diff --git a/codex-rs/common/Cargo.toml b/codex-rs/common/Cargo.toml index d8f30cc09d..a836e5d6cb 100644 --- a/codex-rs/common/Cargo.toml +++ b/codex-rs/common/Cargo.toml @@ -8,6 +8,7 @@ workspace = true [dependencies] clap = { workspace = true, features = ["derive", "wrap_help"], optional = true } +codex-api-client = { workspace = true } codex-core = { workspace = true } codex-protocol = { workspace = true } codex-app-server-protocol = { workspace = true } diff --git a/codex-rs/common/src/config_summary.rs b/codex-rs/common/src/config_summary.rs index dabc606ce1..b3019e3ea9 100644 --- a/codex-rs/common/src/config_summary.rs +++ b/codex-rs/common/src/config_summary.rs @@ -1,4 +1,4 @@ -use codex_core::WireApi; +use codex_api_client::WireApi; use codex_core::config::Config; use crate::sandbox_summary::summarize_sandbox_policy; diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 1675b9eee8..d3318153b2 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::sync::Arc; use async_trait::async_trait; @@ -17,20 +18,19 @@ use codex_protocol::ConversationId; use codex_protocol::config_types::ReasoningEffort as ReasoningEffortConfig; use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig; use codex_protocol::protocol::SessionSource; -use futures::Stream; +use futures::StreamExt; +use futures::stream::BoxStream; +use tokio::sync::OnceCell; use tokio::sync::mpsc; use tracing::warn; use crate::AuthManager; -use crate::ModelProviderInfo; -use crate::WireApi; use crate::client_common::Prompt; use crate::client_common::ResponseEvent; use crate::client_common::ResponseStream; use crate::client_common::create_reasoning_param_for_request; use crate::client_common::create_text_param_for_request; use crate::config::Config; -use crate::default_client::CodexHttpClient; use crate::default_client::create_client; use crate::error::CodexErr; use crate::error::ConnectionFailedError; @@ -43,25 +43,85 @@ use crate::features::Feature; use crate::flags::CODEX_RS_SSE_FIXTURE; use crate::model_family::ModelFamily; use crate::openai_model_info::get_model_info; +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ModelClient { config: Arc, auth_manager: Option>, otel_event_manager: OtelEventManager, - client: CodexHttpClient, + http_client: reqwest::Client, provider: ModelProviderInfo, + backend: Arc>, conversation_id: ConversationId, effort: Option, summary: ReasoningSummaryConfig, session_source: SessionSource, } +impl fmt::Debug for ModelClient { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ModelClient") + .field("provider", &self.provider.name) + .field("model", &self.config.model) + .field("conversation_id", &self.conversation_id) + .field("backend_initialized", &self.backend.get().is_some()) + .finish() + } +} + #[derive(Debug, Clone)] pub struct StreamPayload { pub prompt: Prompt, } +type ApiClientStream = BoxStream<'static, ApiClientResult>; + +enum ModelBackend { + Responses(ResponsesBackend), + Chat(ChatBackend), +} + +impl ModelBackend { + async fn stream(&self, prompt: Prompt) -> ApiClientResult { + match self { + ModelBackend::Responses(backend) => backend.stream(prompt).await, + ModelBackend::Chat(backend) => backend.stream(prompt).await, + } + } +} + +struct ResponsesBackend { + client: ResponsesApiClient, +} + +impl ResponsesBackend { + async fn stream(&self, prompt: Prompt) -> ApiClientResult { + self.client + .stream(prompt) + .await + .map(|stream| stream.boxed()) + } +} + +struct ChatBackend { + client: ChatCompletionsApiClient, + show_reasoning: bool, +} + +impl ChatBackend { + async fn stream(&self, prompt: Prompt) -> ApiClientResult { + let stream = self.client.stream(prompt).await?; + let stream = if self.show_reasoning { + stream.streaming_mode().boxed() + } else { + stream.aggregate().boxed() + }; + Ok(stream) + } +} + #[allow(clippy::too_many_arguments)] impl ModelClient { pub fn new( @@ -74,14 +134,16 @@ impl ModelClient { conversation_id: ConversationId, session_source: SessionSource, ) -> Self { - let client = create_client(); + let http_client = create_client().clone_inner(); + let backend = Arc::new(OnceCell::new()); Self { config, auth_manager, otel_event_manager, - client, + http_client, provider, + backend, conversation_id, effort, summary, @@ -119,11 +181,30 @@ impl ModelClient { pub async fn stream(&self, payload: &StreamPayload) -> Result { let mut prompt = payload.prompt.clone(); self.populate_prompt(&mut prompt); - - match self.provider.wire_api { - WireApi::Responses => self.stream_via_responses(prompt).await, - WireApi::Chat => self.stream_via_chat(prompt).await, + if self.provider.wire_api == WireApi::Responses { + if let Some(path) = &*CODEX_RS_SSE_FIXTURE { + warn!(path, "Streaming from fixture"); + let stream = stream_from_fixture( + path, + self.provider.clone(), + self.otel_event_manager.clone(), + ) + .await + .map_err(map_api_error)? + .boxed(); + return Ok(wrap_stream(stream)); + } } + + let backend = self + .backend + .get_or_try_init(|| async { self.build_backend().await }) + .await + .map_err(map_api_error)?; + + let api_stream = backend.stream(prompt).await.map_err(map_api_error)?; + + Ok(wrap_stream(api_stream)) } fn populate_prompt(&self, prompt: &mut Prompt) { @@ -154,22 +235,20 @@ impl ModelClient { prompt.text_controls = create_text_param_for_request(verbosity, &prompt.output_schema); } - async fn stream_via_responses(&self, prompt: Prompt) -> Result { - if let Some(path) = &*CODEX_RS_SSE_FIXTURE { - warn!(path, "Streaming from fixture"); - let stream = - stream_from_fixture(path, self.provider.clone(), self.otel_event_manager.clone()) - .await - .map_err(map_api_error)?; - return Ok(wrap_stream(stream)); + async fn build_backend(&self) -> ApiClientResult { + match self.provider.wire_api { + WireApi::Responses => self.build_responses_backend().await, + WireApi::Chat => self.build_chat_backend().await, } + } + async fn build_responses_backend(&self) -> ApiClientResult { let auth_provider = self.auth_manager.as_ref().map(|manager| { Arc::new(AuthManagerProvider::new(Arc::clone(manager))) as Arc }); let config = ResponsesApiClientConfig { - http_client: self.client.clone_inner(), + http_client: self.http_client.clone(), provider: self.provider.clone(), model: self.config.model.clone(), conversation_id: self.conversation_id, @@ -177,42 +256,30 @@ impl ModelClient { otel_event_manager: self.otel_event_manager.clone(), }; - let client = ResponsesApiClient::new(config) - .await - .map_err(map_api_error)?; - - let stream = client.stream(prompt).await.map_err(map_api_error)?; - - Ok(wrap_stream(stream)) + let client = ResponsesApiClient::new(config).await?; + Ok(ModelBackend::Responses(ResponsesBackend { client })) } - async fn stream_via_chat(&self, prompt: Prompt) -> Result { + async fn build_chat_backend(&self) -> ApiClientResult { + let show_reasoning = self.config.show_raw_agent_reasoning; let config = ChatCompletionsApiClientConfig { - http_client: self.client.clone_inner(), + http_client: self.http_client.clone(), provider: self.provider.clone(), model: self.config.model.clone(), otel_event_manager: self.otel_event_manager.clone(), session_source: self.session_source.clone(), - aggregation_mode: if self.config.show_raw_agent_reasoning { + aggregation_mode: if show_reasoning { ChatAggregationMode::Streaming } else { ChatAggregationMode::AggregatedOnly }, }; - let client = ChatCompletionsApiClient::new(config) - .await - .map_err(map_api_error)?; - - let stream = client.stream(prompt).await.map_err(map_api_error)?; - - let stream = if self.config.show_raw_agent_reasoning { - stream.streaming_mode() - } else { - stream.aggregate() - }; - - Ok(wrap_stream(stream)) + let client = ChatCompletionsApiClient::new(config).await?; + Ok(ModelBackend::Chat(ChatBackend { + client, + show_reasoning, + })) } pub async fn stream_for_test(&self, mut prompt: Prompt) -> Result { @@ -304,15 +371,10 @@ impl AuthProvider for AuthManagerProvider { } } -fn wrap_stream(stream: S) -> ResponseStream -where - S: Stream> + Send + Unpin + 'static, -{ +fn wrap_stream(stream: ApiClientStream) -> ResponseStream { let (tx, rx) = mpsc::channel::>(1600); tokio::spawn(async move { - use futures::StreamExt; - let mut stream = stream; while let Some(item) = stream.next().await { let mapped = match item { @@ -326,7 +388,7 @@ where } }); - ResponseStream { rx_event: rx } + codex_api_client::EventStream::from_receiver(rx) } fn map_api_error(err: codex_api_client::Error) -> CodexErr { diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index bbf771bbb0..5ecc8a26a0 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -1,13 +1,8 @@ use std::borrow::Cow; use std::ops::Deref; -use futures::Stream; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; -use tokio::sync::mpsc; - use crate::error::Result; +use codex_api_client::EventStream; pub use codex_api_client::Prompt; pub use codex_api_client::Reasoning; pub use codex_api_client::TextControls; @@ -84,17 +79,7 @@ pub fn create_text_param_for_request( pub use codex_api_client::ResponseEvent; -pub struct ResponseStream { - pub(crate) rx_event: mpsc::Receiver>, -} - -impl Stream for ResponseStream { - type Item = Result; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.rx_event.poll_recv(cx) - } -} +pub type ResponseStream = EventStream>; #[cfg(test)] mod tests { diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index fc02b20479..ab9cc66713 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -51,7 +51,6 @@ use tracing::error; use tracing::info; use tracing::warn; -use crate::ModelProviderInfo; use crate::client::ModelClient; use crate::client::StreamPayload; use crate::client_common::Prompt; @@ -65,6 +64,7 @@ use crate::error::CodexErr; use crate::error::Result as CodexResult; #[cfg(test)] use crate::exec::StreamOutput; +use codex_api_client::ModelProviderInfo; // Removed: legacy executor wiring replaced by ToolOrchestrator flows. // legacy normalize_exec_result no longer used after orchestrator migration use crate::conversation_history::ResponsesApiChainState; @@ -362,6 +362,12 @@ pub(crate) struct SessionSettingsUpdate { pub(crate) final_output_json_schema: Option>, } +#[derive(Clone)] +struct PreparedPrompt { + prompt: Prompt, + full_prompt_items: Vec, +} + impl Session { fn make_turn_context( auth_manager: Option>, @@ -923,37 +929,51 @@ impl Session { self.send_raw_response_items(turn_context, items).await; } - async fn prepare_prompt_items( - &self, - turn_context: &TurnContext, - ) -> (Vec, Vec, Option, bool) { + async fn prepare_prompt(&self, turn_context: &TurnContext) -> PreparedPrompt { let use_chain = turn_context.client.supports_responses_api_chaining(); let mut history = self.clone_history().await; let full_prompt_items = history.get_history_for_prompt(); + let mut prompt = Prompt::default(); + prompt.store_response = use_chain; + if !use_chain { - let mut state = self.state.lock().await; - state.reset_responses_api_chain(); - return (full_prompt_items.clone(), full_prompt_items, None, false); + { + let mut state = self.state.lock().await; + state.reset_responses_api_chain(); + } + prompt.input = full_prompt_items.clone(); + return PreparedPrompt { + prompt, + full_prompt_items, + }; } - let mut state = self.state.lock().await; let mut previous_response_id = None; let mut request_items = full_prompt_items.clone(); - if let Some(chain) = state.responses_api_chain() - && let Some(prev_id) = chain.last_response_id { - let prefix = common_prefix_len(&chain.last_prompt_items, &full_prompt_items); - if prefix == 0 && !chain.last_prompt_items.is_empty() { - state.reset_responses_api_chain(); - } else { - previous_response_id = Some(prev_id); - request_items = full_prompt_items[prefix..].to_vec(); + let mut state = self.state.lock().await; + if let Some(chain) = state.responses_api_chain() + && let Some(prev_id) = chain.last_response_id + { + let prefix = common_prefix_len(&chain.last_prompt_items, &full_prompt_items); + if prefix == 0 && !chain.last_prompt_items.is_empty() { + state.reset_responses_api_chain(); + } else { + previous_response_id = Some(prev_id); + request_items = full_prompt_items[prefix..].to_vec(); + } } } - (request_items, full_prompt_items, previous_response_id, true) + prompt.previous_response_id = previous_response_id; + prompt.input = request_items; + + PreparedPrompt { + prompt, + full_prompt_items, + } } fn reconstruct_history_from_rollout( @@ -1797,11 +1817,11 @@ pub(crate) async fn run_task( // Construct the input that we will send to the model. sess.record_conversation_items(&turn_context, &pending_input) .await; - let (request_items, full_prompt_items, previous_response_id, store_response) = - sess.prepare_prompt_items(&turn_context).await; + let prepared_prompt = sess.prepare_prompt(&turn_context).await; let turn_input_messages: Vec = { - full_prompt_items + prepared_prompt + .full_prompt_items .iter() .filter_map(|item| match item { ResponseItem::Message { content, .. } => Some(content), @@ -1819,10 +1839,7 @@ pub(crate) async fn run_task( Arc::clone(&sess), Arc::clone(&turn_context), Arc::clone(&turn_diff_tracker), - request_items, - full_prompt_items, - previous_response_id, - store_response, + prepared_prompt, cancellation_token.child_token(), ) .await @@ -1914,10 +1931,7 @@ async fn run_turn( sess: Arc, turn_context: Arc, turn_diff_tracker: SharedTurnDiffTracker, - mut request_items: Vec, - mut full_prompt_items: Vec, - previous_response_id: Option, - store_response: bool, + mut prepared_prompt: PreparedPrompt, cancellation_token: CancellationToken, ) -> CodexResult { let mcp_tools = sess.services.mcp_connection_manager.list_all_tools(); @@ -1929,11 +1943,17 @@ async fn run_turn( let tool_specs = router.specs(); let (tools_json, has_freeform_apply_patch) = crate::tools::spec::tools_metadata_for_prompt(&tool_specs)?; - crate::conversation_history::format_prompt_items(&mut request_items, has_freeform_apply_patch); crate::conversation_history::format_prompt_items( - &mut full_prompt_items, + &mut prepared_prompt.prompt.input, has_freeform_apply_patch, ); + crate::conversation_history::format_prompt_items( + &mut prepared_prompt.full_prompt_items, + has_freeform_apply_patch, + ); + + let mut prompt = prepared_prompt.prompt; + let full_prompt_items = prepared_prompt.full_prompt_items; let apply_patch_present = tool_specs.iter().any(|spec| spec.name() == "apply_patch"); @@ -1949,16 +1969,10 @@ async fn run_turn( .get_model_family() .supports_parallel_tool_calls; let parallel_tool_calls = model_supports_parallel; - let prompt = Prompt { - instructions: instructions.clone(), - input: request_items, - tools: tools_json, - parallel_tool_calls, - output_schema: turn_context.final_output_json_schema.clone(), - store_response, - previous_response_id: previous_response_id.clone(), - ..Default::default() - }; + prompt.instructions = instructions.clone(); + prompt.tools = tools_json; + prompt.parallel_tool_calls = parallel_tool_calls; + prompt.output_schema = turn_context.final_output_json_schema.clone(); let payload = StreamPayload { prompt }; diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 688acde83f..f04ca5583f 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1,6 +1,4 @@ -use crate::ModelProviderInfo; use crate::auth::AuthCredentialsStoreMode; -use crate::built_in_model_providers; use crate::config::types::DEFAULT_OTEL_ENVIRONMENT; use crate::config::types::History; use crate::config::types::McpServerConfig; @@ -32,6 +30,8 @@ use crate::project_doc::DEFAULT_PROJECT_DOC_FILENAME; use crate::project_doc::LOCAL_PROJECT_DOC_FILENAME; use crate::protocol::AskForApproval; use crate::protocol::SandboxPolicy; +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_app_server_protocol::Tools; use codex_app_server_protocol::UserSavedConfig; use codex_protocol::config_types::ForcedLoginMethod; @@ -2712,7 +2712,7 @@ model_verbosity = "high" name: "OpenAI using Chat Completions".to_string(), base_url: Some("https://api.openai.com/v1".to_string()), env_key: Some("OPENAI_API_KEY".to_string()), - wire_api: crate::WireApi::Chat, + wire_api: codex_api_client::WireApi::Chat, env_key_instructions: None, experimental_bearer_token: None, query_params: None, diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index d303ccb7f7..e4b8e4d95c 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -18,9 +18,11 @@ mod command_safety; pub mod config; pub mod config_loader; mod conversation_history; +mod conversation_manager; pub mod custom_prompts; mod environment_context; pub mod error; +mod event_mapping; pub mod exec; pub mod exec_env; pub mod features; @@ -33,19 +35,12 @@ mod mcp_tool_call; mod message_history; pub mod parse_command; mod response_processing; +pub mod review_format; pub mod sandboxing; pub mod token_data; mod truncate; mod unified_exec; mod user_instructions; -pub use codex_api_client::BUILT_IN_OSS_MODEL_PROVIDER_ID; -pub use codex_api_client::ModelProviderInfo; -pub use codex_api_client::WireApi; -pub use codex_api_client::built_in_model_providers; -pub use codex_api_client::create_oss_provider_with_base_url; -mod conversation_manager; -mod event_mapping; -pub mod review_format; pub use codex_protocol::protocol::InitialHistory; pub use conversation_manager::ConversationManager; pub use conversation_manager::NewConversation; diff --git a/codex-rs/core/src/sandboxing/assessment.rs b/codex-rs/core/src/sandboxing/assessment.rs index 8c417b9663..80a31080df 100644 --- a/codex-rs/core/src/sandboxing/assessment.rs +++ b/codex-rs/core/src/sandboxing/assessment.rs @@ -5,7 +5,6 @@ use std::time::Duration; use std::time::Instant; use crate::AuthManager; -use crate::ModelProviderInfo; use crate::client::ModelClient; use crate::client::StreamPayload; use crate::client_common::Prompt; @@ -13,6 +12,7 @@ use crate::client_common::ResponseEvent; use crate::config::Config; use crate::protocol::SandboxPolicy; use askama::Template; +use codex_api_client::ModelProviderInfo; use codex_otel::otel_event_manager::OtelEventManager; use codex_protocol::ConversationId; use codex_protocol::models::ContentItem; diff --git a/codex-rs/core/tests/chat_completions_payload.rs b/codex-rs/core/tests/chat_completions_payload.rs index d433b75962..ef12c8629b 100644 --- a/codex-rs/core/tests/chat_completions_payload.rs +++ b/codex-rs/core/tests/chat_completions_payload.rs @@ -1,15 +1,15 @@ use std::sync::Arc; +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; use codex_app_server_protocol::AuthMode; use codex_core::ContentItem; use codex_core::LocalShellAction; use codex_core::LocalShellExecAction; use codex_core::LocalShellStatus; use codex_core::ModelClient; -use codex_core::ModelProviderInfo; use codex_core::Prompt; use codex_core::ResponseItem; -use codex_core::WireApi; use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use codex_otel::otel_event_manager::OtelEventManager; use codex_protocol::ConversationId; diff --git a/codex-rs/core/tests/chat_completions_sse.rs b/codex-rs/core/tests/chat_completions_sse.rs index c6ce45ee4f..1db4053bba 100644 --- a/codex-rs/core/tests/chat_completions_sse.rs +++ b/codex-rs/core/tests/chat_completions_sse.rs @@ -2,14 +2,14 @@ use assert_matches::assert_matches; use std::sync::Arc; use tracing_test::traced_test; +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; use codex_app_server_protocol::AuthMode; use codex_core::ContentItem; use codex_core::ModelClient; -use codex_core::ModelProviderInfo; use codex_core::Prompt; use codex_core::ResponseEvent; use codex_core::ResponseItem; -use codex_core::WireApi; use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use codex_otel::otel_event_manager::OtelEventManager; use codex_protocol::ConversationId; diff --git a/codex-rs/core/tests/common/Cargo.toml b/codex-rs/core/tests/common/Cargo.toml index 65abe23c63..b346ca0a2c 100644 --- a/codex-rs/core/tests/common/Cargo.toml +++ b/codex-rs/core/tests/common/Cargo.toml @@ -9,6 +9,7 @@ path = "lib.rs" [dependencies] anyhow = { workspace = true } assert_cmd = { workspace = true } +codex-api-client = { workspace = true } codex-core = { workspace = true } codex-protocol = { workspace = true } notify = { workspace = true } diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 6d1fda5aa0..30b89e028d 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -4,11 +4,11 @@ use std::path::PathBuf; use std::sync::Arc; use anyhow::Result; +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_core::CodexAuth; use codex_core::CodexConversation; use codex_core::ConversationManager; -use codex_core::ModelProviderInfo; -use codex_core::built_in_model_providers; use codex_core::config::Config; use codex_core::features::Feature; use codex_core::protocol::AskForApproval; diff --git a/codex-rs/core/tests/responses_headers.rs b/codex-rs/core/tests/responses_headers.rs index 3214b380cc..21dc88c5cf 100644 --- a/codex-rs/core/tests/responses_headers.rs +++ b/codex-rs/core/tests/responses_headers.rs @@ -1,13 +1,13 @@ use std::sync::Arc; +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; use codex_app_server_protocol::AuthMode; use codex_core::ContentItem; use codex_core::ModelClient; -use codex_core::ModelProviderInfo; use codex_core::Prompt; use codex_core::ResponseEvent; use codex_core::ResponseItem; -use codex_core::WireApi; use codex_otel::otel_event_manager::OtelEventManager; use codex_protocol::ConversationId; use codex_protocol::protocol::SessionSource; diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index add5ce1faf..5739985a65 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -1,3 +1,6 @@ +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; +use codex_api_client::built_in_model_providers; use codex_app_server_protocol::AuthMode; use codex_core::CodexAuth; use codex_core::ContentItem; @@ -6,14 +9,11 @@ use codex_core::LocalShellAction; use codex_core::LocalShellExecAction; use codex_core::LocalShellStatus; use codex_core::ModelClient; -use codex_core::ModelProviderInfo; use codex_core::NewConversation; use codex_core::Prompt; use codex_core::ResponseEvent; use codex_core::ResponseItem; -use codex_core::WireApi; use codex_core::auth::AuthCredentialsStoreMode; -use codex_core::built_in_model_providers; use codex_core::error::CodexErr; use codex_core::features::Feature; use codex_core::model_family::find_family_for_model; diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index 17d72abe7e..54a26eed33 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -1,8 +1,8 @@ +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_core::CodexAuth; use codex_core::ConversationManager; -use codex_core::ModelProviderInfo; use codex_core::NewConversation; -use codex_core::built_in_model_providers; use codex_core::protocol::ErrorEvent; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; diff --git a/codex-rs/core/tests/suite/compact_resume_fork.rs b/codex-rs/core/tests/suite/compact_resume_fork.rs index b13c6e14fd..12b5cf78fb 100644 --- a/codex-rs/core/tests/suite/compact_resume_fork.rs +++ b/codex-rs/core/tests/suite/compact_resume_fork.rs @@ -9,12 +9,12 @@ use super::compact::FIRST_REPLY; use super::compact::SUMMARY_TEXT; +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_core::CodexAuth; use codex_core::CodexConversation; use codex_core::ConversationManager; -use codex_core::ModelProviderInfo; use codex_core::NewConversation; -use codex_core::built_in_model_providers; use codex_core::codex::compact::SUMMARIZATION_PROMPT; use codex_core::config::Config; use codex_core::config::OPENAI_DEFAULT_MODEL; diff --git a/codex-rs/core/tests/suite/fork_conversation.rs b/codex-rs/core/tests/suite/fork_conversation.rs index 75b37ae7ef..c7caf5662c 100644 --- a/codex-rs/core/tests/suite/fork_conversation.rs +++ b/codex-rs/core/tests/suite/fork_conversation.rs @@ -1,8 +1,8 @@ +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_core::CodexAuth; use codex_core::ConversationManager; -use codex_core::ModelProviderInfo; use codex_core::NewConversation; -use codex_core::built_in_model_providers; use codex_core::parse_turn_item; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; diff --git a/codex-rs/core/tests/suite/model_tools.rs b/codex-rs/core/tests/suite/model_tools.rs index 2a04b88a16..079887e48c 100644 --- a/codex-rs/core/tests/suite/model_tools.rs +++ b/codex-rs/core/tests/suite/model_tools.rs @@ -1,9 +1,9 @@ #![allow(clippy::unwrap_used)] +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_core::CodexAuth; use codex_core::ConversationManager; -use codex_core::ModelProviderInfo; -use codex_core::built_in_model_providers; use codex_core::features::Feature; use codex_core::model_family::find_family_for_model; use codex_core::protocol::EventMsg; diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index 04304126f0..e086f81f05 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -1,9 +1,9 @@ #![allow(clippy::unwrap_used)] +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_core::CodexAuth; use codex_core::ConversationManager; -use codex_core::ModelProviderInfo; -use codex_core::built_in_model_providers; use codex_core::config::OPENAI_DEFAULT_MODEL; use codex_core::features::Feature; use codex_core::model_family::find_family_for_model; diff --git a/codex-rs/core/tests/suite/review.rs b/codex-rs/core/tests/suite/review.rs index 093fd99268..2d7680f99c 100644 --- a/codex-rs/core/tests/suite/review.rs +++ b/codex-rs/core/tests/suite/review.rs @@ -1,11 +1,11 @@ +use codex_api_client::ModelProviderInfo; +use codex_api_client::built_in_model_providers; use codex_core::CodexAuth; use codex_core::CodexConversation; use codex_core::ContentItem; use codex_core::ConversationManager; -use codex_core::ModelProviderInfo; use codex_core::REVIEW_PROMPT; use codex_core::ResponseItem; -use codex_core::built_in_model_providers; use codex_core::config::Config; use codex_core::protocol::ENVIRONMENT_CONTEXT_OPEN_TAG; use codex_core::protocol::EventMsg; diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index 672633ace5..edd47a6822 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -422,7 +422,7 @@ async fn stdio_image_completions_round_trip() -> anyhow::Result<()> { let fixture = test_codex() .with_config(move |config| { - config.model_provider.wire_api = codex_core::WireApi::Chat; + config.model_provider.wire_api = codex_api_client::WireApi::Chat; config.features.enable(Feature::RmcpClient); config.mcp_servers.insert( server_name.to_string(), diff --git a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs index ba86f8c155..cbc8c22530 100644 --- a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs +++ b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs @@ -1,7 +1,7 @@ use std::time::Duration; -use codex_core::ModelProviderInfo; -use codex_core::WireApi; +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; use codex_protocol::user_input::UserInput; diff --git a/codex-rs/core/tests/suite/stream_no_completed.rs b/codex-rs/core/tests/suite/stream_no_completed.rs index 550bb3f9c2..0f13be4043 100644 --- a/codex-rs/core/tests/suite/stream_no_completed.rs +++ b/codex-rs/core/tests/suite/stream_no_completed.rs @@ -3,8 +3,8 @@ use std::time::Duration; -use codex_core::ModelProviderInfo; -use codex_core::WireApi; +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; use codex_protocol::user_input::UserInput; diff --git a/codex-rs/exec/Cargo.toml b/codex-rs/exec/Cargo.toml index 8fc1e38875..45e96f6cd7 100644 --- a/codex-rs/exec/Cargo.toml +++ b/codex-rs/exec/Cargo.toml @@ -24,6 +24,7 @@ codex-common = { workspace = true, features = [ "sandbox_summary", ] } codex-core = { workspace = true } +codex-api-client = { workspace = true } codex-ollama = { workspace = true } codex-protocol = { workspace = true } mcp-types = { workspace = true } diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index af45a7d20a..ba0a30d716 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -11,8 +11,8 @@ pub mod event_processor_with_jsonl_output; pub mod exec_events; pub use cli::Cli; +use codex_api_client::BUILT_IN_OSS_MODEL_PROVIDER_ID; use codex_core::AuthManager; -use codex_core::BUILT_IN_OSS_MODEL_PROVIDER_ID; use codex_core::ConversationManager; use codex_core::NewConversation; use codex_core::auth::enforce_login_restrictions; diff --git a/codex-rs/ollama/Cargo.toml b/codex-rs/ollama/Cargo.toml index 14dd6d2fcd..39c39874b2 100644 --- a/codex-rs/ollama/Cargo.toml +++ b/codex-rs/ollama/Cargo.toml @@ -13,6 +13,7 @@ workspace = true [dependencies] async-stream = { workspace = true } bytes = { workspace = true } +codex-api-client = { workspace = true } codex-core = { workspace = true } futures = { workspace = true } reqwest = { workspace = true, features = ["json", "stream"] } diff --git a/codex-rs/ollama/src/client.rs b/codex-rs/ollama/src/client.rs index 04b7e9dea2..460aca4b63 100644 --- a/codex-rs/ollama/src/client.rs +++ b/codex-rs/ollama/src/client.rs @@ -10,9 +10,9 @@ use crate::pull::PullEvent; use crate::pull::PullProgressReporter; use crate::url::base_url_to_host_root; use crate::url::is_openai_compatible_base_url; -use codex_core::BUILT_IN_OSS_MODEL_PROVIDER_ID; -use codex_core::ModelProviderInfo; -use codex_core::WireApi; +use codex_api_client::BUILT_IN_OSS_MODEL_PROVIDER_ID; +use codex_api_client::ModelProviderInfo; +use codex_api_client::WireApi; use codex_core::config::Config; const OLLAMA_CONNECTION_ERROR: &str = "No running Ollama server detected. Start it with: `ollama serve` (after installing). Install instructions: https://github.com/ollama/ollama?tab=readme-ov-file#ollama"; @@ -47,7 +47,7 @@ impl OllamaClient { #[cfg(test)] async fn try_from_provider_with_base_url(base_url: &str) -> io::Result { - let provider = codex_core::create_oss_provider_with_base_url(base_url); + let provider = codex_api_client::create_oss_provider_with_base_url(base_url); Self::try_from_provider(&provider).await } diff --git a/codex-rs/tui/Cargo.toml b/codex-rs/tui/Cargo.toml index f087202c22..f98b2f9199 100644 --- a/codex-rs/tui/Cargo.toml +++ b/codex-rs/tui/Cargo.toml @@ -34,6 +34,7 @@ codex-common = { workspace = true, features = [ "sandbox_summary", ] } codex-core = { workspace = true } +codex-api-client = { workspace = true } codex-file-search = { workspace = true } codex-login = { workspace = true } codex-ollama = { workspace = true } diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 028bf68e87..ecf6925922 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -6,9 +6,9 @@ use additional_dirs::add_dir_warning_message; use app::App; pub use app::AppExitInfo; +use codex_api_client::BUILT_IN_OSS_MODEL_PROVIDER_ID; use codex_app_server_protocol::AuthMode; use codex_core::AuthManager; -use codex_core::BUILT_IN_OSS_MODEL_PROVIDER_ID; use codex_core::CodexAuth; use codex_core::INTERACTIVE_SESSION_SOURCES; use codex_core::RolloutRecorder;