feat: add beta_supported_tools (#4669)

Gate the new read_file tool behind a new `beta_supported_tools` flag and
only enable it for `gpt-5-codex`
This commit is contained in:
jif-oai
2025-10-03 17:58:03 +01:00
committed by GitHub
parent 153338c20f
commit e0b38bd7a2
3 changed files with 57 additions and 27 deletions

View File

@@ -41,6 +41,9 @@ pub struct ModelFamily {
// Instructions to use for querying the model
pub base_instructions: String,
/// Names of beta tools that should be exposed to this model family.
pub experimental_supported_tools: Vec<String>,
}
macro_rules! model_family {
@@ -57,6 +60,7 @@ macro_rules! model_family {
uses_local_shell_tool: false,
apply_patch_tool_type: None,
base_instructions: BASE_INSTRUCTIONS.to_string(),
experimental_supported_tools: Vec::new(),
};
// apply overrides
$(
@@ -105,6 +109,7 @@ pub fn find_family_for_model(slug: &str) -> Option<ModelFamily> {
supports_reasoning_summaries: true,
reasoning_summary_format: ReasoningSummaryFormat::Experimental,
base_instructions: GPT_5_CODEX_INSTRUCTIONS.to_string(),
experimental_supported_tools: vec!["read_file".to_string()],
)
} else if slug.starts_with("gpt-5") {
model_family!(
@@ -127,5 +132,6 @@ pub fn derive_default_model_family(model: &str) -> ModelFamily {
uses_local_shell_tool: false,
apply_patch_tool_type: None,
base_instructions: BASE_INSTRUCTIONS.to_string(),
experimental_supported_tools: Vec::new(),
}
}

View File

@@ -28,6 +28,7 @@ pub(crate) struct ToolsConfig {
pub web_search_request: bool,
pub include_view_image_tool: bool,
pub experimental_unified_exec_tool: bool,
pub experimental_supported_tools: Vec<String>,
}
pub(crate) struct ToolsConfigParams<'a> {
@@ -78,6 +79,7 @@ impl ToolsConfig {
web_search_request: *include_web_search_request,
include_view_image_tool: *include_view_image_tool,
experimental_unified_exec_tool: *experimental_unified_exec_tool,
experimental_supported_tools: model_family.experimental_supported_tools.clone(),
}
}
}
@@ -515,7 +517,6 @@ pub(crate) fn build_specs(
let exec_stream_handler = Arc::new(ExecStreamHandler);
let unified_exec_handler = Arc::new(UnifiedExecHandler);
let plan_handler = Arc::new(PlanHandler);
let read_file_handler = Arc::new(ReadFileHandler);
let apply_patch_handler = Arc::new(ApplyPatchHandler);
let view_image_handler = Arc::new(ViewImageHandler);
let mcp_handler = Arc::new(McpHandler);
@@ -566,8 +567,15 @@ pub(crate) fn build_specs(
builder.register_handler("apply_patch", apply_patch_handler);
}
builder.push_spec(create_read_file_tool());
builder.register_handler("read_file", read_file_handler);
if config
.experimental_supported_tools
.iter()
.any(|tool| tool == "read_file")
{
let read_file_handler = Arc::new(ReadFileHandler);
builder.push_spec(create_read_file_tool());
builder.register_handler("read_file", read_file_handler);
}
if config.web_search_request {
builder.push_spec(ToolSpec::WebSearch {});
@@ -648,13 +656,7 @@ mod tests {
assert_eq_tool_names(
&tools,
&[
"unified_exec",
"update_plan",
"read_file",
"web_search",
"view_image",
],
&["unified_exec", "update_plan", "web_search", "view_image"],
);
}
@@ -674,16 +676,28 @@ mod tests {
assert_eq_tool_names(
&tools,
&[
"unified_exec",
"update_plan",
"read_file",
"web_search",
"view_image",
],
&["unified_exec", "update_plan", "web_search", "view_image"],
);
}
#[test]
fn test_build_specs_includes_beta_read_file_tool() {
let model_family = find_family_for_model("gpt-5-codex")
.expect("gpt-5-codex should be a valid model family");
let config = ToolsConfig::new(&ToolsConfigParams {
model_family: &model_family,
include_plan_tool: false,
include_apply_patch_tool: false,
include_web_search_request: false,
use_streamable_shell_tool: false,
include_view_image_tool: false,
experimental_unified_exec_tool: true,
});
let (tools, _) = build_specs(&config, Some(HashMap::new())).build();
assert_eq_tool_names(&tools, &["unified_exec", "read_file"]);
}
#[test]
fn test_build_specs_mcp_tools() {
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
@@ -739,7 +753,6 @@ mod tests {
&tools,
&[
"unified_exec",
"read_file",
"web_search",
"view_image",
"test_server/do_something_cool",
@@ -747,7 +760,7 @@ mod tests {
);
assert_eq!(
tools[4],
tools[3],
ToolSpec::Function(ResponsesApiTool {
name: "test_server/do_something_cool".to_string(),
parameters: JsonSchema::Object {
@@ -858,7 +871,6 @@ mod tests {
&tools,
&[
"unified_exec",
"read_file",
"view_image",
"test_server/cool",
"test_server/do",
@@ -869,7 +881,8 @@ mod tests {
#[test]
fn test_mcp_tool_property_missing_type_defaults_to_string() {
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
let model_family = find_family_for_model("gpt-5-codex")
.expect("gpt-5-codex should be a valid model family");
let config = ToolsConfig::new(&ToolsConfigParams {
model_family: &model_family,
include_plan_tool: false,
@@ -937,7 +950,8 @@ mod tests {
#[test]
fn test_mcp_tool_integer_normalized_to_number() {
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
let model_family = find_family_for_model("gpt-5-codex")
.expect("gpt-5-codex should be a valid model family");
let config = ToolsConfig::new(&ToolsConfigParams {
model_family: &model_family,
include_plan_tool: false,
@@ -1000,7 +1014,8 @@ mod tests {
#[test]
fn test_mcp_tool_array_without_items_gets_default_string_items() {
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
let model_family = find_family_for_model("gpt-5-codex")
.expect("gpt-5-codex should be a valid model family");
let config = ToolsConfig::new(&ToolsConfigParams {
model_family: &model_family,
include_plan_tool: false,
@@ -1066,7 +1081,8 @@ mod tests {
#[test]
fn test_mcp_tool_anyof_defaults_to_string() {
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
let model_family = find_family_for_model("gpt-5-codex")
.expect("gpt-5-codex should be a valid model family");
let config = ToolsConfig::new(&ToolsConfigParams {
model_family: &model_family,
include_plan_tool: false,
@@ -1144,7 +1160,8 @@ mod tests {
#[test]
fn test_get_openai_tools_mcp_tools_with_additional_properties_schema() {
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
let model_family = find_family_for_model("gpt-5-codex")
.expect("gpt-5-codex should be a valid model family");
let config = ToolsConfig::new(&ToolsConfigParams {
model_family: &model_family,
include_plan_tool: false,

View File

@@ -111,14 +111,21 @@ async fn model_selects_expected_tools() {
let codex_tools = collect_tool_identifiers_for_model("codex-mini-latest").await;
assert_eq!(
codex_tools,
vec!["local_shell".to_string(), "read_file".to_string()],
vec!["local_shell".to_string()],
"codex-mini-latest should expose the local shell tool",
);
let o3_tools = collect_tool_identifiers_for_model("o3").await;
assert_eq!(
o3_tools,
vec!["shell".to_string(), "read_file".to_string()],
vec!["shell".to_string()],
"o3 should expose the generic shell tool",
);
let gpt5_codex_tools = collect_tool_identifiers_for_model("gpt-5-codex").await;
assert_eq!(
gpt5_codex_tools,
vec!["shell".to_string(), "read_file".to_string()],
"gpt-5-codex should expose the beta read_file tool",
);
}