Skip to main content

tycode_core/chat/
tool_extraction.rs

1use crate::ai::types::{Content, ToolUseData};
2
3use super::json_tool_parser::parse_json_tool_calls;
4use super::xml_tool_parser::parse_xml_tool_calls;
5
6/// Result of extracting tool calls from AI response content.
7/// Combines native API tool calls with XML and JSON parsed calls from text.
8pub struct ToolExtractionResult {
9    /// All extracted tool calls (native + XML + JSON)
10    pub tool_calls: Vec<ToolUseData>,
11    /// Remaining display text after removing parsed tool call blocks
12    pub display_text: String,
13    /// Error from XML parsing, if any
14    pub xml_parse_error: Option<String>,
15    /// Error from JSON parsing, if any
16    pub json_parse_error: Option<String>,
17}
18
19/// Extract all tool calls from AI response content using three strategies:
20/// 1. Native tool use blocks from the API response
21/// 2. XML function_calls blocks parsed from text
22/// 3. JSON tool_use structures parsed from remaining text
23pub fn extract_all_tool_calls(content: &Content) -> ToolExtractionResult {
24    let native_tool_calls: Vec<_> = content.tool_uses().iter().map(|t| (*t).clone()).collect();
25
26    let (xml_tool_calls, text_after_xml, xml_parse_error) =
27        match parse_xml_tool_calls(&content.text()) {
28            Ok((calls, remaining)) => (calls, remaining, None),
29            Err(e) => (vec![], content.text(), Some(format!("{e:?}"))),
30        };
31
32    let (json_tool_calls, display_text, json_parse_error) =
33        match parse_json_tool_calls(&text_after_xml) {
34            Ok((calls, remaining)) => (calls, remaining, None),
35            Err(e) => (vec![], text_after_xml, Some(format!("{e:?}"))),
36        };
37
38    let mut tool_calls = native_tool_calls;
39    tool_calls.extend(xml_tool_calls);
40    tool_calls.extend(json_tool_calls);
41
42    ToolExtractionResult {
43        tool_calls,
44        display_text,
45        xml_parse_error,
46        json_parse_error,
47    }
48}