From 7f203576116de4002fcda5942a1e4248e18692ff Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 4 Feb 2026 09:05:37 -0800 Subject: [PATCH] Stop client from being state carrier (#10595) I'd like to make client session wide. This requires shedding all random state it has to carry. --- codex-rs/core/src/client.rs | 55 -------- codex-rs/core/src/codex.rs | 133 ++++++++++++------- codex-rs/core/src/compact.rs | 10 +- codex-rs/core/src/compact_remote.rs | 4 +- codex-rs/core/src/context_manager/history.rs | 6 +- codex-rs/core/src/mcp/skill_dependencies.rs | 2 +- codex-rs/core/src/mcp_tool_call.rs | 6 +- codex-rs/core/src/stream_events_utils.rs | 3 +- codex-rs/core/src/tasks/compact.rs | 5 +- codex-rs/core/src/tasks/mod.rs | 3 +- codex-rs/core/src/tasks/review.rs | 4 +- codex-rs/core/src/tasks/user_shell.rs | 2 +- codex-rs/core/src/tools/handlers/collab.rs | 66 +++------ codex-rs/core/src/tools/orchestrator.rs | 2 +- codex-rs/core/src/tools/registry.rs | 2 +- 15 files changed, 127 insertions(+), 176 deletions(-) diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 1d0dc7fb6..dd92c5da0 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -153,61 +153,6 @@ impl ModelClient { } impl ModelClient { - pub fn get_model_context_window(&self) -> Option { - let model_info = &self.state.model_info; - let effective_context_window_percent = model_info.effective_context_window_percent; - model_info.context_window.map(|context_window| { - context_window.saturating_mul(effective_context_window_percent) / 100 - }) - } - - pub fn config(&self) -> Arc { - Arc::clone(&self.state.config) - } - - pub fn provider(&self) -> &ModelProviderInfo { - &self.state.provider - } - - pub fn get_provider(&self) -> ModelProviderInfo { - self.state.provider.clone() - } - - pub fn get_otel_manager(&self) -> OtelManager { - self.state.otel_manager.clone() - } - - pub fn get_session_source(&self) -> SessionSource { - self.state.session_source.clone() - } - - pub(crate) fn transport_manager(&self) -> TransportManager { - self.state.transport_manager.clone() - } - - /// Returns the currently configured model slug. - pub fn get_model(&self) -> String { - self.state.model_info.slug.clone() - } - - pub fn get_model_info(&self) -> ModelInfo { - self.state.model_info.clone() - } - - /// Returns the current reasoning effort setting. - pub fn get_reasoning_effort(&self) -> Option { - self.state.effort - } - - /// Returns the current reasoning summary setting. - pub fn get_reasoning_summary(&self) -> ReasoningSummaryConfig { - self.state.summary - } - - pub fn get_auth_manager(&self) -> Option> { - self.state.auth_manager.clone() - } - /// Compacts the current conversation history using the Compact endpoint. /// /// This is a unary call (no streaming) that returns a new list of diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 7fe3b8c81..979f2a5f6 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -212,6 +212,7 @@ use codex_protocol::models::ContentItem; use codex_protocol::models::DeveloperInstructions; use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; +use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; use codex_protocol::protocol::CodexErrorInfo; use codex_protocol::protocol::InitialHistory; use codex_protocol::user_input::UserInput; @@ -484,6 +485,15 @@ pub(crate) struct Session { pub(crate) struct TurnContext { pub(crate) sub_id: String, pub(crate) client: ModelClient, + pub(crate) config: Arc, + pub(crate) auth_manager: Option>, + pub(crate) model_info: ModelInfo, + pub(crate) otel_manager: OtelManager, + pub(crate) provider: ModelProviderInfo, + pub(crate) reasoning_effort: Option, + pub(crate) reasoning_summary: ReasoningSummaryConfig, + pub(crate) session_source: SessionSource, + pub(crate) transport_manager: TransportManager, /// The session's current working directory. All relative paths provided by /// the model as well as sandbox policies are resolved against this path /// instead of `std::env::current_dir()`. @@ -507,6 +517,13 @@ pub(crate) struct TurnContext { turn_metadata_header: OnceCell>, } impl TurnContext { + pub(crate) fn model_context_window(&self) -> Option { + let effective_context_window_percent = self.model_info.effective_context_window_percent; + self.model_info.context_window.map(|context_window| { + context_window.saturating_mul(effective_context_window_percent) / 100 + }) + } + pub(crate) fn resolve_path(&self, path: Option) -> PathBuf { path.as_ref() .map(PathBuf::from) @@ -694,10 +711,17 @@ impl Session { sub_id: String, transport_manager: TransportManager, ) -> TurnContext { + let reasoning_effort = session_configuration.collaboration_mode.reasoning_effort(); + let reasoning_summary = session_configuration.model_reasoning_summary; let otel_manager = otel_manager.clone().with_model( session_configuration.collaboration_mode.model(), model_info.slug.as_str(), ); + let session_source = session_configuration.session_source.clone(); + let auth_manager_for_context = auth_manager.clone(); + let provider_for_context = provider.clone(); + let transport_manager_for_context = transport_manager.clone(); + let otel_manager_for_context = otel_manager.clone(); let per_turn_config = Arc::new(per_turn_config); let client = ModelClient::new( per_turn_config.clone(), @@ -705,10 +729,10 @@ impl Session { model_info.clone(), otel_manager, provider, - session_configuration.collaboration_mode.reasoning_effort(), - session_configuration.model_reasoning_summary, + reasoning_effort, + reasoning_summary, conversation_id, - session_configuration.session_source.clone(), + session_source.clone(), transport_manager, ); @@ -722,6 +746,15 @@ impl Session { TurnContext { sub_id, client, + config: per_turn_config.clone(), + auth_manager: auth_manager_for_context, + model_info: model_info.clone(), + otel_manager: otel_manager_for_context, + provider: provider_for_context, + reasoning_effort, + reasoning_summary, + session_source, + transport_manager: transport_manager_for_context, cwd, developer_instructions: session_configuration.developer_instructions.clone(), compact_prompt: session_configuration.compact_prompt.clone(), @@ -1100,7 +1133,7 @@ impl Session { None } }) { - let curr = turn_context.client.get_model(); + let curr = turn_context.model_info.slug.as_str(); if prev != curr { warn!( "resuming session with different model: previous={prev}, current={curr}" @@ -1378,8 +1411,8 @@ impl Session { if let Some(personality) = next.personality && next.personality != previous.personality { - let model_info = next.client.get_model_info(); - let personality_message = Self::personality_message_for(&model_info, personality); + let model_info = &next.model_info; + let personality_message = Self::personality_message_for(model_info, personality); personality_message.map(|personality_message| { DeveloperInstructions::personality_spec_message(personality_message).into() }) @@ -1943,7 +1976,7 @@ impl Session { if self.features.enabled(Feature::Personality) && let Some(personality) = turn_context.personality { - let model_info = turn_context.client.get_model_info(); + let model_info = turn_context.model_info.clone(); let has_baked_personality = model_info.supports_personality() && base_instructions == model_info.get_model_instructions(Some(personality)); if !has_baked_personality @@ -1996,10 +2029,8 @@ impl Session { { let mut state = self.state.lock().await; if let Some(token_usage) = token_usage { - state.update_token_info_from_usage( - token_usage, - turn_context.client.get_model_context_window(), - ); + state + .update_token_info_from_usage(token_usage, turn_context.model_context_window()); } } self.send_token_count_event(turn_context).await; @@ -2030,7 +2061,7 @@ impl Session { }; if info.model_context_window.is_none() { - info.model_context_window = turn_context.client.get_model_context_window(); + info.model_context_window = turn_context.model_context_window(); } state.set_token_info(Some(info)); @@ -2088,7 +2119,7 @@ impl Session { } pub(crate) async fn set_total_tokens_full(&self, turn_context: &TurnContext) { - if let Some(context_window) = turn_context.client.get_model_context_window() { + if let Some(context_window) = turn_context.model_context_window() { let mut state = self.state.lock().await; state.set_token_usage_full(context_window); } @@ -2702,10 +2733,7 @@ mod handlers { // new_turn_with_sub_id already emits the error event. return; }; - current_context - .client - .get_otel_manager() - .user_prompt(&items); + current_context.otel_manager.user_prompt(&items); // Attempt to inject input into current task if let Err(items) = sess.inject_input(items).await { @@ -3247,7 +3275,7 @@ async fn spawn_review_thread( let model = config .review_model .clone() - .unwrap_or_else(|| parent_turn_context.client.get_model()); + .unwrap_or_else(|| parent_turn_context.model_info.slug.clone()); let review_model_info = sess .services .models_manager @@ -3266,8 +3294,8 @@ async fn spawn_review_thread( }); let review_prompt = resolved.prompt.clone(); - let provider = parent_turn_context.client.get_provider(); - let auth_manager = parent_turn_context.client.get_auth_manager(); + let provider = parent_turn_context.provider.clone(); + let auth_manager = parent_turn_context.auth_manager.clone(); let model_info = review_model_info.clone(); // Build per‑turn client with the requested model/family. @@ -3277,9 +3305,16 @@ async fn spawn_review_thread( per_turn_config.web_search_mode = Some(review_web_search_mode); let otel_manager = parent_turn_context - .client - .get_otel_manager() + .otel_manager + .clone() .with_model(model.as_str(), review_model_info.slug.as_str()); + let auth_manager_for_context = auth_manager.clone(); + let provider_for_context = provider.clone(); + let otel_manager_for_context = otel_manager.clone(); + let reasoning_effort = per_turn_config.model_reasoning_effort; + let reasoning_summary = per_turn_config.model_reasoning_summary; + let session_source = parent_turn_context.session_source.clone(); + let transport_manager = parent_turn_context.transport_manager.clone(); let per_turn_config = Arc::new(per_turn_config); let client = ModelClient::new( @@ -3288,16 +3323,25 @@ async fn spawn_review_thread( model_info.clone(), otel_manager, provider, - per_turn_config.model_reasoning_effort, - per_turn_config.model_reasoning_summary, + reasoning_effort, + reasoning_summary, sess.conversation_id, - parent_turn_context.client.get_session_source(), - parent_turn_context.client.transport_manager(), + session_source.clone(), + transport_manager.clone(), ); let review_turn_context = TurnContext { sub_id: sub_id.to_string(), client, + config: per_turn_config, + auth_manager: auth_manager_for_context, + model_info: model_info.clone(), + otel_manager: otel_manager_for_context, + provider: provider_for_context, + reasoning_effort, + reasoning_summary, + session_source, + transport_manager, tools_config, ghost_snapshot: parent_turn_context.ghost_snapshot.clone(), developer_instructions: None, @@ -3414,12 +3458,12 @@ pub(crate) async fn run_turn( return None; } - let model_info = turn_context.client.get_model_info(); + let model_info = turn_context.model_info.clone(); let auto_compact_limit = model_info.auto_compact_token_limit().unwrap_or(i64::MAX); let total_usage_tokens = sess.get_total_token_usage().await; let event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), + model_context_window: turn_context.model_context_window(), collaboration_mode_kind: turn_context.collaboration_mode.mode, }); sess.send_event(&turn_context, event).await; @@ -3438,7 +3482,7 @@ pub(crate) async fn run_turn( || (HashMap::new(), HashMap::new()), |outcome| build_skill_name_counts(&outcome.skills, &outcome.disabled_paths), ); - let connector_slug_counts = if turn_context.client.config().features.enabled(Feature::Apps) { + let connector_slug_counts = if turn_context.config.features.enabled(Feature::Apps) { let mcp_tools = match sess .services .mcp_connection_manager @@ -3467,7 +3511,7 @@ pub(crate) async fn run_turn( }); let explicit_app_paths = collect_explicit_app_paths(&input); - let config = turn_context.client.config(); + let config = turn_context.config.clone(); if config .features .enabled(Feature::SkillEnvVarDependencyPrompt) @@ -3484,9 +3528,9 @@ pub(crate) async fn run_turn( ) .await; - let otel_manager = turn_context.client.get_otel_manager(); + let otel_manager = turn_context.otel_manager.clone(); let thread_id = sess.conversation_id.to_string(); - let tracking = build_track_events_context(turn_context.client.get_model(), thread_id); + let tracking = build_track_events_context(turn_context.model_info.slug.clone(), thread_id); let SkillInjections { items: skill_items, warnings: skill_warnings, @@ -3639,7 +3683,7 @@ pub(crate) async fn run_turn( } async fn run_auto_compact(sess: &Arc, turn_context: &Arc) { - if should_use_remote_compact_task(sess.as_ref(), &turn_context.client.get_provider()) { + if should_use_remote_compact_task(sess.as_ref(), &turn_context.provider) { run_inline_remote_auto_compact_task(Arc::clone(sess), Arc::clone(turn_context)).await; } else { run_inline_auto_compact_task(Arc::clone(sess), Arc::clone(turn_context)).await; @@ -3748,7 +3792,7 @@ struct SamplingRequestToolSelection<'a> { skip_all, fields( turn_id = %turn_context.sub_id, - model = %turn_context.client.get_model(), + model = %turn_context.model_info.slug, cwd = %turn_context.cwd.display() ) )] @@ -3769,7 +3813,7 @@ async fn run_sampling_request( .list_all_tools() .or_cancel(&cancellation_token) .await?; - let connectors_for_tools = if turn_context.client.config().features.enabled(Feature::Apps) { + let connectors_for_tools = if turn_context.config.features.enabled(Feature::Apps) { let connectors = connectors::accessible_connectors_from_mcp_tools(&mcp_tools); Some(filter_connectors_for_input( connectors, @@ -3794,10 +3838,7 @@ async fn run_sampling_request( turn_context.dynamic_tools.as_slice(), )); - let model_supports_parallel = turn_context - .client - .get_model_info() - .supports_parallel_tool_calls; + let model_supports_parallel = turn_context.model_info.supports_parallel_tool_calls; let base_instructions = sess.get_base_instructions().await; @@ -3845,7 +3886,7 @@ async fn run_sampling_request( } // Use the configured provider-specific stream retry budget. - let max_retries = turn_context.client.get_provider().stream_max_retries(); + let max_retries = turn_context.provider.stream_max_retries(); if retries >= max_retries && client_session.try_switch_fallback_transport() { sess.send_event( &turn_context, @@ -4291,7 +4332,7 @@ async fn drain_in_flight( skip_all, fields( turn_id = %turn_context.sub_id, - model = %turn_context.client.get_model() + model = %turn_context.model_info.slug ) )] async fn try_run_sampling_request( @@ -4308,11 +4349,11 @@ async fn try_run_sampling_request( cwd: turn_context.cwd.clone(), approval_policy: turn_context.approval_policy, sandbox_policy: turn_context.sandbox_policy.clone(), - model: turn_context.client.get_model(), + model: turn_context.model_info.slug.clone(), personality: turn_context.personality, collaboration_mode: Some(collaboration_mode), - effort: turn_context.client.get_reasoning_effort(), - summary: turn_context.client.get_reasoning_summary(), + effort: turn_context.reasoning_effort, + summary: turn_context.reasoning_summary, user_instructions: turn_context.user_instructions.clone(), developer_instructions: turn_context.developer_instructions.clone(), final_output_json_schema: turn_context.final_output_json_schema.clone(), @@ -4320,10 +4361,10 @@ async fn try_run_sampling_request( }); feedback_tags!( - model = turn_context.client.get_model(), + model = turn_context.model_info.slug.clone(), approval_policy = turn_context.approval_policy, sandbox_policy = turn_context.sandbox_policy, - effort = turn_context.client.get_reasoning_effort(), + effort = turn_context.reasoning_effort, auth_mode = sess.services.auth_manager.get_auth_mode(), features = sess.features.enabled_features(), ); diff --git a/codex-rs/core/src/compact.rs b/codex-rs/core/src/compact.rs index 7ae773884..c8aba5661 100644 --- a/codex-rs/core/src/compact.rs +++ b/codex-rs/core/src/compact.rs @@ -60,7 +60,7 @@ pub(crate) async fn run_compact_task( input: Vec, ) { let start_event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), + model_context_window: turn_context.model_context_window(), collaboration_mode_kind: turn_context.collaboration_mode.mode, }); sess.send_event(&turn_context, start_event).await; @@ -85,7 +85,7 @@ async fn run_compact_task_inner( let mut truncated_count = 0usize; - let max_retries = turn_context.client.get_provider().stream_max_retries(); + let max_retries = turn_context.provider.stream_max_retries(); let mut retries = 0; // TODO: If we need to guarantee the persisted mode always matches the prompt used for this @@ -97,11 +97,11 @@ async fn run_compact_task_inner( cwd: turn_context.cwd.clone(), approval_policy: turn_context.approval_policy, sandbox_policy: turn_context.sandbox_policy.clone(), - model: turn_context.client.get_model(), + model: turn_context.model_info.slug.clone(), personality: turn_context.personality, collaboration_mode: Some(collaboration_mode), - effort: turn_context.client.get_reasoning_effort(), - summary: turn_context.client.get_reasoning_summary(), + effort: turn_context.reasoning_effort, + summary: turn_context.reasoning_summary, user_instructions: turn_context.user_instructions.clone(), developer_instructions: turn_context.developer_instructions.clone(), final_output_json_schema: turn_context.final_output_json_schema.clone(), diff --git a/codex-rs/core/src/compact_remote.rs b/codex-rs/core/src/compact_remote.rs index 12bc769ce..fbb6863c3 100644 --- a/codex-rs/core/src/compact_remote.rs +++ b/codex-rs/core/src/compact_remote.rs @@ -24,7 +24,7 @@ pub(crate) async fn run_inline_remote_auto_compact_task( pub(crate) async fn run_remote_compact_task(sess: Arc, turn_context: Arc) { let start_event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), + model_context_window: turn_context.model_context_window(), collaboration_mode_kind: turn_context.collaboration_mode.mode, }); sess.send_event(&turn_context, start_event).await; @@ -104,7 +104,7 @@ fn trim_function_call_history_to_fit_context_window( turn_context: &TurnContext, ) -> usize { let mut deleted_items = 0usize; - let Some(context_window) = turn_context.client.get_model_context_window() else { + let Some(context_window) = turn_context.model_context_window() else { return deleted_items; }; diff --git a/codex-rs/core/src/context_manager/history.rs b/codex-rs/core/src/context_manager/history.rs index a29f7df7e..a68c4daaf 100644 --- a/codex-rs/core/src/context_manager/history.rs +++ b/codex-rs/core/src/context_manager/history.rs @@ -85,10 +85,8 @@ impl ContextManager { // Estimate token usage using byte-based heuristics from the truncation helpers. // This is a coarse lower bound, not a tokenizer-accurate count. pub(crate) fn estimate_token_count(&self, turn_context: &TurnContext) -> Option { - let model_info = turn_context.client.get_model_info(); - let personality = turn_context - .personality - .or(turn_context.client.config().personality); + let model_info = &turn_context.model_info; + let personality = turn_context.personality.or(turn_context.config.personality); let base_instructions = model_info.get_model_instructions(personality); let base_tokens = i64::try_from(approx_token_count(&base_instructions)).unwrap_or(i64::MAX); diff --git a/codex-rs/core/src/mcp/skill_dependencies.rs b/codex-rs/core/src/mcp/skill_dependencies.rs index 37620d73d..14b71fcf1 100644 --- a/codex-rs/core/src/mcp/skill_dependencies.rs +++ b/codex-rs/core/src/mcp/skill_dependencies.rs @@ -144,7 +144,7 @@ pub(crate) async fn maybe_prompt_and_install_mcp_dependencies( return; } - let config = turn_context.client.config(); + let config = turn_context.config.clone(); if mentioned_skills.is_empty() || !config.features.enabled(Feature::SkillMcpDependencyInstall) { return; } diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 75248f34c..d1d009470 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -120,8 +120,7 @@ pub(crate) async fn handle_mcp_tool_call( let status = if result.is_ok() { "ok" } else { "error" }; turn_context - .client - .get_otel_manager() + .otel_manager .counter("codex.mcp.call", 1, &[("status", status)]); return ResponseInputItem::McpToolCallOutput { call_id, result }; @@ -153,8 +152,7 @@ pub(crate) async fn handle_mcp_tool_call( let status = if result.is_ok() { "ok" } else { "error" }; turn_context - .client - .get_otel_manager() + .otel_manager .counter("codex.mcp.call", 1, &[("status", status)]); ResponseInputItem::McpToolCallOutput { call_id, result } diff --git a/codex-rs/core/src/stream_events_utils.rs b/codex-rs/core/src/stream_events_utils.rs index 02d982251..31b4f1837 100644 --- a/codex-rs/core/src/stream_events_utils.rs +++ b/codex-rs/core/src/stream_events_utils.rs @@ -100,8 +100,7 @@ pub(crate) async fn handle_output_item_done( Err(FunctionCallError::MissingLocalShellCallId) => { let msg = "LocalShellCall without call_id or id"; ctx.turn_context - .client - .get_otel_manager() + .otel_manager .log_tool_failed("local_shell", msg); tracing::error!(msg); diff --git a/codex-rs/core/src/tasks/compact.rs b/codex-rs/core/src/tasks/compact.rs index 00d882f60..e1fdbfd91 100644 --- a/codex-rs/core/src/tasks/compact.rs +++ b/codex-rs/core/src/tasks/compact.rs @@ -25,10 +25,7 @@ impl SessionTask for CompactTask { _cancellation_token: CancellationToken, ) -> Option { let session = session.clone_session(); - if crate::compact::should_use_remote_compact_task( - session.as_ref(), - &ctx.client.get_provider(), - ) { + if crate::compact::should_use_remote_compact_task(session.as_ref(), &ctx.provider) { let _ = session.services.otel_manager.counter( "codex.task.compact", 1, diff --git a/codex-rs/core/src/tasks/mod.rs b/codex-rs/core/src/tasks/mod.rs index d5d6f6058..0ab6e2f49 100644 --- a/codex-rs/core/src/tasks/mod.rs +++ b/codex-rs/core/src/tasks/mod.rs @@ -158,8 +158,7 @@ impl Session { }; let timer = turn_context - .client - .get_otel_manager() + .otel_manager .start_timer("codex.turn.e2e_duration_ms", &[]) .ok(); diff --git a/codex-rs/core/src/tasks/review.rs b/codex-rs/core/src/tasks/review.rs index cfc554f85..87d6c4193 100644 --- a/codex-rs/core/src/tasks/review.rs +++ b/codex-rs/core/src/tasks/review.rs @@ -82,7 +82,7 @@ async fn start_review_conversation( input: Vec, cancellation_token: CancellationToken, ) -> Option> { - let config = ctx.client.config(); + let config = ctx.config.clone(); let mut sub_agent_config = config.as_ref().clone(); // Carry over review-only feature restrictions so the delegate cannot // re-enable blocked tools (web search, view image). @@ -94,7 +94,7 @@ async fn start_review_conversation( let model = config .review_model .clone() - .unwrap_or_else(|| ctx.client.get_model()); + .unwrap_or_else(|| ctx.model_info.slug.clone()); sub_agent_config.model = Some(model); (run_codex_thread_one_shot( sub_agent_config, diff --git a/codex-rs/core/src/tasks/user_shell.rs b/codex-rs/core/src/tasks/user_shell.rs index a3d38afd2..6c37cb150 100644 --- a/codex-rs/core/src/tasks/user_shell.rs +++ b/codex-rs/core/src/tasks/user_shell.rs @@ -66,7 +66,7 @@ impl SessionTask for UserShellCommandTask { .counter("codex.task.user_shell", 1, &[]); let event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), + model_context_window: turn_context.model_context_window(), collaboration_mode_kind: turn_context.collaboration_mode.mode, }); let session = session.clone_session(); diff --git a/codex-rs/core/src/tools/handlers/collab.rs b/codex-rs/core/src/tools/handlers/collab.rs index 61ccc1932..7ef19506d 100644 --- a/codex-rs/core/src/tools/handlers/collab.rs +++ b/codex-rs/core/src/tools/handlers/collab.rs @@ -114,7 +114,7 @@ mod spawn { "Empty message can't be sent to an agent".to_string(), )); } - let session_source = turn.client.get_session_source(); + let session_source = turn.session_source.clone(); let child_depth = next_thread_spawn_depth(&session_source); if exceeds_thread_spawn_depth_limit(child_depth) { return Err(FunctionCallError::RespondToModel( @@ -593,13 +593,13 @@ fn build_agent_spawn_config( turn: &TurnContext, child_depth: i32, ) -> Result { - let base_config = turn.client.config(); + let base_config = turn.config.clone(); let mut config = (*base_config).clone(); config.base_instructions = Some(base_instructions.text.clone()); - config.model = Some(turn.client.get_model()); - config.model_provider = turn.client.get_provider(); - config.model_reasoning_effort = turn.client.get_reasoning_effort(); - config.model_reasoning_summary = turn.client.get_reasoning_summary(); + config.model = Some(turn.model_info.slug.clone()); + config.model_provider = turn.provider.clone(); + config.model_reasoning_effort = turn.reasoning_effort; + config.model_reasoning_summary = turn.reasoning_summary; config.developer_instructions = turn.developer_instructions.clone(); config.compact_prompt = turn.compact_prompt.clone(); config.shell_environment_policy = turn.shell_environment_policy.clone(); @@ -633,7 +633,6 @@ mod tests { use crate::ThreadManager; use crate::agent::MAX_THREAD_SPAWN_DEPTH; use crate::built_in_model_providers; - use crate::client::ModelClient; use crate::codex::make_session_and_context; use crate::config::types::ShellEnvironmentPolicy; use crate::function_tool::FunctionCallError; @@ -767,22 +766,10 @@ mod tests { let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn { + turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn { parent_thread_id: session.conversation_id, depth: MAX_THREAD_SPAWN_DEPTH, }); - turn.client = ModelClient::new( - turn.client.config(), - Some(session.services.auth_manager.clone()), - turn.client.get_model_info(), - turn.client.get_otel_manager(), - turn.client.get_provider(), - turn.client.get_reasoning_effort(), - turn.client.get_reasoning_summary(), - session.conversation_id, - session_source, - session.services.transport_manager.clone(), - ); let invocation = invocation( Arc::new(session), @@ -865,7 +852,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let invocation = invocation( @@ -1008,7 +995,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let invocation = invocation( @@ -1053,7 +1040,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let invocation = invocation( @@ -1084,7 +1071,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let mut status_rx = manager @@ -1138,7 +1125,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let status_before = manager.agent_control().get_status(agent_id).await; @@ -1193,12 +1180,12 @@ mod tests { turn.sandbox_policy = SandboxPolicy::DangerFullAccess; let config = build_agent_spawn_config(&base_instructions, &turn, 0).expect("spawn config"); - let mut expected = (*turn.client.config()).clone(); + let mut expected = (*turn.config).clone(); expected.base_instructions = Some(base_instructions.text); - expected.model = Some(turn.client.get_model()); - expected.model_provider = turn.client.get_provider(); - expected.model_reasoning_effort = turn.client.get_reasoning_effort(); - expected.model_reasoning_summary = turn.client.get_reasoning_summary(); + expected.model = Some(turn.model_info.slug.clone()); + expected.model_provider = turn.provider.clone(); + expected.model_reasoning_effort = turn.reasoning_effort; + expected.model_reasoning_summary = turn.reasoning_summary; expected.developer_instructions = turn.developer_instructions.clone(); expected.compact_prompt = turn.compact_prompt.clone(); expected.shell_environment_policy = turn.shell_environment_policy.clone(); @@ -1217,24 +1204,11 @@ mod tests { #[tokio::test] async fn build_agent_spawn_config_preserves_base_user_instructions() { - let (session, mut turn) = make_session_and_context().await; - let session_source = turn.client.get_session_source(); - let mut base_config = (*turn.client.config()).clone(); + let (_session, mut turn) = make_session_and_context().await; + let mut base_config = (*turn.config).clone(); base_config.user_instructions = Some("base-user".to_string()); turn.user_instructions = Some("resolved-user".to_string()); - let transport_manager = turn.client.transport_manager(); - turn.client = ModelClient::new( - Arc::new(base_config.clone()), - Some(session.services.auth_manager.clone()), - turn.client.get_model_info(), - turn.client.get_otel_manager(), - turn.client.get_provider(), - turn.client.get_reasoning_effort(), - turn.client.get_reasoning_summary(), - session.conversation_id, - session_source, - transport_manager, - ); + turn.config = Arc::new(base_config.clone()); let base_instructions = BaseInstructions { text: "base".to_string(), }; diff --git a/codex-rs/core/src/tools/orchestrator.rs b/codex-rs/core/src/tools/orchestrator.rs index e9fdd6208..fb2150953 100644 --- a/codex-rs/core/src/tools/orchestrator.rs +++ b/codex-rs/core/src/tools/orchestrator.rs @@ -43,7 +43,7 @@ impl ToolOrchestrator { where T: ToolRuntime, { - let otel = turn_ctx.client.get_otel_manager(); + let otel = turn_ctx.otel_manager.clone(); let otel_tn = &tool_ctx.tool_name; let otel_ci = &tool_ctx.call_id; let otel_user = ToolDecisionSource::User; diff --git a/codex-rs/core/src/tools/registry.rs b/codex-rs/core/src/tools/registry.rs index 72b848e31..f67db6394 100644 --- a/codex-rs/core/src/tools/registry.rs +++ b/codex-rs/core/src/tools/registry.rs @@ -70,7 +70,7 @@ impl ToolRegistry { ) -> Result { let tool_name = invocation.tool_name.clone(); let call_id_owned = invocation.call_id.clone(); - let otel = invocation.turn.client.get_otel_manager(); + let otel = invocation.turn.otel_manager.clone(); let payload_for_response = invocation.payload.clone(); let log_payload = payload_for_response.log_payload();