mirror of
https://github.com/openai/codex.git
synced 2026-05-02 10:26:45 +00:00
Add js_repl_tools_only model and routing restrictions (#10671)
# External (non-OpenAI) Pull Request Requirements Before opening this Pull Request, please read the dedicated "Contributing" markdown file or your PR may be closed: https://github.com/openai/codex/blob/main/docs/contributing.md If your PR conforms to our contribution guidelines, replace this text with a detailed and high quality description of your changes. Include a link to a bug report or enhancement request. #### [git stack](https://github.com/magus/git-stack-cli) - ✅ `1` https://github.com/openai/codex/pull/10674 - ✅ `2` https://github.com/openai/codex/pull/10672 - 👉 `3` https://github.com/openai/codex/pull/10671 - ⏳ `4` https://github.com/openai/codex/pull/10673 - ⏳ `5` https://github.com/openai/codex/pull/10670
This commit is contained in:
committed by
GitHub
parent
a7ce2a1c31
commit
0dcfc59171
@@ -34,6 +34,7 @@ pub(crate) struct ToolsConfig {
|
||||
pub web_search_mode: Option<WebSearchMode>,
|
||||
pub search_tool: bool,
|
||||
pub js_repl_enabled: bool,
|
||||
pub js_repl_tools_only: bool,
|
||||
pub collab_tools: bool,
|
||||
pub collaboration_modes_tools: bool,
|
||||
pub request_rule_enabled: bool,
|
||||
@@ -55,6 +56,8 @@ impl ToolsConfig {
|
||||
} = params;
|
||||
let include_apply_patch_tool = features.enabled(Feature::ApplyPatchFreeform);
|
||||
let include_js_repl = features.enabled(Feature::JsRepl);
|
||||
let include_js_repl_tools_only =
|
||||
include_js_repl && features.enabled(Feature::JsReplToolsOnly);
|
||||
let include_collab_tools = features.enabled(Feature::Collab);
|
||||
let include_collaboration_modes_tools = features.enabled(Feature::CollaborationModes);
|
||||
let request_rule_enabled = features.enabled(Feature::RequestRule);
|
||||
@@ -91,6 +94,7 @@ impl ToolsConfig {
|
||||
web_search_mode: *web_search_mode,
|
||||
search_tool: include_search_tool,
|
||||
js_repl_enabled: include_js_repl,
|
||||
js_repl_tools_only: include_js_repl_tools_only,
|
||||
collab_tools: include_collab_tools,
|
||||
collaboration_modes_tools: include_collaboration_modes_tools,
|
||||
request_rule_enabled,
|
||||
@@ -99,8 +103,15 @@ impl ToolsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_tools_for_model(tools: Vec<ToolSpec>, _config: &ToolsConfig) -> Vec<ToolSpec> {
|
||||
pub(crate) fn filter_tools_for_model(tools: Vec<ToolSpec>, config: &ToolsConfig) -> Vec<ToolSpec> {
|
||||
if !config.js_repl_tools_only {
|
||||
return tools;
|
||||
}
|
||||
|
||||
tools
|
||||
.into_iter()
|
||||
.filter(|spec| matches!(spec.name(), "js_repl" | "js_repl_reset"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Generic JSON‑Schema subset needed for our tool definitions
|
||||
@@ -1685,6 +1696,27 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_contains_tool_specs(tools: &[ToolSpec], expected_subset: &[&str]) {
|
||||
use std::collections::HashSet;
|
||||
let mut names = HashSet::new();
|
||||
let mut duplicates = Vec::new();
|
||||
for name in tools.iter().map(tool_name) {
|
||||
if !names.insert(name) {
|
||||
duplicates.push(name);
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
duplicates.is_empty(),
|
||||
"duplicate tool entries detected: {duplicates:?}"
|
||||
);
|
||||
for expected in expected_subset {
|
||||
assert!(
|
||||
names.contains(expected),
|
||||
"expected tool {expected} to be present; had: {names:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn shell_tool_name(config: &ToolsConfig) -> Option<&'static str> {
|
||||
match config.shell_type {
|
||||
ConfigShellToolType::Default => Some("shell"),
|
||||
@@ -1910,6 +1942,77 @@ mod tests {
|
||||
assert_contains_tool_names(&tools, &["js_repl", "js_repl_reset"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn js_repl_tools_only_filters_model_tools() {
|
||||
let config = test_config();
|
||||
let model_info =
|
||||
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::JsRepl);
|
||||
features.enable(Feature::JsReplToolsOnly);
|
||||
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
features: &features,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, &[]).build();
|
||||
let filtered = filter_tools_for_model(
|
||||
tools.iter().map(|tool| tool.spec.clone()).collect(),
|
||||
&tools_config,
|
||||
);
|
||||
assert_contains_tool_specs(&filtered, &["js_repl", "js_repl_reset"]);
|
||||
assert!(
|
||||
!filtered.iter().any(|tool| tool_name(tool) == "shell"),
|
||||
"expected non-js_repl tools to be hidden when js_repl_tools_only is enabled"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn js_repl_tools_only_hides_dynamic_tools_from_model_tools() {
|
||||
let config = test_config();
|
||||
let model_info =
|
||||
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::JsRepl);
|
||||
features.enable(Feature::JsReplToolsOnly);
|
||||
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
features: &features,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
});
|
||||
let dynamic_tools = vec![DynamicToolSpec {
|
||||
name: "dynamic_echo".to_string(),
|
||||
description: "echo dynamic payload".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string"}
|
||||
},
|
||||
"required": ["text"],
|
||||
"additionalProperties": false
|
||||
}),
|
||||
}];
|
||||
let (tools, _) = build_specs(&tools_config, None, &dynamic_tools).build();
|
||||
assert!(
|
||||
tools.iter().any(|tool| tool.spec.name() == "dynamic_echo"),
|
||||
"expected dynamic tool in full router specs"
|
||||
);
|
||||
|
||||
let filtered = filter_tools_for_model(
|
||||
tools.iter().map(|tool| tool.spec.clone()).collect(),
|
||||
&tools_config,
|
||||
);
|
||||
assert!(
|
||||
!filtered
|
||||
.iter()
|
||||
.any(|tool| tool_name(tool) == "dynamic_echo"),
|
||||
"expected dynamic tools to be hidden from direct model tools in js_repl_tools_only mode"
|
||||
);
|
||||
assert_contains_tool_specs(&filtered, &["js_repl", "js_repl_reset"]);
|
||||
}
|
||||
|
||||
fn assert_model_tools(
|
||||
model_slug: &str,
|
||||
features: &Features,
|
||||
|
||||
Reference in New Issue
Block a user