use std::collections::HashMap;
use regex::RegexBuilder;
use serde_json::Value;
use uuid::Uuid;
use super::config::JsonParserConfig;
use super::response::{CalledFunction, ToolCallResponse, ToolCallType};
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct CalledFunctionParameters {
pub name: String,
pub parameters: HashMap<String, Value>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct CalledFunctionArguments {
pub name: String,
pub arguments: HashMap<String, Value>,
}
fn extract_tool_call_content(input: &str, start_token: &str, end_token: &str) -> Option<String> {
let escaped_start = regex::escape(start_token);
let escaped_end = regex::escape(end_token);
let pattern = format!(r"{}(.*?){}", escaped_start, escaped_end);
match RegexBuilder::new(&pattern)
.dot_matches_new_line(true)
.build()
{
Ok(regex) => {
let matches: Vec<_> = regex
.captures_iter(input)
.filter_map(|captures| captures.get(1))
.map(|m| m.as_str().trim().to_string())
.collect();
if !matches.is_empty() {
if matches.len() == 1 {
return Some(matches.last().unwrap().clone());
} else {
return Some(format!("[{}]", matches.join(",")));
}
}
None
}
Err(_) => None,
}
}
fn handle_single_token_tool_calls(input: &str, start_token: &str) -> Option<String> {
if !input.contains(start_token) {
return None;
}
let mut items: Vec<String> = Vec::new();
for seg in input.split(start_token) {
let s = seg.trim();
if s.is_empty() {
continue;
}
if s.starts_with('{') {
if let Some(pos) = s.rfind('}') {
let candidate = &s[..=pos].trim();
if serde_json::from_str::<serde_json::Value>(candidate).is_ok() {
items.push(candidate.to_string());
}
}
}
}
if items.is_empty() {
if let Some(idx) = input.find(start_token) {
let rest = &input[idx + start_token.len()..];
return Some(rest.trim_start().to_string());
} else {
return None;
}
}
Some(format!("[{}]", items.join(",")))
}
fn try_parse_normal_text(input: &str, start_token: &str) -> String {
if let Some(idx) = input.find(start_token) {
return input[..idx].trim().to_string();
}
String::new()
}
pub fn try_tool_call_parse_basic_json(
message: &str,
config: &JsonParserConfig,
) -> anyhow::Result<(Vec<ToolCallResponse>, Option<String>)> {
tracing::debug!("Using JSON parser config: {:?}", config);
let trimmed = message.trim();
if trimmed.is_empty() {
return Ok((vec![], Some(String::new())));
}
let tool_call_start_tokens = &config.tool_call_start_tokens;
let tool_call_end_tokens = &config.tool_call_end_tokens;
if tool_call_start_tokens.is_empty() {
return Ok((vec![], Some(trimmed.to_string())));
}
let mut json = trimmed.to_string();
let mut normal_text = trimmed.to_string();
let has_start_token = tool_call_start_tokens
.iter()
.any(|token| !token.is_empty() && normal_text.contains(token));
if !has_start_token {
if let Some(idx) = normal_text.find(['{', '[']) {
let extracted_normal = normal_text[..idx].trim().to_string();
let extracted_json = normal_text[idx..].trim().to_string();
if !extracted_json.is_empty() {
normal_text = extracted_normal;
json = extracted_json;
}
}
} else {
for (start_token, end_token) in tool_call_start_tokens
.iter()
.zip(tool_call_end_tokens.iter())
{
let new_normal_text = try_parse_normal_text(&normal_text, start_token);
match (start_token.is_empty(), end_token.is_empty()) {
(false, true) => {
let result = handle_single_token_tool_calls(&json, start_token);
if let Some(content) = result {
json = content;
normal_text = new_normal_text;
break; }
}
(false, false) => {
let result = extract_tool_call_content(&json, start_token, end_token);
if let Some(content) = result {
json = content;
normal_text = new_normal_text;
break; }
}
_ => {
continue;
}
}
}
}
let json = json.as_str();
let parse = |name: String, args: HashMap<String, Value>| -> anyhow::Result<_> {
Ok(ToolCallResponse {
id: format!("call-{}", Uuid::new_v4()),
tp: ToolCallType::Function,
function: CalledFunction {
name,
arguments: serde_json::to_string(&args)?,
},
})
};
if let Ok(single) = serde_json::from_str::<CalledFunctionParameters>(json) {
return Ok((
vec![parse(single.name, single.parameters)?],
Some(normal_text),
));
} else if let Ok(single) = serde_json::from_str::<CalledFunctionArguments>(json) {
return Ok((
vec![parse(single.name, single.arguments)?],
Some(normal_text),
));
} else if let Ok(list) = serde_json::from_str::<Vec<CalledFunctionParameters>>(json) {
let mut results = Vec::new();
for item in list {
results.push(parse(item.name, item.parameters)?);
}
return Ok((results, Some(normal_text)));
} else if let Ok(list) = serde_json::from_str::<Vec<CalledFunctionArguments>>(json) {
let mut results = Vec::new();
for item in list {
results.push(parse(item.name, item.arguments)?);
}
return Ok((results, Some(normal_text)));
}
Ok((vec![], Some(trimmed.to_string())))
}