fix(code_execution): handle model quirks with tool calls (#6352)
Signed-off-by: rabi <ramishra@redhat.com>
This commit is contained in:
@@ -466,6 +466,8 @@ impl CodeExecutionClient {
|
|||||||
- WRONG: Multiple execute_code calls, each with one tool
|
- WRONG: Multiple execute_code calls, each with one tool
|
||||||
- RIGHT: One execute_code call with a script that calls all needed tools
|
- RIGHT: One execute_code call with a script that calls all needed tools
|
||||||
|
|
||||||
|
IMPORTANT: All tool calls are SYNCHRONOUS. Do NOT use async/await.
|
||||||
|
|
||||||
Workflow:
|
Workflow:
|
||||||
1. Use the read_module tool to discover tools and signatures
|
1. Use the read_module tool to discover tools and signatures
|
||||||
2. Write ONE script that imports and calls ALL tools needed for the task
|
2. Write ONE script that imports and calls ALL tools needed for the task
|
||||||
@@ -572,12 +574,16 @@ impl CodeExecutionClient {
|
|||||||
.and_then(|a| a.get("terms"))
|
.and_then(|a| a.get("terms"))
|
||||||
.ok_or("Missing required parameter: terms")?;
|
.ok_or("Missing required parameter: terms")?;
|
||||||
|
|
||||||
let terms_vec = if let Some(s) = terms.as_str() {
|
let terms_vec = if let Some(arr) = terms.as_array() {
|
||||||
vec![s.to_string()]
|
|
||||||
} else if let Some(arr) = terms.as_array() {
|
|
||||||
arr.iter()
|
arr.iter()
|
||||||
.filter_map(|v| v.as_str().map(String::from))
|
.filter_map(|v| v.as_str().map(String::from))
|
||||||
.collect()
|
.collect()
|
||||||
|
} else if let Some(s) = terms.as_str() {
|
||||||
|
if s.starts_with('[') && s.ends_with(']') {
|
||||||
|
serde_json::from_str::<Vec<String>>(s).unwrap_or_else(|_| vec![s.to_string()])
|
||||||
|
} else {
|
||||||
|
vec![s.to_string()]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err("Parameter 'terms' must be a string or array of strings".to_string());
|
return Err("Parameter 'terms' must be a string or array of strings".to_string());
|
||||||
};
|
};
|
||||||
@@ -830,9 +836,11 @@ impl McpClientTrait for CodeExecutionClient {
|
|||||||
Search for tools by name or description across all available modules.
|
Search for tools by name or description across all available modules.
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
- Single term: search_modules with terms="file"
|
- Single term: terms="github" (just a plain string)
|
||||||
- Multiple terms: search_modules with terms=["git", "shell"]
|
- Multiple terms: terms=["git", "shell"] (a JSON array, NOT a string)
|
||||||
- Regex patterns: search_modules with terms="sh.*", regex=true
|
- Regex patterns: terms="sh.*", regex=true
|
||||||
|
|
||||||
|
IMPORTANT: Do NOT stringify arrays. Use terms=["a","b"] not terms="[\"a\",\"b\"]"
|
||||||
|
|
||||||
Returns matching servers and tools with descriptions.
|
Returns matching servers and tools with descriptions.
|
||||||
Use this when you don't know which module contains the tool you need.
|
Use this when you don't know which module contains the tool you need.
|
||||||
|
|||||||
@@ -1135,17 +1135,30 @@ impl ExtensionManager {
|
|||||||
tool_call: CallToolRequestParam,
|
tool_call: CallToolRequestParam,
|
||||||
cancellation_token: CancellationToken,
|
cancellation_token: CancellationToken,
|
||||||
) -> Result<ToolCallResult> {
|
) -> Result<ToolCallResult> {
|
||||||
|
// Some models strip the tool prefix, so auto-add it for known code_execution tools
|
||||||
|
let tool_name_str = tool_call.name.to_string();
|
||||||
|
let prefixed_name = if !tool_name_str.contains("__") {
|
||||||
|
let code_exec_tools = ["execute_code", "read_module", "search_modules"];
|
||||||
|
if code_exec_tools.contains(&tool_name_str.as_str())
|
||||||
|
&& self.extensions.lock().await.contains_key("code_execution")
|
||||||
|
{
|
||||||
|
format!("code_execution__{}", tool_name_str)
|
||||||
|
} else {
|
||||||
|
tool_name_str
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tool_name_str
|
||||||
|
};
|
||||||
|
|
||||||
// Dispatch tool call based on the prefix naming convention
|
// Dispatch tool call based on the prefix naming convention
|
||||||
let (client_name, client) =
|
let (client_name, client) =
|
||||||
self.get_client_for_tool(&tool_call.name)
|
self.get_client_for_tool(&prefixed_name)
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
ErrorData::new(ErrorCode::RESOURCE_NOT_FOUND, tool_call.name.clone(), None)
|
ErrorData::new(ErrorCode::RESOURCE_NOT_FOUND, tool_call.name.clone(), None)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// rsplit returns the iterator in reverse, tool_name is then at 0
|
let tool_name = prefixed_name
|
||||||
let tool_name = tool_call
|
|
||||||
.name
|
|
||||||
.strip_prefix(client_name.as_str())
|
.strip_prefix(client_name.as_str())
|
||||||
.and_then(|s| s.strip_prefix("__"))
|
.and_then(|s| s.strip_prefix("__"))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
|
|||||||
Reference in New Issue
Block a user