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:
@@ -41,6 +41,9 @@ pub struct ModelFamily {
|
|||||||
|
|
||||||
// Instructions to use for querying the model
|
// Instructions to use for querying the model
|
||||||
pub base_instructions: String,
|
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 {
|
macro_rules! model_family {
|
||||||
@@ -57,6 +60,7 @@ macro_rules! model_family {
|
|||||||
uses_local_shell_tool: false,
|
uses_local_shell_tool: false,
|
||||||
apply_patch_tool_type: None,
|
apply_patch_tool_type: None,
|
||||||
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
||||||
|
experimental_supported_tools: Vec::new(),
|
||||||
};
|
};
|
||||||
// apply overrides
|
// apply overrides
|
||||||
$(
|
$(
|
||||||
@@ -105,6 +109,7 @@ pub fn find_family_for_model(slug: &str) -> Option<ModelFamily> {
|
|||||||
supports_reasoning_summaries: true,
|
supports_reasoning_summaries: true,
|
||||||
reasoning_summary_format: ReasoningSummaryFormat::Experimental,
|
reasoning_summary_format: ReasoningSummaryFormat::Experimental,
|
||||||
base_instructions: GPT_5_CODEX_INSTRUCTIONS.to_string(),
|
base_instructions: GPT_5_CODEX_INSTRUCTIONS.to_string(),
|
||||||
|
experimental_supported_tools: vec!["read_file".to_string()],
|
||||||
)
|
)
|
||||||
} else if slug.starts_with("gpt-5") {
|
} else if slug.starts_with("gpt-5") {
|
||||||
model_family!(
|
model_family!(
|
||||||
@@ -127,5 +132,6 @@ pub fn derive_default_model_family(model: &str) -> ModelFamily {
|
|||||||
uses_local_shell_tool: false,
|
uses_local_shell_tool: false,
|
||||||
apply_patch_tool_type: None,
|
apply_patch_tool_type: None,
|
||||||
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
||||||
|
experimental_supported_tools: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ pub(crate) struct ToolsConfig {
|
|||||||
pub web_search_request: bool,
|
pub web_search_request: bool,
|
||||||
pub include_view_image_tool: bool,
|
pub include_view_image_tool: bool,
|
||||||
pub experimental_unified_exec_tool: bool,
|
pub experimental_unified_exec_tool: bool,
|
||||||
|
pub experimental_supported_tools: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ToolsConfigParams<'a> {
|
pub(crate) struct ToolsConfigParams<'a> {
|
||||||
@@ -78,6 +79,7 @@ impl ToolsConfig {
|
|||||||
web_search_request: *include_web_search_request,
|
web_search_request: *include_web_search_request,
|
||||||
include_view_image_tool: *include_view_image_tool,
|
include_view_image_tool: *include_view_image_tool,
|
||||||
experimental_unified_exec_tool: *experimental_unified_exec_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 exec_stream_handler = Arc::new(ExecStreamHandler);
|
||||||
let unified_exec_handler = Arc::new(UnifiedExecHandler);
|
let unified_exec_handler = Arc::new(UnifiedExecHandler);
|
||||||
let plan_handler = Arc::new(PlanHandler);
|
let plan_handler = Arc::new(PlanHandler);
|
||||||
let read_file_handler = Arc::new(ReadFileHandler);
|
|
||||||
let apply_patch_handler = Arc::new(ApplyPatchHandler);
|
let apply_patch_handler = Arc::new(ApplyPatchHandler);
|
||||||
let view_image_handler = Arc::new(ViewImageHandler);
|
let view_image_handler = Arc::new(ViewImageHandler);
|
||||||
let mcp_handler = Arc::new(McpHandler);
|
let mcp_handler = Arc::new(McpHandler);
|
||||||
@@ -566,8 +567,15 @@ pub(crate) fn build_specs(
|
|||||||
builder.register_handler("apply_patch", apply_patch_handler);
|
builder.register_handler("apply_patch", apply_patch_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.push_spec(create_read_file_tool());
|
if config
|
||||||
builder.register_handler("read_file", read_file_handler);
|
.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 {
|
if config.web_search_request {
|
||||||
builder.push_spec(ToolSpec::WebSearch {});
|
builder.push_spec(ToolSpec::WebSearch {});
|
||||||
@@ -648,13 +656,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq_tool_names(
|
assert_eq_tool_names(
|
||||||
&tools,
|
&tools,
|
||||||
&[
|
&["unified_exec", "update_plan", "web_search", "view_image"],
|
||||||
"unified_exec",
|
|
||||||
"update_plan",
|
|
||||||
"read_file",
|
|
||||||
"web_search",
|
|
||||||
"view_image",
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,16 +676,28 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq_tool_names(
|
assert_eq_tool_names(
|
||||||
&tools,
|
&tools,
|
||||||
&[
|
&["unified_exec", "update_plan", "web_search", "view_image"],
|
||||||
"unified_exec",
|
|
||||||
"update_plan",
|
|
||||||
"read_file",
|
|
||||||
"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]
|
#[test]
|
||||||
fn test_build_specs_mcp_tools() {
|
fn test_build_specs_mcp_tools() {
|
||||||
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
|
let model_family = find_family_for_model("o3").expect("o3 should be a valid model family");
|
||||||
@@ -739,7 +753,6 @@ mod tests {
|
|||||||
&tools,
|
&tools,
|
||||||
&[
|
&[
|
||||||
"unified_exec",
|
"unified_exec",
|
||||||
"read_file",
|
|
||||||
"web_search",
|
"web_search",
|
||||||
"view_image",
|
"view_image",
|
||||||
"test_server/do_something_cool",
|
"test_server/do_something_cool",
|
||||||
@@ -747,7 +760,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tools[4],
|
tools[3],
|
||||||
ToolSpec::Function(ResponsesApiTool {
|
ToolSpec::Function(ResponsesApiTool {
|
||||||
name: "test_server/do_something_cool".to_string(),
|
name: "test_server/do_something_cool".to_string(),
|
||||||
parameters: JsonSchema::Object {
|
parameters: JsonSchema::Object {
|
||||||
@@ -858,7 +871,6 @@ mod tests {
|
|||||||
&tools,
|
&tools,
|
||||||
&[
|
&[
|
||||||
"unified_exec",
|
"unified_exec",
|
||||||
"read_file",
|
|
||||||
"view_image",
|
"view_image",
|
||||||
"test_server/cool",
|
"test_server/cool",
|
||||||
"test_server/do",
|
"test_server/do",
|
||||||
@@ -869,7 +881,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mcp_tool_property_missing_type_defaults_to_string() {
|
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 {
|
let config = ToolsConfig::new(&ToolsConfigParams {
|
||||||
model_family: &model_family,
|
model_family: &model_family,
|
||||||
include_plan_tool: false,
|
include_plan_tool: false,
|
||||||
@@ -937,7 +950,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mcp_tool_integer_normalized_to_number() {
|
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 {
|
let config = ToolsConfig::new(&ToolsConfigParams {
|
||||||
model_family: &model_family,
|
model_family: &model_family,
|
||||||
include_plan_tool: false,
|
include_plan_tool: false,
|
||||||
@@ -1000,7 +1014,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mcp_tool_array_without_items_gets_default_string_items() {
|
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 {
|
let config = ToolsConfig::new(&ToolsConfigParams {
|
||||||
model_family: &model_family,
|
model_family: &model_family,
|
||||||
include_plan_tool: false,
|
include_plan_tool: false,
|
||||||
@@ -1066,7 +1081,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mcp_tool_anyof_defaults_to_string() {
|
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 {
|
let config = ToolsConfig::new(&ToolsConfigParams {
|
||||||
model_family: &model_family,
|
model_family: &model_family,
|
||||||
include_plan_tool: false,
|
include_plan_tool: false,
|
||||||
@@ -1144,7 +1160,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_openai_tools_mcp_tools_with_additional_properties_schema() {
|
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 {
|
let config = ToolsConfig::new(&ToolsConfigParams {
|
||||||
model_family: &model_family,
|
model_family: &model_family,
|
||||||
include_plan_tool: false,
|
include_plan_tool: false,
|
||||||
|
|||||||
@@ -111,14 +111,21 @@ async fn model_selects_expected_tools() {
|
|||||||
let codex_tools = collect_tool_identifiers_for_model("codex-mini-latest").await;
|
let codex_tools = collect_tool_identifiers_for_model("codex-mini-latest").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
codex_tools,
|
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",
|
"codex-mini-latest should expose the local shell tool",
|
||||||
);
|
);
|
||||||
|
|
||||||
let o3_tools = collect_tool_identifiers_for_model("o3").await;
|
let o3_tools = collect_tool_identifiers_for_model("o3").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
o3_tools,
|
o3_tools,
|
||||||
vec!["shell".to_string(), "read_file".to_string()],
|
vec!["shell".to_string()],
|
||||||
"o3 should expose the generic shell tool",
|
"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",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user