Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions codex-rs/core/src/tools/code_mode/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;

use codex_code_mode::CodeModeTurnHost;
use codex_code_mode::ExecuteRequest;
use codex_code_mode::RuntimeResponse;
use codex_code_mode::WaitOutcome;
use codex_code_mode::WaitRequest;
use serde_json::Value as JsonValue;

/// Host-facing execution boundary for code-mode runtimes.
///
/// Implementations own how JavaScript execution is reached, while callers keep
/// the model-facing host behavior in `codex-core`. The initial implementation is
/// in-process; later implementations can forward the same requests to a child
/// process without changing the call sites that manage turns.
pub(super) trait CodeModeBackend: Send + Sync {
fn allocate_cell_id(&self) -> String;

fn execute(
&self,
request: ExecuteRequest,
) -> Pin<Box<dyn Future<Output = Result<RuntimeResponse, String>> + Send + '_>>;

fn wait(
&self,
request: WaitRequest,
) -> Pin<Box<dyn Future<Output = Result<WaitOutcome, String>> + Send + '_>>;

fn start_turn_worker(&self, host: Arc<dyn CodeModeTurnHost>) -> CodeModeTurnWorker;
}

/// Opaque turn-scoped worker guard returned by code-mode backends.
///
/// The host only needs to keep this guard alive until the turn ends. Hiding the
/// concrete type keeps the public host seam independent of whether the backend
/// is local or process-backed.
pub(crate) struct CodeModeTurnWorker {
_inner: Box<dyn TurnWorkerHandle>,
}

impl CodeModeTurnWorker {
fn new(inner: impl TurnWorkerHandle + 'static) -> Self {
Self {
_inner: Box::new(inner),
}
}
}

trait TurnWorkerHandle: Send {}

impl<T> TurnWorkerHandle for T where T: Send {}

pub(super) struct InProcessCodeModeBackend {
inner: codex_code_mode::CodeModeService,
}

impl InProcessCodeModeBackend {
pub(super) fn new() -> Self {
Self {
inner: codex_code_mode::CodeModeService::new(),
}
}
}

impl Default for InProcessCodeModeBackend {
fn default() -> Self {
Self::new()
}
}

impl CodeModeBackend for InProcessCodeModeBackend {
fn allocate_cell_id(&self) -> String {
self.inner.allocate_cell_id()
}

fn execute(
&self,
request: ExecuteRequest,
) -> Pin<Box<dyn Future<Output = Result<RuntimeResponse, String>> + Send + '_>> {
Box::pin(self.inner.execute(request))
}

fn wait(
&self,
request: WaitRequest,
) -> Pin<Box<dyn Future<Output = Result<WaitOutcome, String>> + Send + '_>> {
Box::pin(self.inner.wait(request))
}

fn start_turn_worker(&self, host: Arc<dyn CodeModeTurnHost>) -> CodeModeTurnWorker {
CodeModeTurnWorker::new(self.inner.start_turn_worker(host))
}
}

pub(super) type StoredValues = HashMap<String, JsonValue>;
33 changes: 19 additions & 14 deletions codex-rs/core/src/tools/code_mode/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod backend;
mod execute_handler;
pub(crate) mod execute_spec;
mod response_adapter;
Expand All @@ -8,13 +9,18 @@ use std::collections::HashSet;
use std::sync::Arc;
use std::time::Duration;

use backend::CodeModeBackend;
use backend::CodeModeTurnWorker;
use backend::InProcessCodeModeBackend;
use backend::StoredValues;
use codex_code_mode::CodeModeNestedToolCall;
use codex_code_mode::CodeModeTurnHost;
use codex_code_mode::RuntimeResponse;
use codex_protocol::models::FunctionCallOutputContentItem;
use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::ResponseInputItem;
use serde_json::Value as JsonValue;
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;

use crate::function_tool::FunctionCallError;
Expand Down Expand Up @@ -59,43 +65,42 @@ pub(crate) struct ExecContext {
}

pub(crate) struct CodeModeService {
inner: codex_code_mode::CodeModeService,
backend: Arc<dyn CodeModeBackend>,
stored_values: Mutex<StoredValues>,
}

impl CodeModeService {
pub(crate) fn new() -> Self {
Self {
inner: codex_code_mode::CodeModeService::new(),
backend: Arc::new(InProcessCodeModeBackend::new()),
stored_values: Mutex::new(StoredValues::new()),
}
}

pub(crate) async fn stored_values(&self) -> std::collections::HashMap<String, JsonValue> {
self.inner.stored_values().await
pub(crate) async fn stored_values(&self) -> StoredValues {
self.stored_values.lock().await.clone()
}

pub(crate) async fn replace_stored_values(
&self,
values: std::collections::HashMap<String, JsonValue>,
) {
self.inner.replace_stored_values(values).await;
pub(crate) async fn replace_stored_values(&self, values: StoredValues) {
*self.stored_values.lock().await = values;
}

pub(crate) fn allocate_cell_id(&self) -> String {
self.inner.allocate_cell_id()
self.backend.allocate_cell_id()
}

pub(crate) async fn execute(
&self,
request: codex_code_mode::ExecuteRequest,
) -> Result<RuntimeResponse, String> {
self.inner.execute(request).await
self.backend.execute(request).await
}

pub(crate) async fn wait(
&self,
request: codex_code_mode::WaitRequest,
) -> Result<codex_code_mode::WaitOutcome, String> {
self.inner.wait(request).await
self.backend.wait(request).await
}

pub(crate) async fn start_turn_worker(
Expand All @@ -104,7 +109,7 @@ impl CodeModeService {
turn: &Arc<TurnContext>,
router: Arc<ToolRouter>,
tracker: SharedTurnDiffTracker,
) -> Option<codex_code_mode::CodeModeTurnWorker> {
) -> Option<CodeModeTurnWorker> {
if !turn.features.enabled(Feature::CodeMode) {
return None;
}
Expand All @@ -116,7 +121,7 @@ impl CodeModeService {
let tool_runtime =
ToolCallRuntime::new(router, Arc::clone(session), Arc::clone(turn), tracker);
let host = Arc::new(CoreTurnHost { exec, tool_runtime });
Some(self.inner.start_turn_worker(host))
Some(self.backend.start_turn_worker(host))
}
}

Expand Down
Loading