From e7ad9449ea28542363ddb7d3d273a34f1953fc77 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 28 Apr 2025 15:39:34 -0700 Subject: [PATCH] feat: make it possible to set `disable_response_storage = true` in config.toml (#714) https://github.com/openai/codex/pull/642 introduced support for the `--disable-response-storage` flag, but if you are a ZDR customer, it is tedious to set this every time, so this PR makes it possible to set this once in `config.toml` and be done with it. Incidentally, this tidies things up such that now `init_codex()` takes only one parameter: `Config`. --- codex-rs/core/src/codex_wrapper.rs | 7 ++----- codex-rs/core/src/config.rs | 12 ++++++++++++ codex-rs/exec/src/lib.rs | 8 ++++++-- codex-rs/repl/src/lib.rs | 7 ++++++- codex-rs/tui/src/app.rs | 2 -- codex-rs/tui/src/chatwidget.rs | 18 ++++++++---------- codex-rs/tui/src/lib.rs | 20 +++++++------------- 7 files changed, 41 insertions(+), 33 deletions(-) diff --git a/codex-rs/core/src/codex_wrapper.rs b/codex-rs/core/src/codex_wrapper.rs index 3aeff676..146a812e 100644 --- a/codex-rs/core/src/codex_wrapper.rs +++ b/codex-rs/core/src/codex_wrapper.rs @@ -15,10 +15,7 @@ use tokio::sync::Notify; /// Returns the wrapped [`Codex`] **and** the `SessionInitialized` event that /// is received as a response to the initial `ConfigureSession` submission so /// that callers can surface the information to the UI. -pub async fn init_codex( - config: Config, - disable_response_storage: bool, -) -> anyhow::Result<(CodexWrapper, Event, Arc)> { +pub async fn init_codex(config: Config) -> anyhow::Result<(CodexWrapper, Event, Arc)> { let ctrl_c = notify_on_sigint(); let codex = CodexWrapper::new(Codex::spawn(ctrl_c.clone())?); let init_id = codex @@ -27,7 +24,7 @@ pub async fn init_codex( instructions: config.instructions.clone(), approval_policy: config.approval_policy, sandbox_policy: config.sandbox_policy, - disable_response_storage, + disable_response_storage: config.disable_response_storage, }) .await?; diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index d9ad3336..95abae52 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -21,6 +21,13 @@ pub struct Config { pub approval_policy: AskForApproval, #[serde(default)] pub sandbox_policy: SandboxPolicy, + + /// Disable server-side response storage (sends the full conversation + /// context with every request). Currently necessary for OpenAI customers + /// who have opted into Zero Data Retention (ZDR). + #[serde(default)] + pub disable_response_storage: bool, + /// System instructions. pub instructions: Option, } @@ -31,6 +38,7 @@ pub struct ConfigOverrides { pub model: Option, pub approval_policy: Option, pub sandbox_policy: Option, + pub disable_response_storage: Option, } impl Config { @@ -50,6 +58,7 @@ impl Config { model, approval_policy, sandbox_policy, + disable_response_storage, } = overrides; if let Some(model) = model { @@ -61,6 +70,9 @@ impl Config { if let Some(sandbox_policy) = sandbox_policy { cfg.sandbox_policy = sandbox_policy; } + if let Some(disable_response_storage) = disable_response_storage { + cfg.disable_response_storage = disable_response_storage; + } Ok(cfg) } diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index daa07e46..d37e5a95 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -56,10 +56,14 @@ pub async fn run_main(cli: Cli) -> anyhow::Result<()> { // the user for approval. approval_policy: Some(AskForApproval::Never), sandbox_policy: sandbox_policy.map(Into::into), + disable_response_storage: if disable_response_storage { + Some(true) + } else { + None + }, }; let config = Config::load_with_overrides(overrides)?; - let (codex_wrapper, event, ctrl_c) = - codex_wrapper::init_codex(config, disable_response_storage).await?; + let (codex_wrapper, event, ctrl_c) = codex_wrapper::init_codex(config).await?; let codex = Arc::new(codex_wrapper); info!("Codex initialized with event: {event:?}"); diff --git a/codex-rs/repl/src/lib.rs b/codex-rs/repl/src/lib.rs index 74e54181..17586332 100644 --- a/codex-rs/repl/src/lib.rs +++ b/codex-rs/repl/src/lib.rs @@ -81,6 +81,11 @@ pub async fn run_main(cli: Cli) -> anyhow::Result<()> { model: cli.model.clone(), approval_policy: cli.approval_policy.map(Into::into), sandbox_policy: cli.sandbox_policy.map(Into::into), + disable_response_storage: if cli.disable_response_storage { + Some(true) + } else { + None + }, }; let config = Config::load_with_overrides(overrides)?; @@ -104,7 +109,7 @@ async fn codex_main(cli: Cli, cfg: Config, ctrl_c: Arc) -> anyhow::Resul instructions: cfg.instructions, approval_policy: cfg.approval_policy, sandbox_policy: cfg.sandbox_policy, - disable_response_storage: cli.disable_response_storage, + disable_response_storage: cfg.disable_response_storage, }, }; diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index c5da0b56..cb2b44e0 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -37,7 +37,6 @@ impl App<'_> { initial_prompt: Option, show_git_warning: bool, initial_images: Vec, - disable_response_storage: bool, ) -> Self { let (app_event_tx, app_event_rx) = channel(); let scroll_event_helper = ScrollEventHelper::new(app_event_tx.clone()); @@ -81,7 +80,6 @@ impl App<'_> { app_event_tx.clone(), initial_prompt.clone(), initial_images, - disable_response_storage, ); let app_state = if show_git_warning { diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index e2224f99..06bf1bc8 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -49,7 +49,6 @@ impl ChatWidget<'_> { app_event_tx: Sender, initial_prompt: Option, initial_images: Vec, - disable_response_storage: bool, ) -> Self { let (codex_op_tx, mut codex_op_rx) = unbounded_channel::(); @@ -62,15 +61,14 @@ impl ChatWidget<'_> { // Create the Codex asynchronously so the UI loads as quickly as possible. let config_for_agent_loop = config.clone(); tokio::spawn(async move { - let (codex, session_event, _ctrl_c) = - match init_codex(config_for_agent_loop, disable_response_storage).await { - Ok(vals) => vals, - Err(e) => { - // TODO: surface this error to the user. - tracing::error!("failed to initialize codex: {e}"); - return; - } - }; + let (codex, session_event, _ctrl_c) = match init_codex(config_for_agent_loop).await { + Ok(vals) => vals, + Err(e) => { + // TODO: surface this error to the user. + tracing::error!("failed to initialize codex: {e}"); + return; + } + }; // Forward the captured `SessionInitialized` event that was consumed // inside `init_codex()` so it can be rendered in the UI. diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 8e987ad7..bf4ebec4 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -39,6 +39,11 @@ pub fn run_main(cli: Cli) -> std::io::Result<()> { model: cli.model.clone(), approval_policy: cli.approval_policy.map(Into::into), sandbox_policy: cli.sandbox_policy.map(Into::into), + disable_response_storage: if cli.disable_response_storage { + Some(true) + } else { + None + }, }; #[allow(clippy::print_stderr)] match Config::load_with_overrides(overrides) { @@ -134,19 +139,8 @@ fn run_ratatui_app( let mut terminal = tui::init()?; terminal.clear()?; - let Cli { - prompt, - images, - disable_response_storage, - .. - } = cli; - let mut app = App::new( - config, - prompt, - show_git_warning, - images, - disable_response_storage, - ); + let Cli { prompt, images, .. } = cli; + let mut app = App::new(config.clone(), prompt, show_git_warning, images); // Bridge log receiver into the AppEvent channel so latest log lines update the UI. {