mirror of
https://github.com/openai/codex.git
synced 2026-02-06 17:03:42 +00:00
Compare commits
5 Commits
centralize
...
nornagon/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55fe41e243 | ||
|
|
d36118a8fd | ||
|
|
6e946d1707 | ||
|
|
c24b0fcc78 | ||
|
|
2ad649684f |
36
codex-rs/Cargo.lock
generated
36
codex-rs/Cargo.lock
generated
@@ -728,6 +728,7 @@ dependencies = [
|
||||
"rand 0.9.2",
|
||||
"regex-lite",
|
||||
"reqwest",
|
||||
"rust-embed",
|
||||
"seccompiler",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
@@ -4024,6 +4025,41 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.104",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
|
||||
@@ -58,6 +58,7 @@ tree-sitter-bash = "0.25.0"
|
||||
uuid = { version = "1", features = ["serde", "v4"] }
|
||||
whoami = "1.6.1"
|
||||
wildmatch = "2.4.0"
|
||||
rust-embed = { version = "8.5.0", features = ["include-exclude"] }
|
||||
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
||||
@@ -42,6 +42,9 @@ use crate::client::ModelClient;
|
||||
use crate::client_common::Prompt;
|
||||
use crate::client_common::ResponseEvent;
|
||||
use crate::config::Config;
|
||||
use crate::config_edit_tool::handle_get_config;
|
||||
use crate::config_edit_tool::handle_set_config;
|
||||
use crate::config_edit_tool::handle_show_codex_docs;
|
||||
use crate::config_types::ShellEnvironmentPolicy;
|
||||
use crate::conversation_history::ConversationHistory;
|
||||
use crate::conversation_manager::InitialHistory;
|
||||
@@ -2127,6 +2130,9 @@ async fn handle_function_call(
|
||||
.await
|
||||
}
|
||||
"update_plan" => handle_update_plan(sess, arguments, sub_id, call_id).await,
|
||||
"get_config" => handle_get_config(sess, arguments, sub_id, call_id).await,
|
||||
"set_config" => handle_set_config(sess, arguments, sub_id, call_id).await,
|
||||
"show_codex_docs" => handle_show_codex_docs(sess, arguments, sub_id, call_id).await,
|
||||
EXEC_COMMAND_TOOL_NAME => {
|
||||
// TODO(mbolin): Sandbox check.
|
||||
let exec_params = match serde_json::from_str::<ExecCommandParams>(&arguments) {
|
||||
|
||||
347
codex-rs/core/src/config_edit_tool.rs
Normal file
347
codex-rs/core/src/config_edit_tool.rs
Normal file
@@ -0,0 +1,347 @@
|
||||
use crate::codex::Session;
|
||||
use crate::config::find_codex_home;
|
||||
use crate::openai_tools::JsonSchema;
|
||||
use crate::openai_tools::OpenAiTool;
|
||||
use crate::openai_tools::ResponsesApiTool;
|
||||
use crate::protocol::ReviewDecision;
|
||||
use codex_apply_patch::ApplyPatchAction;
|
||||
use codex_apply_patch::MaybeApplyPatchVerified;
|
||||
use codex_apply_patch::maybe_parse_apply_patch_verified;
|
||||
use codex_protocol::models::FunctionCallOutputPayload;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::Deserialize;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const CONFIG_TOML_FILE: &str = "config.toml";
|
||||
|
||||
// Embed docs at compile time.
|
||||
// README from the repo root
|
||||
const README_MD: &str = include_str!("../../../README.md");
|
||||
// Entire docs directory from the repo root (only *.md files are embedded)
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../../docs"]
|
||||
#[include = "**/*.md"]
|
||||
struct EmbeddedDocs;
|
||||
|
||||
fn push_separator(buf: &mut String) {
|
||||
buf.push_str("\n\n---\n\n");
|
||||
}
|
||||
|
||||
fn build_all_codex_docs() -> String {
|
||||
let mut out = String::new();
|
||||
out.push_str("# Codex Documentation\n\n");
|
||||
out.push_str("<!-- Source: README.md -->\n\n");
|
||||
out.push_str(README_MD);
|
||||
|
||||
// Add markdown files from ../docs recursively (embedded at compile time)
|
||||
let mut paths: Vec<String> = EmbeddedDocs::iter()
|
||||
.map(|p| p.as_ref().to_string())
|
||||
.collect();
|
||||
paths.sort();
|
||||
for path in paths.into_iter() {
|
||||
if let Some(file) = EmbeddedDocs::get(&path) {
|
||||
push_separator(&mut out);
|
||||
out.push_str(&format!("<!-- Source: {path} -->\n\n"));
|
||||
out.push_str(&String::from_utf8_lossy(&file.data));
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// get_config() — fetches the current config.toml.
|
||||
pub(crate) fn create_get_config_tool() -> OpenAiTool {
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "get_config".to_string(),
|
||||
description: "Gets the current ~/.codex/config.toml. If the user asks about their configuration or wants to review it, call this tool and use the result to answer or summarize as needed.".to_string(),
|
||||
strict: false,
|
||||
parameters: JsonSchema::Object {
|
||||
properties: BTreeMap::new(),
|
||||
required: Some(vec![]),
|
||||
additional_properties: Some(false),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// set_config(new_config: string) — writes the provided TOML to config.toml.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct SetConfigArgs {
|
||||
new_config: String,
|
||||
}
|
||||
|
||||
pub(crate) fn create_set_config_tool() -> OpenAiTool {
|
||||
let mut properties = BTreeMap::new();
|
||||
properties.insert(
|
||||
"new_config".to_string(),
|
||||
JsonSchema::String {
|
||||
description: Some("Full TOML contents to write to ~/.codex/config.toml".to_string()),
|
||||
},
|
||||
);
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "set_config".to_string(),
|
||||
description: "Overwrites ~/.codex/config.toml with the provided TOML string. If the user requests configuration changes, construct the full desired TOML and call this tool. The value is validated and a diff will be shown for user approval before writing.".to_string(),
|
||||
strict: false,
|
||||
parameters: JsonSchema::Object {
|
||||
properties,
|
||||
required: Some(vec!["new_config".to_string()]),
|
||||
additional_properties: Some(false),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// show_codex_docs() — returns Codex documentation.
|
||||
pub(crate) fn create_show_codex_docs_tool() -> OpenAiTool {
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "show_codex_docs".to_string(),
|
||||
description: "Returns Codex documentation, including the repo README and all user docs under docs/. Use this when you need information about configuration, setup, features, or usage.".to_string(),
|
||||
strict: false,
|
||||
parameters: JsonSchema::Object {
|
||||
properties: BTreeMap::new(),
|
||||
required: Some(vec![]),
|
||||
additional_properties: Some(false),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_config_path() -> std::io::Result<PathBuf> {
|
||||
let mut p = find_codex_home()?;
|
||||
p.push(CONFIG_TOML_FILE);
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_get_config(
|
||||
_session: &Session,
|
||||
_arguments: String,
|
||||
_sub_id: String,
|
||||
call_id: String,
|
||||
) -> ResponseInputItem {
|
||||
let content = match resolve_config_path().and_then(fs::read_to_string) {
|
||||
Ok(s) => s,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => String::new(),
|
||||
Err(e) => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to read config: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content,
|
||||
success: Some(true),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_set_config(
|
||||
session: &Session,
|
||||
arguments: String,
|
||||
sub_id: String,
|
||||
call_id: String,
|
||||
) -> ResponseInputItem {
|
||||
let args: SetConfigArgs = match serde_json::from_str(&arguments) {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to parse function arguments: {e}"),
|
||||
success: None,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
// Validate TOML and ensure it can be materialized into a runtime Config.
|
||||
let cfg_toml: crate::config::ConfigToml = match toml::from_str(&args.new_config) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("invalid TOML: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
let codex_home = match find_codex_home() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to resolve codex_home: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
if let Err(e) = crate::config::Config::load_from_base_config_with_overrides(
|
||||
cfg_toml.clone(),
|
||||
crate::config::ConfigOverrides::default(),
|
||||
codex_home.clone(),
|
||||
) {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("invalid config: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
let path = match resolve_config_path() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to resolve config path: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
// Build a synthetic patch showing the proposed change and ask for patch approval.
|
||||
let current = match std::fs::read_to_string(&path) {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => None,
|
||||
Err(e) => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to read existing config: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let make_lines = |s: &str| {
|
||||
let mut v: Vec<&str> = s.split('\n').collect();
|
||||
if v.last().is_some_and(|l| l.is_empty()) {
|
||||
v.pop();
|
||||
}
|
||||
v.into_iter()
|
||||
.map(|l| l.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
let patch_body = if let Some(curr) = ¤t {
|
||||
let mut body = format!("*** Update File: {}\n@@\n", path.display());
|
||||
for line in make_lines(curr) {
|
||||
body.push_str(&format!("-{line}\n"));
|
||||
}
|
||||
for line in make_lines(&args.new_config) {
|
||||
body.push_str(&format!("+{line}\n"));
|
||||
}
|
||||
body
|
||||
} else {
|
||||
let mut body = format!("*** Add File: {}\n", path.display());
|
||||
for line in make_lines(&args.new_config) {
|
||||
body.push_str(&format!("+{line}\n"));
|
||||
}
|
||||
body
|
||||
};
|
||||
|
||||
let patch_text = format!("*** Begin Patch\n{patch_body}*** End Patch");
|
||||
let argv = vec!["apply_patch".to_string(), patch_text];
|
||||
|
||||
let cwd = path
|
||||
.parent()
|
||||
.map(Path::to_path_buf)
|
||||
.unwrap_or_else(|| PathBuf::from("/"));
|
||||
let action: ApplyPatchAction = match maybe_parse_apply_patch_verified(&argv, &cwd) {
|
||||
MaybeApplyPatchVerified::Body(action) => action,
|
||||
MaybeApplyPatchVerified::CorrectnessError(e) => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to compute patch diff: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: "failed to construct patch diff".to_string(),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let rx = session
|
||||
.request_patch_approval(
|
||||
sub_id.clone(),
|
||||
call_id.clone(),
|
||||
&action,
|
||||
Some("Update Codex configuration file".to_string()),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
match rx.await.unwrap_or_default() {
|
||||
ReviewDecision::Approved | ReviewDecision::ApprovedForSession => { /* proceed */ }
|
||||
ReviewDecision::Denied | ReviewDecision::Abort => {
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: "set_config rejected by user".to_string(),
|
||||
success: None,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some(parent) = path.parent()
|
||||
&& let Err(e) = fs::create_dir_all(parent)
|
||||
{
|
||||
return ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to create config directory: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
}
|
||||
match fs::write(&path, args.new_config.as_bytes()) {
|
||||
Ok(_) => ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("wrote {}", path.display()),
|
||||
success: Some(true),
|
||||
},
|
||||
},
|
||||
Err(e) => ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content: format!("failed to write config: {e}"),
|
||||
success: Some(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_show_codex_docs(
|
||||
_session: &Session,
|
||||
_arguments: String,
|
||||
_sub_id: String,
|
||||
call_id: String,
|
||||
) -> ResponseInputItem {
|
||||
let content = build_all_codex_docs();
|
||||
ResponseInputItem::FunctionCallOutput {
|
||||
call_id,
|
||||
output: FunctionCallOutputPayload {
|
||||
content,
|
||||
success: Some(true),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ mod conversation_manager;
|
||||
mod event_mapping;
|
||||
pub use conversation_manager::ConversationManager;
|
||||
pub use conversation_manager::NewConversation;
|
||||
mod config_edit_tool;
|
||||
// Re-export common auth types for workspace consumers
|
||||
pub use auth::AuthManager;
|
||||
pub use auth::CodexAuth;
|
||||
|
||||
@@ -5,6 +5,9 @@ use serde_json::json;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::config_edit_tool::create_get_config_tool;
|
||||
use crate::config_edit_tool::create_set_config_tool;
|
||||
use crate::config_edit_tool::create_show_codex_docs_tool;
|
||||
use crate::model_family::ModelFamily;
|
||||
use crate::plan_tool::PLAN_TOOL;
|
||||
use crate::protocol::AskForApproval;
|
||||
@@ -572,6 +575,10 @@ pub(crate) fn get_openai_tools(
|
||||
if config.web_search_request {
|
||||
tools.push(OpenAiTool::WebSearch {});
|
||||
}
|
||||
// Always include internal config tools.
|
||||
tools.push(create_get_config_tool());
|
||||
tools.push(create_set_config_tool());
|
||||
tools.push(create_show_codex_docs_tool());
|
||||
|
||||
// Include the view_image tool so the agent can attach images to context.
|
||||
if config.include_view_image_tool {
|
||||
@@ -647,7 +654,15 @@ mod tests {
|
||||
|
||||
assert_eq_tool_names(
|
||||
&tools,
|
||||
&["local_shell", "update_plan", "web_search", "view_image"],
|
||||
&[
|
||||
"local_shell",
|
||||
"update_plan",
|
||||
"web_search",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -668,7 +683,15 @@ mod tests {
|
||||
|
||||
assert_eq_tool_names(
|
||||
&tools,
|
||||
&["shell", "update_plan", "web_search", "view_image"],
|
||||
&[
|
||||
"shell",
|
||||
"update_plan",
|
||||
"web_search",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -728,13 +751,16 @@ mod tests {
|
||||
&[
|
||||
"shell",
|
||||
"web_search",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
"test_server/do_something_cool",
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tools[3],
|
||||
tools[6],
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "test_server/do_something_cool".to_string(),
|
||||
parameters: JsonSchema::Object {
|
||||
@@ -841,11 +867,14 @@ mod tests {
|
||||
]);
|
||||
|
||||
let tools = get_openai_tools(&config, Some(tools_map));
|
||||
// Expect shell first, followed by MCP tools sorted by fully-qualified name.
|
||||
// Expect shell first, followed by built-in config tools, then MCP tools sorted by fully-qualified name.
|
||||
assert_eq_tool_names(
|
||||
&tools,
|
||||
&[
|
||||
"shell",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
"test_server/cool",
|
||||
"test_server/do",
|
||||
@@ -893,11 +922,19 @@ mod tests {
|
||||
|
||||
assert_eq_tool_names(
|
||||
&tools,
|
||||
&["shell", "web_search", "view_image", "dash/search"],
|
||||
&[
|
||||
"shell",
|
||||
"web_search",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
"dash/search",
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tools[3],
|
||||
tools[6],
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "dash/search".to_string(),
|
||||
parameters: JsonSchema::Object {
|
||||
@@ -953,10 +990,18 @@ mod tests {
|
||||
|
||||
assert_eq_tool_names(
|
||||
&tools,
|
||||
&["shell", "web_search", "view_image", "dash/paginate"],
|
||||
&[
|
||||
"shell",
|
||||
"web_search",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
"dash/paginate",
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
tools[3],
|
||||
tools[6],
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "dash/paginate".to_string(),
|
||||
parameters: JsonSchema::Object {
|
||||
@@ -1008,9 +1053,21 @@ mod tests {
|
||||
)])),
|
||||
);
|
||||
|
||||
assert_eq_tool_names(&tools, &["shell", "web_search", "view_image", "dash/tags"]);
|
||||
assert_eq_tool_names(
|
||||
&tools,
|
||||
&[
|
||||
"shell",
|
||||
"web_search",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
"dash/tags",
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tools[3],
|
||||
tools[6],
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "dash/tags".to_string(),
|
||||
parameters: JsonSchema::Object {
|
||||
@@ -1065,9 +1122,20 @@ mod tests {
|
||||
)])),
|
||||
);
|
||||
|
||||
assert_eq_tool_names(&tools, &["shell", "web_search", "view_image", "dash/value"]);
|
||||
assert_eq_tool_names(
|
||||
&tools,
|
||||
&[
|
||||
"shell",
|
||||
"web_search",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
"dash/value",
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
tools[3],
|
||||
tools[6],
|
||||
OpenAiTool::Function(ResponsesApiTool {
|
||||
name: "dash/value".to_string(),
|
||||
parameters: JsonSchema::Object {
|
||||
|
||||
@@ -191,7 +191,15 @@ async fn prompt_tools_are_consistent_across_requests() {
|
||||
let expected_instructions: &str = include_str!("../../prompt.md");
|
||||
// our internal implementation is responsible for keeping tools in sync
|
||||
// with the OpenAI schema, so we just verify the tool presence here
|
||||
let expected_tools_names: &[&str] = &["shell", "update_plan", "apply_patch", "view_image"];
|
||||
let expected_tools_names: &[&str] = &[
|
||||
"shell",
|
||||
"update_plan",
|
||||
"apply_patch",
|
||||
"get_config",
|
||||
"set_config",
|
||||
"show_codex_docs",
|
||||
"view_image",
|
||||
];
|
||||
let body0 = requests[0].body_json::<serde_json::Value>().unwrap();
|
||||
assert_eq!(
|
||||
body0["instructions"],
|
||||
|
||||
Reference in New Issue
Block a user