mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
code-mode standalone: extract protocol and add host crate (#27724)
This is phase 1 of a 4 phase stack: 1. **Add protocol and host crates for new IPC code mode implementation** 2. Create the new standalone binary 3. Create a new IPC `CodeModeSessionProvider` to use new binary 4. Remove v8 from core and only use IPC provider ## Add protocol and host crates for new IPC code mode implementation Establish a clean process boundary without changing the existing in-process behavior. - Add the codex-code-mode-protocol crate for shared session, runtime, response, and tool-definition types. - Move protocol-facing code out of the V8-backed implementation. - Add a buildable codex-code-mode-host crate as the foundation for the standalone process. - Keep the existing in-process runtime as the active implementation.
This commit is contained in:
1 parent
216ce03031
commit
aa46f2debf
19 files changed
+407
-282
No files matched your search
Generated
+17
-1
@@ -2479,10 +2479,10 @@ dependencies = [
|
|||||||
name = "codex-code-mode"
|
name = "codex-code-mode"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"codex-code-mode-protocol",
|
||||||
"codex-protocol",
|
"codex-protocol",
|
||||||
"deno_core_icudata",
|
"deno_core_icudata",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@@ -2490,6 +2490,22 @@ dependencies = [
|
|||||||
"v8",
|
"v8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codex-code-mode-host"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codex-code-mode-protocol"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"codex-protocol",
|
||||||
|
"pretty_assertions",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codex-collaboration-mode-templates"
|
name = "codex-collaboration-mode-templates"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ members = [
|
|||||||
"install-context",
|
"install-context",
|
||||||
"codex-backend-openapi-models",
|
"codex-backend-openapi-models",
|
||||||
"code-mode",
|
"code-mode",
|
||||||
|
"code-mode-host",
|
||||||
|
"code-mode-protocol",
|
||||||
"codex-home",
|
"codex-home",
|
||||||
"cloud-config",
|
"cloud-config",
|
||||||
"cloud-tasks",
|
"cloud-tasks",
|
||||||
@@ -158,6 +160,7 @@ codex-cloud-config = { path = "cloud-config" }
|
|||||||
codex-cloud-tasks-client = { path = "cloud-tasks-client" }
|
codex-cloud-tasks-client = { path = "cloud-tasks-client" }
|
||||||
codex-cloud-tasks-mock-client = { path = "cloud-tasks-mock-client" }
|
codex-cloud-tasks-mock-client = { path = "cloud-tasks-mock-client" }
|
||||||
codex-code-mode = { path = "code-mode" }
|
codex-code-mode = { path = "code-mode" }
|
||||||
|
codex-code-mode-protocol = { path = "code-mode-protocol" }
|
||||||
codex-home = { path = "codex-home" }
|
codex-home = { path = "codex-home" }
|
||||||
codex-config = { path = "config" }
|
codex-config = { path = "config" }
|
||||||
codex-connectors = { path = "connectors" }
|
codex-connectors = { path = "connectors" }
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
load("//:defs.bzl", "codex_rust_crate")
|
||||||
|
|
||||||
|
codex_rust_crate(
|
||||||
|
name = "code-mode-host",
|
||||||
|
crate_name = "codex_code_mode_host",
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "codex-code-mode-host"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "codex-code-mode-host"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fn main() {}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
load("//:defs.bzl", "codex_rust_crate")
|
||||||
|
|
||||||
|
codex_rust_crate(
|
||||||
|
name = "code-mode-protocol",
|
||||||
|
crate_name = "codex_code_mode_protocol",
|
||||||
|
)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
name = "codex-code-mode-protocol"
|
||||||
|
version.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
doctest = false
|
||||||
|
name = "codex_code_mode_protocol"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codex-protocol = { workspace = true }
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
tokio = { workspace = true, features = ["io-util", "sync"] }
|
||||||
|
tokio-util = { workspace = true, features = ["rt"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions = { workspace = true }
|
||||||
|
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
|
||||||
+1
-1
@@ -128,7 +128,7 @@ pub enum CodeModeToolKind {
|
|||||||
Freeform,
|
Freeform,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
pub struct ToolDefinition {
|
pub struct ToolDefinition {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub tool_name: ToolName,
|
pub tool_name: ToolName,
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
mod description;
|
||||||
|
mod response;
|
||||||
|
mod runtime;
|
||||||
|
mod session;
|
||||||
|
|
||||||
|
pub use description::CODE_MODE_PRAGMA_PREFIX;
|
||||||
|
pub use description::CodeModeToolKind;
|
||||||
|
pub use description::EnabledToolMetadata;
|
||||||
|
pub use description::ToolDefinition;
|
||||||
|
pub use description::ToolNamespaceDescription;
|
||||||
|
pub use description::augment_tool_definition;
|
||||||
|
pub use description::build_exec_tool_description;
|
||||||
|
pub use description::build_wait_tool_description;
|
||||||
|
pub use description::enabled_tool_metadata;
|
||||||
|
pub use description::is_code_mode_nested_tool;
|
||||||
|
pub use description::normalize_code_mode_identifier;
|
||||||
|
pub use description::parse_exec_source;
|
||||||
|
pub use description::render_code_mode_sample;
|
||||||
|
pub use description::render_json_schema_to_typescript;
|
||||||
|
pub use response::DEFAULT_IMAGE_DETAIL;
|
||||||
|
pub use response::FunctionCallOutputContentItem;
|
||||||
|
pub use response::ImageDetail;
|
||||||
|
pub use runtime::CodeModeNestedToolCall;
|
||||||
|
pub use runtime::DEFAULT_EXEC_YIELD_TIME_MS;
|
||||||
|
pub use runtime::DEFAULT_MAX_OUTPUT_TOKENS_PER_EXEC_CALL;
|
||||||
|
pub use runtime::DEFAULT_WAIT_YIELD_TIME_MS;
|
||||||
|
pub use runtime::ExecuteRequest;
|
||||||
|
pub use runtime::ExecuteToPendingOutcome;
|
||||||
|
pub use runtime::RuntimeResponse;
|
||||||
|
pub use runtime::WaitOutcome;
|
||||||
|
pub use runtime::WaitRequest;
|
||||||
|
pub use runtime::WaitToPendingOutcome;
|
||||||
|
pub use runtime::WaitToPendingRequest;
|
||||||
|
pub use session::CellId;
|
||||||
|
pub use session::CodeModeSession;
|
||||||
|
pub use session::CodeModeSessionDelegate;
|
||||||
|
pub use session::CodeModeSessionProvider;
|
||||||
|
pub use session::CodeModeSessionProviderFuture;
|
||||||
|
pub use session::CodeModeSessionResultFuture;
|
||||||
|
pub use session::NotificationFuture;
|
||||||
|
pub use session::StartedCell;
|
||||||
|
pub use session::ToolInvocationFuture;
|
||||||
|
|
||||||
|
pub const PUBLIC_TOOL_NAME: &str = "exec";
|
||||||
|
pub const WAIT_TOOL_NAME: &str = "wait";
|
||||||
File renamed without changes.
@@ -0,0 +1,89 @@
|
|||||||
|
use codex_protocol::ToolName;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
|
use crate::CellId;
|
||||||
|
use crate::CodeModeToolKind;
|
||||||
|
use crate::FunctionCallOutputContentItem;
|
||||||
|
use crate::ToolDefinition;
|
||||||
|
|
||||||
|
pub const DEFAULT_EXEC_YIELD_TIME_MS: u64 = 10_000;
|
||||||
|
pub const DEFAULT_WAIT_YIELD_TIME_MS: u64 = 10_000;
|
||||||
|
pub const DEFAULT_MAX_OUTPUT_TOKENS_PER_EXEC_CALL: usize = 10_000;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub struct ExecuteRequest {
|
||||||
|
pub tool_call_id: String,
|
||||||
|
pub enabled_tools: Vec<ToolDefinition>,
|
||||||
|
pub source: String,
|
||||||
|
pub yield_time_ms: Option<u64>,
|
||||||
|
pub max_output_tokens: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub struct WaitRequest {
|
||||||
|
pub cell_id: CellId,
|
||||||
|
pub yield_time_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub struct WaitToPendingRequest {
|
||||||
|
pub cell_id: CellId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub enum WaitOutcome {
|
||||||
|
LiveCell(RuntimeResponse),
|
||||||
|
MissingCell(RuntimeResponse),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub enum ExecuteToPendingOutcome {
|
||||||
|
Pending {
|
||||||
|
cell_id: CellId,
|
||||||
|
content_items: Vec<FunctionCallOutputContentItem>,
|
||||||
|
pending_tool_call_ids: Vec<String>,
|
||||||
|
},
|
||||||
|
Completed(RuntimeResponse),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub enum WaitToPendingOutcome {
|
||||||
|
LiveCell(ExecuteToPendingOutcome),
|
||||||
|
MissingCell(RuntimeResponse),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WaitOutcome> for RuntimeResponse {
|
||||||
|
fn from(outcome: WaitOutcome) -> Self {
|
||||||
|
match outcome {
|
||||||
|
WaitOutcome::LiveCell(response) | WaitOutcome::MissingCell(response) => response,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub enum RuntimeResponse {
|
||||||
|
Yielded {
|
||||||
|
cell_id: CellId,
|
||||||
|
content_items: Vec<FunctionCallOutputContentItem>,
|
||||||
|
},
|
||||||
|
Terminated {
|
||||||
|
cell_id: CellId,
|
||||||
|
content_items: Vec<FunctionCallOutputContentItem>,
|
||||||
|
},
|
||||||
|
Result {
|
||||||
|
cell_id: CellId,
|
||||||
|
content_items: Vec<FunctionCallOutputContentItem>,
|
||||||
|
error_text: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
|
pub struct CodeModeNestedToolCall {
|
||||||
|
pub cell_id: CellId,
|
||||||
|
pub runtime_tool_call_id: String,
|
||||||
|
pub tool_name: ToolName,
|
||||||
|
pub tool_kind: CodeModeToolKind,
|
||||||
|
pub input: Option<JsonValue>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
use crate::CodeModeNestedToolCall;
|
||||||
|
use crate::ExecuteRequest;
|
||||||
|
use crate::RuntimeResponse;
|
||||||
|
use crate::WaitOutcome;
|
||||||
|
use crate::WaitRequest;
|
||||||
|
|
||||||
|
pub type CodeModeSessionResultFuture<'a, T> =
|
||||||
|
Pin<Box<dyn Future<Output = Result<T, String>> + Send + 'a>>;
|
||||||
|
pub type CodeModeSessionProviderFuture<'a> =
|
||||||
|
CodeModeSessionResultFuture<'a, Arc<dyn CodeModeSession>>;
|
||||||
|
pub type ToolInvocationFuture<'a> =
|
||||||
|
Pin<Box<dyn Future<Output = Result<JsonValue, String>> + Send + 'a>>;
|
||||||
|
pub type NotificationFuture<'a> = Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'a>>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
pub struct CellId(String);
|
||||||
|
|
||||||
|
impl CellId {
|
||||||
|
pub fn new(value: String) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for CellId {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CellId {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
formatter.write_str(self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StartedCell {
|
||||||
|
pub cell_id: CellId,
|
||||||
|
initial_response: CodeModeSessionResultFuture<'static, RuntimeResponse>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StartedCell {
|
||||||
|
pub fn new(cell_id: CellId, initial_response_rx: oneshot::Receiver<RuntimeResponse>) -> Self {
|
||||||
|
Self {
|
||||||
|
cell_id,
|
||||||
|
initial_response: Box::pin(async move {
|
||||||
|
initial_response_rx
|
||||||
|
.await
|
||||||
|
.map_err(|_| "exec runtime ended unexpectedly".to_string())
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_result_receiver(
|
||||||
|
cell_id: CellId,
|
||||||
|
initial_response_rx: oneshot::Receiver<Result<RuntimeResponse, String>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
cell_id,
|
||||||
|
initial_response: Box::pin(async move {
|
||||||
|
initial_response_rx
|
||||||
|
.await
|
||||||
|
.map_err(|_| "exec runtime ended unexpectedly".to_string())?
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initial_response(self) -> Result<RuntimeResponse, String> {
|
||||||
|
self.initial_response.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Host callbacks used by a code-mode session while cells are executing.
|
||||||
|
pub trait CodeModeSessionDelegate: Send + Sync {
|
||||||
|
fn invoke_tool<'a>(
|
||||||
|
&'a self,
|
||||||
|
invocation: CodeModeNestedToolCall,
|
||||||
|
cancellation_token: CancellationToken,
|
||||||
|
) -> ToolInvocationFuture<'a>;
|
||||||
|
|
||||||
|
fn notify<'a>(
|
||||||
|
&'a self,
|
||||||
|
call_id: String,
|
||||||
|
cell_id: CellId,
|
||||||
|
text: String,
|
||||||
|
cancellation_token: CancellationToken,
|
||||||
|
) -> NotificationFuture<'a>;
|
||||||
|
|
||||||
|
/// Releases delegate state associated with a cell after it reaches a terminal state.
|
||||||
|
fn cell_closed(&self, cell_id: &CellId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A durable code-mode session owned by one Codex thread.
|
||||||
|
///
|
||||||
|
/// Cells executed in the same session share stored values. Separate sessions
|
||||||
|
/// must keep those values isolated. Implementations may execute cells
|
||||||
|
/// in-process or remotely.
|
||||||
|
pub trait CodeModeSession: Send + Sync {
|
||||||
|
/// Returns whether the session can still accept requests.
|
||||||
|
///
|
||||||
|
/// Remote implementations should return `false` after their underlying
|
||||||
|
/// connection fails so callers can create a fresh session for later work.
|
||||||
|
fn is_alive(&self) -> bool;
|
||||||
|
|
||||||
|
fn execute<'a>(
|
||||||
|
&'a self,
|
||||||
|
request: ExecuteRequest,
|
||||||
|
) -> CodeModeSessionResultFuture<'a, StartedCell>;
|
||||||
|
|
||||||
|
fn wait<'a>(&'a self, request: WaitRequest) -> CodeModeSessionResultFuture<'a, WaitOutcome>;
|
||||||
|
|
||||||
|
fn terminate<'a>(&'a self, cell_id: CellId) -> CodeModeSessionResultFuture<'a, WaitOutcome>;
|
||||||
|
|
||||||
|
fn shutdown<'a>(&'a self) -> CodeModeSessionResultFuture<'a, ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates code-mode sessions for Codex threads.
|
||||||
|
///
|
||||||
|
/// Implementations may share a remote host process across all sessions created
|
||||||
|
/// by one provider.
|
||||||
|
pub trait CodeModeSessionProvider: Send + Sync {
|
||||||
|
fn create_session<'a>(
|
||||||
|
&'a self,
|
||||||
|
delegate: Arc<dyn CodeModeSessionDelegate>,
|
||||||
|
) -> CodeModeSessionProviderFuture<'a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "session_tests.rs"]
|
||||||
|
mod tests;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
|
use super::CellId;
|
||||||
|
use super::StartedCell;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn started_cell_preserves_remote_initial_response_errors() {
|
||||||
|
let (response_tx, response_rx) = oneshot::channel();
|
||||||
|
response_tx
|
||||||
|
.send(Err("remote runtime failed".to_string()))
|
||||||
|
.expect("initial response receiver should be open");
|
||||||
|
let started = StartedCell::from_result_receiver(CellId::new("1".to_string()), response_rx);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
started.initial_response().await,
|
||||||
|
Err("remote runtime failed".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -16,9 +16,9 @@ sandbox = ["v8/v8_enable_sandbox"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
codex-code-mode-protocol = { workspace = true }
|
||||||
codex-protocol = { workspace = true }
|
codex-protocol = { workspace = true }
|
||||||
deno_core_icudata = { workspace = true }
|
deno_core_icudata = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["macros", "rt", "sync", "time"] }
|
tokio = { workspace = true, features = ["macros", "rt", "sync", "time"] }
|
||||||
tokio-util = { workspace = true, features = ["rt"] }
|
tokio-util = { workspace = true, features = ["rt"] }
|
||||||
|
|||||||
@@ -1,46 +1,7 @@
|
|||||||
mod description;
|
|
||||||
mod response;
|
|
||||||
mod runtime;
|
mod runtime;
|
||||||
mod service;
|
mod service;
|
||||||
|
|
||||||
pub use description::CODE_MODE_PRAGMA_PREFIX;
|
pub use codex_code_mode_protocol::*;
|
||||||
pub use description::CodeModeToolKind;
|
|
||||||
pub use description::ToolDefinition;
|
|
||||||
pub use description::ToolNamespaceDescription;
|
|
||||||
pub use description::augment_tool_definition;
|
|
||||||
pub use description::build_exec_tool_description;
|
|
||||||
pub use description::build_wait_tool_description;
|
|
||||||
pub use description::is_code_mode_nested_tool;
|
|
||||||
pub use description::normalize_code_mode_identifier;
|
|
||||||
pub use description::parse_exec_source;
|
|
||||||
pub use description::render_code_mode_sample;
|
|
||||||
pub use description::render_json_schema_to_typescript;
|
|
||||||
pub use response::DEFAULT_IMAGE_DETAIL;
|
|
||||||
pub use response::FunctionCallOutputContentItem;
|
|
||||||
pub use response::ImageDetail;
|
|
||||||
pub use runtime::CodeModeNestedToolCall;
|
|
||||||
pub use runtime::DEFAULT_EXEC_YIELD_TIME_MS;
|
|
||||||
pub use runtime::DEFAULT_MAX_OUTPUT_TOKENS_PER_EXEC_CALL;
|
|
||||||
pub use runtime::DEFAULT_WAIT_YIELD_TIME_MS;
|
|
||||||
pub use runtime::ExecuteRequest;
|
|
||||||
pub use runtime::ExecuteToPendingOutcome;
|
|
||||||
pub use runtime::RuntimeResponse;
|
|
||||||
pub use runtime::WaitOutcome;
|
|
||||||
pub use runtime::WaitRequest;
|
|
||||||
pub use runtime::WaitToPendingOutcome;
|
|
||||||
pub use runtime::WaitToPendingRequest;
|
|
||||||
pub use service::CellId;
|
|
||||||
pub use service::CodeModeService;
|
pub use service::CodeModeService;
|
||||||
pub use service::CodeModeSession;
|
|
||||||
pub use service::CodeModeSessionDelegate;
|
|
||||||
pub use service::CodeModeSessionProvider;
|
|
||||||
pub use service::CodeModeSessionProviderFuture;
|
|
||||||
pub use service::CodeModeSessionResultFuture;
|
|
||||||
pub use service::InProcessCodeModeSessionProvider;
|
pub use service::InProcessCodeModeSessionProvider;
|
||||||
pub use service::NoopCodeModeSessionDelegate;
|
pub use service::NoopCodeModeSessionDelegate;
|
||||||
pub use service::NotificationFuture;
|
|
||||||
pub use service::StartedCell;
|
|
||||||
pub use service::ToolInvocationFuture;
|
|
||||||
|
|
||||||
pub const PUBLIC_TOOL_NAME: &str = "exec";
|
|
||||||
pub const WAIT_TOOL_NAME: &str = "wait";
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::response::FunctionCallOutputContentItem;
|
use codex_code_mode_protocol::FunctionCallOutputContentItem;
|
||||||
|
|
||||||
use super::EXIT_SENTINEL;
|
use super::EXIT_SENTINEL;
|
||||||
use super::RuntimeEvent;
|
use super::RuntimeEvent;
|
||||||
|
|||||||
@@ -9,125 +9,17 @@ use std::sync::OnceLock;
|
|||||||
use std::sync::mpsc as std_mpsc;
|
use std::sync::mpsc as std_mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
use codex_code_mode_protocol::CodeModeToolKind;
|
||||||
|
use codex_code_mode_protocol::EnabledToolMetadata;
|
||||||
|
use codex_code_mode_protocol::ExecuteRequest;
|
||||||
|
use codex_code_mode_protocol::FunctionCallOutputContentItem;
|
||||||
|
use codex_code_mode_protocol::enabled_tool_metadata;
|
||||||
use codex_protocol::ToolName;
|
use codex_protocol::ToolName;
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::description::CodeModeToolKind;
|
|
||||||
use crate::description::EnabledToolMetadata;
|
|
||||||
use crate::description::ToolDefinition;
|
|
||||||
use crate::description::enabled_tool_metadata;
|
|
||||||
use crate::response::FunctionCallOutputContentItem;
|
|
||||||
use crate::service::CellId;
|
|
||||||
|
|
||||||
pub const DEFAULT_EXEC_YIELD_TIME_MS: u64 = 10_000;
|
|
||||||
pub const DEFAULT_WAIT_YIELD_TIME_MS: u64 = 10_000;
|
|
||||||
pub const DEFAULT_MAX_OUTPUT_TOKENS_PER_EXEC_CALL: usize = 10_000;
|
|
||||||
const EXIT_SENTINEL: &str = "__codex_code_mode_exit__";
|
const EXIT_SENTINEL: &str = "__codex_code_mode_exit__";
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ExecuteRequest {
|
|
||||||
pub tool_call_id: String,
|
|
||||||
pub enabled_tools: Vec<ToolDefinition>,
|
|
||||||
pub source: String,
|
|
||||||
pub yield_time_ms: Option<u64>,
|
|
||||||
pub max_output_tokens: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct WaitRequest {
|
|
||||||
pub cell_id: CellId,
|
|
||||||
pub yield_time_ms: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct WaitToPendingRequest {
|
|
||||||
pub cell_id: CellId,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of waiting on a code-mode cell.
|
|
||||||
///
|
|
||||||
/// The wrapped `RuntimeResponse` is the model-facing wait result. The enum
|
|
||||||
/// variant carries the extra lifecycle provenance that `RuntimeResponse` cannot:
|
|
||||||
/// a failed real cell and a missing-cell wait both use
|
|
||||||
/// `RuntimeResponse::Result { error_text: Some(..), .. }`, but only the former
|
|
||||||
/// should be treated as a code-cell lifecycle event.
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum WaitOutcome {
|
|
||||||
/// The requested code cell was live when the wait command was accepted.
|
|
||||||
LiveCell(RuntimeResponse),
|
|
||||||
/// The requested code cell was not live.
|
|
||||||
MissingCell(RuntimeResponse),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of executing a code-mode cell until it either completes or reaches a
|
|
||||||
/// quiescent pending state.
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum ExecuteToPendingOutcome {
|
|
||||||
/// The cell is waiting for more runtime input after draining the runtime
|
|
||||||
/// input queue that was ready at the pending boundary.
|
|
||||||
Pending {
|
|
||||||
cell_id: CellId,
|
|
||||||
content_items: Vec<FunctionCallOutputContentItem>,
|
|
||||||
/// Runtime tool-call ids emitted before this paused execution frontier
|
|
||||||
/// sealed. Hosts can use these ids to drain their tool-call transport
|
|
||||||
/// before surfacing the pending boundary to callers.
|
|
||||||
pending_tool_call_ids: Vec<String>,
|
|
||||||
},
|
|
||||||
/// The cell reached a terminal runtime response before going pending.
|
|
||||||
Completed(RuntimeResponse),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of resuming a live code-mode cell until it completes or becomes
|
|
||||||
/// quiescent again.
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum WaitToPendingOutcome {
|
|
||||||
/// The requested code cell was live when the wait command was accepted.
|
|
||||||
LiveCell(ExecuteToPendingOutcome),
|
|
||||||
/// The requested code cell was not live.
|
|
||||||
MissingCell(RuntimeResponse),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WaitOutcome> for RuntimeResponse {
|
|
||||||
fn from(outcome: WaitOutcome) -> Self {
|
|
||||||
match outcome {
|
|
||||||
WaitOutcome::LiveCell(response) | WaitOutcome::MissingCell(response) => response,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
|
||||||
pub enum RuntimeResponse {
|
|
||||||
Yielded {
|
|
||||||
cell_id: CellId,
|
|
||||||
content_items: Vec<FunctionCallOutputContentItem>,
|
|
||||||
},
|
|
||||||
Terminated {
|
|
||||||
cell_id: CellId,
|
|
||||||
content_items: Vec<FunctionCallOutputContentItem>,
|
|
||||||
},
|
|
||||||
Result {
|
|
||||||
cell_id: CellId,
|
|
||||||
content_items: Vec<FunctionCallOutputContentItem>,
|
|
||||||
error_text: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Nested tool request emitted by one code-mode cell.
|
|
||||||
///
|
|
||||||
/// Code mode owns the per-cell runtime id. Hosts should preserve it for
|
|
||||||
/// provenance/debugging, but should still assign their own runtime tool call id
|
|
||||||
/// if their tool-call graph requires globally unique ids.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CodeModeNestedToolCall {
|
|
||||||
pub cell_id: CellId,
|
|
||||||
pub runtime_tool_call_id: String,
|
|
||||||
pub tool_name: ToolName,
|
|
||||||
pub tool_kind: CodeModeToolKind,
|
|
||||||
pub input: Option<JsonValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum RuntimeCommand {
|
pub(crate) enum RuntimeCommand {
|
||||||
ToolResponse { id: String, result: JsonValue },
|
ToolResponse { id: String, result: JsonValue },
|
||||||
@@ -326,12 +218,9 @@ fn run_runtime(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut pending_promise = pending_promise;
|
let mut pending_promise = pending_promise;
|
||||||
loop {
|
while let Some(command) =
|
||||||
let Some(command) = next_runtime_command(&event_tx, &command_rx, &control_rx, pending_mode)
|
next_runtime_command(&event_tx, &command_rx, &control_rx, pending_mode)
|
||||||
else {
|
{
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
RuntimeCommand::Terminate => break,
|
RuntimeCommand::Terminate => break,
|
||||||
RuntimeCommand::ToolResponse { id, result } => {
|
RuntimeCommand::ToolResponse { id, result } => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
use crate::response::DEFAULT_IMAGE_DETAIL;
|
use codex_code_mode_protocol::DEFAULT_IMAGE_DETAIL;
|
||||||
use crate::response::FunctionCallOutputContentItem;
|
use codex_code_mode_protocol::FunctionCallOutputContentItem;
|
||||||
use crate::response::ImageDetail;
|
use codex_code_mode_protocol::ImageDetail;
|
||||||
|
|
||||||
const IMAGE_HELPER_EXPECTS_MESSAGE: &str = "image expects a non-empty image URL string, an object with image_url and optional detail, or a raw MCP image block";
|
const IMAGE_HELPER_EXPECTS_MESSAGE: &str = "image expects a non-empty image URL string, an object with image_url and optional detail, or a raw MCP image block";
|
||||||
const CODEX_IMAGE_DETAIL_META_KEY: &str = "codex/imageDetail";
|
const CODEX_IMAGE_DETAIL_META_KEY: &str = "codex/imageDetail";
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
|
||||||
use std::future::Future;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use codex_code_mode_protocol::CellId;
|
||||||
use serde::Serialize;
|
use codex_code_mode_protocol::CodeModeNestedToolCall;
|
||||||
|
use codex_code_mode_protocol::CodeModeSession;
|
||||||
|
use codex_code_mode_protocol::CodeModeSessionDelegate;
|
||||||
|
use codex_code_mode_protocol::CodeModeSessionProvider;
|
||||||
|
use codex_code_mode_protocol::CodeModeSessionProviderFuture;
|
||||||
|
use codex_code_mode_protocol::CodeModeSessionResultFuture;
|
||||||
|
use codex_code_mode_protocol::DEFAULT_EXEC_YIELD_TIME_MS;
|
||||||
|
use codex_code_mode_protocol::ExecuteRequest;
|
||||||
|
use codex_code_mode_protocol::ExecuteToPendingOutcome;
|
||||||
|
use codex_code_mode_protocol::FunctionCallOutputContentItem;
|
||||||
|
use codex_code_mode_protocol::NotificationFuture;
|
||||||
|
use codex_code_mode_protocol::RuntimeResponse;
|
||||||
|
use codex_code_mode_protocol::StartedCell;
|
||||||
|
use codex_code_mode_protocol::ToolInvocationFuture;
|
||||||
|
use codex_code_mode_protocol::WaitOutcome;
|
||||||
|
use codex_code_mode_protocol::WaitRequest;
|
||||||
|
use codex_code_mode_protocol::WaitToPendingOutcome;
|
||||||
|
use codex_code_mode_protocol::WaitToPendingRequest;
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
@@ -18,88 +32,12 @@ use tokio::task::JoinSet;
|
|||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::FunctionCallOutputContentItem;
|
|
||||||
use crate::runtime::CodeModeNestedToolCall;
|
|
||||||
use crate::runtime::DEFAULT_EXEC_YIELD_TIME_MS;
|
|
||||||
use crate::runtime::ExecuteRequest;
|
|
||||||
use crate::runtime::ExecuteToPendingOutcome;
|
|
||||||
use crate::runtime::PendingRuntimeMode;
|
use crate::runtime::PendingRuntimeMode;
|
||||||
use crate::runtime::RuntimeCommand;
|
use crate::runtime::RuntimeCommand;
|
||||||
use crate::runtime::RuntimeControlCommand;
|
use crate::runtime::RuntimeControlCommand;
|
||||||
use crate::runtime::RuntimeEvent;
|
use crate::runtime::RuntimeEvent;
|
||||||
use crate::runtime::RuntimeResponse;
|
|
||||||
use crate::runtime::WaitOutcome;
|
|
||||||
use crate::runtime::WaitRequest;
|
|
||||||
use crate::runtime::WaitToPendingOutcome;
|
|
||||||
use crate::runtime::WaitToPendingRequest;
|
|
||||||
use crate::runtime::spawn_runtime;
|
use crate::runtime::spawn_runtime;
|
||||||
|
|
||||||
pub type CodeModeSessionResultFuture<'a, T> =
|
|
||||||
Pin<Box<dyn Future<Output = Result<T, String>> + Send + 'a>>;
|
|
||||||
pub type CodeModeSessionProviderFuture<'a> =
|
|
||||||
CodeModeSessionResultFuture<'a, Arc<dyn CodeModeSession>>;
|
|
||||||
pub type ToolInvocationFuture<'a> =
|
|
||||||
Pin<Box<dyn Future<Output = Result<JsonValue, String>> + Send + 'a>>;
|
|
||||||
pub type NotificationFuture<'a> = Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'a>>;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
|
||||||
pub struct CellId(String);
|
|
||||||
|
|
||||||
impl CellId {
|
|
||||||
pub fn new(value: String) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for CellId {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
self.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for CellId {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
formatter.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StartedCell {
|
|
||||||
pub cell_id: CellId,
|
|
||||||
initial_response_rx: oneshot::Receiver<RuntimeResponse>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StartedCell {
|
|
||||||
pub async fn initial_response(self) -> Result<RuntimeResponse, String> {
|
|
||||||
self.initial_response_rx
|
|
||||||
.await
|
|
||||||
.map_err(|_| "exec runtime ended unexpectedly".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Host callbacks used by a code-mode session while cells are executing.
|
|
||||||
pub trait CodeModeSessionDelegate: Send + Sync {
|
|
||||||
fn invoke_tool<'a>(
|
|
||||||
&'a self,
|
|
||||||
invocation: CodeModeNestedToolCall,
|
|
||||||
cancellation_token: CancellationToken,
|
|
||||||
) -> ToolInvocationFuture<'a>;
|
|
||||||
|
|
||||||
fn notify<'a>(
|
|
||||||
&'a self,
|
|
||||||
call_id: String,
|
|
||||||
cell_id: CellId,
|
|
||||||
text: String,
|
|
||||||
cancellation_token: CancellationToken,
|
|
||||||
) -> NotificationFuture<'a>;
|
|
||||||
|
|
||||||
/// Releases delegate state associated with a cell after it reaches a terminal state.
|
|
||||||
fn cell_closed(&self, cell_id: &CellId);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NoopCodeModeSessionDelegate;
|
pub struct NoopCodeModeSessionDelegate;
|
||||||
|
|
||||||
impl CodeModeSessionDelegate for NoopCodeModeSessionDelegate {
|
impl CodeModeSessionDelegate for NoopCodeModeSessionDelegate {
|
||||||
@@ -127,35 +65,6 @@ impl CodeModeSessionDelegate for NoopCodeModeSessionDelegate {
|
|||||||
fn cell_closed(&self, _cell_id: &CellId) {}
|
fn cell_closed(&self, _cell_id: &CellId) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A durable code-mode session owned by one Codex thread.
|
|
||||||
///
|
|
||||||
/// Cells executed in the same session share stored values. Separate sessions
|
|
||||||
/// must keep those values isolated. Implementations may execute cells
|
|
||||||
/// in-process or remotely.
|
|
||||||
pub trait CodeModeSession: Send + Sync {
|
|
||||||
fn execute<'a>(
|
|
||||||
&'a self,
|
|
||||||
request: ExecuteRequest,
|
|
||||||
) -> CodeModeSessionResultFuture<'a, StartedCell>;
|
|
||||||
|
|
||||||
fn wait<'a>(&'a self, request: WaitRequest) -> CodeModeSessionResultFuture<'a, WaitOutcome>;
|
|
||||||
|
|
||||||
fn terminate<'a>(&'a self, cell_id: CellId) -> CodeModeSessionResultFuture<'a, WaitOutcome>;
|
|
||||||
|
|
||||||
fn shutdown<'a>(&'a self) -> CodeModeSessionResultFuture<'a, ()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates code-mode sessions for one Codex thread.
|
|
||||||
///
|
|
||||||
/// Providers choose where a session executes and receive the host delegate that
|
|
||||||
/// the session should use for nested tool calls and notifications.
|
|
||||||
pub trait CodeModeSessionProvider: Send + Sync {
|
|
||||||
fn create_session<'a>(
|
|
||||||
&'a self,
|
|
||||||
delegate: Arc<dyn CodeModeSessionDelegate>,
|
|
||||||
) -> CodeModeSessionProviderFuture<'a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InProcessCodeModeSessionProvider;
|
pub struct InProcessCodeModeSessionProvider;
|
||||||
|
|
||||||
@@ -233,10 +142,7 @@ impl CodeModeService {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(StartedCell {
|
Ok(StartedCell::new(cell_id, response_rx))
|
||||||
cell_id,
|
|
||||||
initial_response_rx: response_rx,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute_to_pending(
|
pub async fn execute_to_pending(
|
||||||
@@ -432,6 +338,10 @@ impl Drop for CodeModeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CodeModeSession for CodeModeService {
|
impl CodeModeSession for CodeModeService {
|
||||||
|
fn is_alive(&self) -> bool {
|
||||||
|
!self.inner.shutting_down.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
fn execute<'a>(
|
fn execute<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
request: ExecuteRequest,
|
request: ExecuteRequest,
|
||||||
@@ -876,10 +786,10 @@ mod tests {
|
|||||||
use super::WaitToPendingRequest;
|
use super::WaitToPendingRequest;
|
||||||
use super::run_cell_control;
|
use super::run_cell_control;
|
||||||
use crate::CodeModeToolKind;
|
use crate::CodeModeToolKind;
|
||||||
|
use crate::ExecuteRequest;
|
||||||
|
use crate::ExecuteToPendingOutcome;
|
||||||
use crate::FunctionCallOutputContentItem;
|
use crate::FunctionCallOutputContentItem;
|
||||||
use crate::ToolDefinition;
|
use crate::ToolDefinition;
|
||||||
use crate::runtime::ExecuteRequest;
|
|
||||||
use crate::runtime::ExecuteToPendingOutcome;
|
|
||||||
use crate::runtime::RuntimeEvent;
|
use crate::runtime::RuntimeEvent;
|
||||||
use crate::runtime::spawn_runtime;
|
use crate::runtime::spawn_runtime;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user