mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
Compare commits
3 Commits
response-a
...
pakrym/sup
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2eb50be9f | ||
|
|
17cee69a85 | ||
|
|
19d8658917 |
@@ -51,6 +51,7 @@ struct MultitoolCli {
|
||||
subcommand: Option<Subcommand>,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
enum Subcommand {
|
||||
/// Run Codex non-interactively.
|
||||
@@ -255,7 +256,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
|
||||
}
|
||||
Some(Subcommand::Exec(mut exec_cli)) => {
|
||||
prepend_config_flags(
|
||||
&mut exec_cli.config_overrides,
|
||||
&mut exec_cli.shared.config_overrides,
|
||||
root_config_overrides.clone(),
|
||||
);
|
||||
codex_exec::run_main(exec_cli, codex_linux_sandbox_exe).await?;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use clap::Args;
|
||||
use clap::Parser;
|
||||
use clap::ValueEnum;
|
||||
use codex_common::CliConfigOverrides;
|
||||
@@ -10,6 +11,33 @@ pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Option<Command>,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub shared: SharedExecArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Command {
|
||||
/// Resume a previous session by id or pick the most recent with --last.
|
||||
Resume(ResumeArgs),
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct ResumeArgs {
|
||||
/// Conversation/session id (UUID). When provided, resumes this session.
|
||||
/// If omitted, use --last to pick the most recent recorded session.
|
||||
#[arg(value_name = "SESSION_ID")]
|
||||
pub session_id: Option<String>,
|
||||
|
||||
/// Resume the most recent recorded session (newest) without specifying an id.
|
||||
#[arg(long = "last", default_value_t = false, conflicts_with = "session_id")]
|
||||
pub last: bool,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub shared: SharedExecArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Args)]
|
||||
pub struct SharedExecArgs {
|
||||
/// Optional image(s) to attach to the initial prompt.
|
||||
#[arg(long = "image", short = 'i', value_name = "FILE", value_delimiter = ',', num_args = 1..)]
|
||||
pub images: Vec<PathBuf>,
|
||||
@@ -56,9 +84,6 @@ pub struct Cli {
|
||||
#[arg(long = "output-schema", value_name = "FILE")]
|
||||
pub output_schema: Option<PathBuf>,
|
||||
|
||||
#[clap(skip)]
|
||||
pub config_overrides: CliConfigOverrides,
|
||||
|
||||
/// Specifies color settings for use in the output.
|
||||
#[arg(long = "color", value_enum, default_value_t = Color::Auto)]
|
||||
pub color: Color,
|
||||
@@ -86,34 +111,15 @@ pub struct Cli {
|
||||
#[arg(long = "output-last-message")]
|
||||
pub last_message_file: Option<PathBuf>,
|
||||
|
||||
#[clap(skip)]
|
||||
pub config_overrides: CliConfigOverrides,
|
||||
|
||||
/// Initial instructions for the agent. If not provided as an argument (or
|
||||
/// if `-` is used), instructions are read from stdin.
|
||||
#[arg(value_name = "PROMPT")]
|
||||
pub prompt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Command {
|
||||
/// Resume a previous session by id or pick the most recent with --last.
|
||||
Resume(ResumeArgs),
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct ResumeArgs {
|
||||
/// Conversation/session id (UUID). When provided, resumes this session.
|
||||
/// If omitted, use --last to pick the most recent recorded session.
|
||||
#[arg(value_name = "SESSION_ID")]
|
||||
pub session_id: Option<String>,
|
||||
|
||||
/// Resume the most recent recorded session (newest) without specifying an id.
|
||||
#[arg(long = "last", default_value_t = false, conflicts_with = "session_id")]
|
||||
pub last: bool,
|
||||
|
||||
/// Prompt to send after resuming the session. If `-` is used, read from stdin.
|
||||
#[arg(value_name = "PROMPT")]
|
||||
pub prompt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, ValueEnum)]
|
||||
#[value(rename_all = "kebab-case")]
|
||||
pub enum Color {
|
||||
|
||||
@@ -36,6 +36,7 @@ use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
use crate::cli::Command as ExecCommand;
|
||||
use crate::cli::SharedExecArgs;
|
||||
use crate::event_processor::CodexStatus;
|
||||
use crate::event_processor::EventProcessor;
|
||||
use codex_core::default_client::set_default_originator;
|
||||
@@ -46,8 +47,19 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
tracing::warn!(?err, "Failed to set codex exec originator override {err:?}");
|
||||
}
|
||||
|
||||
let Cli {
|
||||
command,
|
||||
let Cli { command, shared } = cli;
|
||||
|
||||
// Merge overrides from root CLI.
|
||||
let mut config_overrides = shared.config_overrides.clone();
|
||||
|
||||
let shared = command
|
||||
.as_ref()
|
||||
.map(|cmd| match cmd {
|
||||
ExecCommand::Resume(args) => args.shared.clone(),
|
||||
})
|
||||
.unwrap_or(shared);
|
||||
|
||||
let SharedExecArgs {
|
||||
images,
|
||||
model: model_cli_arg,
|
||||
oss,
|
||||
@@ -61,19 +73,15 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
json: json_mode,
|
||||
experimental_json,
|
||||
sandbox_mode: sandbox_mode_cli_arg,
|
||||
prompt,
|
||||
output_schema: output_schema_path,
|
||||
include_plan_tool,
|
||||
config_overrides,
|
||||
} = cli;
|
||||
prompt: prompt_arg,
|
||||
..
|
||||
} = shared;
|
||||
|
||||
// Determine the prompt source (parent or subcommand) and read from stdin if needed.
|
||||
let prompt_arg = match &command {
|
||||
// Allow prompt before the subcommand by falling back to the parent-level prompt
|
||||
// when the Resume subcommand did not provide its own prompt.
|
||||
Some(ExecCommand::Resume(args)) => args.prompt.clone().or(prompt),
|
||||
None => prompt,
|
||||
};
|
||||
config_overrides
|
||||
.raw_overrides
|
||||
.splice(0..0, shared.config_overrides.raw_overrides);
|
||||
|
||||
let prompt = match prompt_arg {
|
||||
Some(p) if p != "-" => p,
|
||||
|
||||
@@ -30,6 +30,7 @@ fn main() -> anyhow::Result<()> {
|
||||
// Merge root-level overrides into inner CLI struct so downstream logic remains unchanged.
|
||||
let mut inner = top_cli.inner;
|
||||
inner
|
||||
.shared
|
||||
.config_overrides
|
||||
.raw_overrides
|
||||
.splice(0..0, top_cli.config_overrides.raw_overrides);
|
||||
|
||||
138
codex-rs/exec/tests/suite/config_overrides.rs
Normal file
138
codex-rs/exec/tests/suite/config_overrides.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
#![cfg(not(target_os = "windows"))]
|
||||
#![allow(clippy::expect_used, clippy::unwrap_used)]
|
||||
|
||||
use core_test_support::responses;
|
||||
use core_test_support::test_codex_exec::test_codex_exec;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn config_overrides_are_applied_resume() -> anyhow::Result<()> {
|
||||
config_overrides_are_applied_resume_inner(
|
||||
&["--model", "o3", "prompt text"],
|
||||
&[
|
||||
"resume",
|
||||
"--skip-git-repo-check",
|
||||
"fake id",
|
||||
"--model",
|
||||
"o3",
|
||||
"resume prompt text",
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
|
||||
config_overrides_are_applied_resume_inner(
|
||||
&["-c", "model=o3", "prompt text"],
|
||||
&[
|
||||
"resume",
|
||||
"--skip-git-repo-check",
|
||||
"fake id",
|
||||
"-c",
|
||||
"model=o3",
|
||||
"resume prompt text",
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
|
||||
config_overrides_are_applied_resume_inner(
|
||||
&["-c", "model=o3", "prompt text"],
|
||||
&[
|
||||
"-c",
|
||||
"model=o3",
|
||||
"resume",
|
||||
"--skip-git-repo-check",
|
||||
"fake id",
|
||||
"resume prompt text",
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn config_overrides_are_applied_resume_inner(
|
||||
args: &[&str],
|
||||
resume_args: &[&str],
|
||||
) -> anyhow::Result<()> {
|
||||
let test = test_codex_exec();
|
||||
|
||||
let match_config_overrides_applied = |req: &wiremock::Request| {
|
||||
let body = std::str::from_utf8(&req.body).unwrap_or("");
|
||||
body.contains("prompt text") && body.contains("o3")
|
||||
};
|
||||
|
||||
let server = responses::start_mock_server().await;
|
||||
responses::mount_sse_once_match(
|
||||
&server,
|
||||
match_config_overrides_applied,
|
||||
responses::sse(vec![
|
||||
responses::ev_assistant_message("msg-1", "config overrides are applied."),
|
||||
responses::ev_completed("resp-2"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
|
||||
test.cmd_with_server(&server)
|
||||
.arg("--skip-git-repo-check")
|
||||
.arg("--experimental-json")
|
||||
.args(args)
|
||||
.assert()
|
||||
.code(0);
|
||||
|
||||
let match_config_resume_overrides_applied = |req: &wiremock::Request| {
|
||||
let body = std::str::from_utf8(&req.body).unwrap_or("");
|
||||
body.contains("resume prompt text") && body.contains("o3")
|
||||
};
|
||||
|
||||
responses::mount_sse_once_match(
|
||||
&server,
|
||||
match_config_resume_overrides_applied,
|
||||
responses::sse(vec![
|
||||
responses::ev_assistant_message("msg-1", "config overrides are applied resume."),
|
||||
responses::ev_completed("resp-2"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
|
||||
test.cmd_with_server(&server)
|
||||
.args(resume_args)
|
||||
.assert()
|
||||
.code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn config_overrides_are_applied() -> anyhow::Result<()> {
|
||||
config_overrides_are_applied_inner(&["-c", "model=o3", "prompt text"]).await?;
|
||||
config_overrides_are_applied_inner(&["prompt text", "-c", "model=o3"]).await?;
|
||||
config_overrides_are_applied_inner(&["--model", "o3", "prompt text"]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn config_overrides_are_applied_inner(args: &[&str]) -> anyhow::Result<()> {
|
||||
let test = test_codex_exec();
|
||||
|
||||
let match_config_overrides_applied = |req: &wiremock::Request| {
|
||||
let body = std::str::from_utf8(&req.body).unwrap_or("");
|
||||
body.contains("prompt text") && body.contains("o3")
|
||||
};
|
||||
|
||||
let server = responses::start_mock_server().await;
|
||||
responses::mount_sse_once_match(
|
||||
&server,
|
||||
match_config_overrides_applied,
|
||||
responses::sse(vec![
|
||||
responses::ev_assistant_message("msg-1", "config overrides are applied."),
|
||||
responses::ev_completed("resp-2"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
|
||||
test.cmd_with_server(&server)
|
||||
.arg("--skip-git-repo-check")
|
||||
.args(args)
|
||||
.assert()
|
||||
.code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Aggregates all former standalone integration tests as modules.
|
||||
mod apply_patch;
|
||||
mod config_overrides;
|
||||
mod output_schema;
|
||||
mod resume;
|
||||
mod sandbox;
|
||||
|
||||
Reference in New Issue
Block a user