fix: creating an instance of Codex requires a Config (#859)
I discovered that I accidentally introduced a change in
https://github.com/openai/codex/pull/829 where we load a fresh `Config`
in the middle of `codex.rs`:
c3e10e180a/codex-rs/core/src/codex.rs (L515-L522)
This is not good because the `Config` could differ from the one that has
the user's overrides specified from the CLI. Also, in unit tests, it
means the `Config` was picking up my personal settings as opposed to
using a vanilla config, which was problematic.
This PR cleans things up by moving the common case where
`Op::ConfigureSession` is derived from `Config` (originally done in
`codex_wrapper.rs`) and making it the standard way to initialize `Codex`
by putting it in `Codex::spawn()`. Note this also eliminates quite a bit
of boilerplate from the tests and relieves the caller of the
responsibility of minting out unique IDs when invoking `submit()`.
This commit is contained in:
@@ -4,6 +4,7 @@ use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
@@ -31,7 +32,6 @@ use crate::client::ModelClient;
|
||||
use crate::client::Prompt;
|
||||
use crate::client::ResponseEvent;
|
||||
use crate::config::Config;
|
||||
use crate::config::ConfigOverrides;
|
||||
use crate::error::CodexErr;
|
||||
use crate::error::Result as CodexResult;
|
||||
use crate::exec::ExecParams;
|
||||
@@ -66,25 +66,59 @@ use crate::zdr_transcript::ZdrTranscript;
|
||||
|
||||
/// The high-level interface to the Codex system.
|
||||
/// It operates as a queue pair where you send submissions and receive events.
|
||||
#[derive(Clone)]
|
||||
pub struct Codex {
|
||||
next_id: AtomicU64,
|
||||
tx_sub: Sender<Submission>,
|
||||
rx_event: Receiver<Event>,
|
||||
}
|
||||
|
||||
impl Codex {
|
||||
pub fn spawn(ctrl_c: Arc<Notify>) -> CodexResult<Self> {
|
||||
/// Spawn a new [`Codex`] and initialize the session. Returns the instance
|
||||
/// of `Codex` and the ID of the `SessionInitialized` event that was
|
||||
/// submitted to start the session.
|
||||
pub async fn spawn(config: Config, ctrl_c: Arc<Notify>) -> CodexResult<(Codex, String)> {
|
||||
let (tx_sub, rx_sub) = async_channel::bounded(64);
|
||||
let (tx_event, rx_event) = async_channel::bounded(64);
|
||||
tokio::spawn(submission_loop(rx_sub, tx_event, ctrl_c));
|
||||
Ok(Self { tx_sub, rx_event })
|
||||
let configure_session = Op::ConfigureSession {
|
||||
model: config.model.clone(),
|
||||
instructions: config.instructions.clone(),
|
||||
approval_policy: config.approval_policy,
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
disable_response_storage: config.disable_response_storage,
|
||||
notify: config.notify.clone(),
|
||||
cwd: config.cwd.clone(),
|
||||
};
|
||||
|
||||
tokio::spawn(submission_loop(config, rx_sub, tx_event, ctrl_c));
|
||||
let codex = Codex {
|
||||
next_id: AtomicU64::new(0),
|
||||
tx_sub,
|
||||
rx_event,
|
||||
};
|
||||
let init_id = codex.submit(configure_session).await?;
|
||||
|
||||
Ok((codex, init_id))
|
||||
}
|
||||
|
||||
pub async fn submit(&self, sub: Submission) -> CodexResult<()> {
|
||||
/// Submit the `op` wrapped in a `Submission` with a unique ID.
|
||||
pub async fn submit(&self, op: Op) -> CodexResult<String> {
|
||||
let id = self
|
||||
.next_id
|
||||
.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
|
||||
.to_string();
|
||||
let sub = Submission { id: id.clone(), op };
|
||||
self.submit_with_id(sub).await?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Use sparingly: prefer `submit()` so Codex is responsible for generating
|
||||
/// unique IDs for each submission.
|
||||
pub async fn submit_with_id(&self, sub: Submission) -> CodexResult<()> {
|
||||
self.tx_sub
|
||||
.send(sub)
|
||||
.await
|
||||
.map_err(|_| CodexErr::InternalAgentDied)
|
||||
.map_err(|_| CodexErr::InternalAgentDied)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn next_event(&self) -> CodexResult<Event> {
|
||||
@@ -424,6 +458,7 @@ impl AgentTask {
|
||||
}
|
||||
|
||||
async fn submission_loop(
|
||||
config: Config,
|
||||
rx_sub: Receiver<Submission>,
|
||||
tx_event: Sender<Event>,
|
||||
ctrl_c: Arc<Notify>,
|
||||
@@ -511,16 +546,6 @@ async fn submission_loop(
|
||||
|
||||
let writable_roots = Mutex::new(get_writable_roots(&cwd));
|
||||
|
||||
// Load config to initialize the MCP connection manager.
|
||||
let config = match Config::load_with_overrides(ConfigOverrides::default()) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(e) => {
|
||||
error!("Failed to load config for MCP servers: {e:#}");
|
||||
// Fall back to empty server map so the session can still proceed.
|
||||
Config::load_default_config_for_test()
|
||||
}
|
||||
};
|
||||
|
||||
let mcp_connection_manager =
|
||||
match McpConnectionManager::new(config.mcp_servers.clone()).await {
|
||||
Ok(mgr) => mgr,
|
||||
|
||||
Reference in New Issue
Block a user