#![allow(unused_imports, dead_code)]
use super::*;
pub(super) fn parse_qwen3_coder_call(text: &str, _idx: u32) -> Option<ToolCall> {
let (func_start, prefix_len) = if let Some(pos) = text.find("<function=") {
(pos, "<function=".len())
} else if let Some(pos) = text.find("<|function=") {
(pos, "<|function=".len())
} else if let Some(pos) = text.find("<function ") {
(pos, "<function ".len())
} else if let Some(pos) = text.find("<|function ") {
(pos, "<|function ".len())
} else {
return None;
};
let name_start = func_start + prefix_len;
let name_end = name_start
+ text[name_start..]
.find(['>', '\n', '<'])
.unwrap_or(text[name_start..].len());
let func_name = normalize_tool_name(&text[name_start..name_end]);
if func_name.is_empty() {
return None;
}
let mut args = serde_json::Map::new();
let after_name = if name_end < text.len() {
&text[name_end + 1..]
} else {
""
};
let mut rest = after_name;
while let Some(p) = rest.find("<parameter=") {
if let Some(fc) = rest.find("</function>")
&& fc < p
{
break;
}
rest = &rest[p + "<parameter=".len()..];
let key_end = rest.find('>')?;
let param_name = rest[..key_end].trim().to_string();
rest = &rest[key_end + 1..];
let proper = rest.find("</parameter>");
let next_param = rest.find("<parameter=");
let func_close = rest.find("</function>");
let mut val_end = rest.len();
let mut consumed_close = false;
if let Some(p) = proper {
val_end = p;
consumed_close = true;
}
for cand in [next_param, func_close].into_iter().flatten() {
if cand < val_end {
val_end = cand;
consumed_close = false;
}
}
let raw_value = rest[..val_end].trim();
let raw_value = if !consumed_close {
raw_value
.strip_suffix("</parameter")
.map(str::trim_end)
.unwrap_or(raw_value)
} else {
raw_value
};
let advanced_to_func_close =
!consumed_close && val_end < rest.len() && rest[val_end..].starts_with("</function>");
rest = if consumed_close {
&rest[val_end + "</parameter>".len()..]
} else if val_end < rest.len() {
&rest[val_end..]
} else {
""
};
args.insert(param_name, serde_json::Value::String(raw_value.to_string()));
if advanced_to_func_close {
break;
}
}
if args.is_empty() {
let body = after_name
.find("</function>")
.map(|end| after_name[..end].trim())
.unwrap_or(after_name.trim());
if body.starts_with('{')
&& let Ok(json_args) =
serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(body)
{
args = json_args;
}
}
args.remove("_think");
Some(ToolCall {
id: next_tool_call_id(),
call_type: "function".into(),
function: FunctionCall {
name: func_name,
arguments: serde_json::to_string(&serde_json::Value::Object(args))
.unwrap_or_else(|_| "{}".into()),
},
})
}
pub(super) fn parse_tag_style_call(text: &str, _idx: u32) -> Option<ToolCall> {
let func_start = text.find("<function>")?;
let after_tag = &text[func_start + "<function>".len()..];
let close = after_tag.find("</function>")?;
let raw_name = after_tag[..close].trim();
let func_name = if raw_name.contains('<') {
normalize_tool_name(&extract_tag_value(raw_name, "name")?)
} else if raw_name.is_empty() {
return None;
} else {
normalize_tool_name(raw_name)
};
let mut args = serde_json::Map::new();
let rest = &after_tag[close + "</function>".len()..];
if let Some(ps) = rest.find("<parameters>") {
let params_inner = &rest[ps + "<parameters>".len()..];
let pe = params_inner
.find("</parameters>")
.unwrap_or(params_inner.len());
extract_params_from_block(¶ms_inner[..pe], &mut args);
}
Some(ToolCall {
id: next_tool_call_id(),
call_type: "function".into(),
function: FunctionCall {
name: func_name,
arguments: serde_json::to_string(&serde_json::Value::Object(args))
.unwrap_or_else(|_| "{}".into()),
},
})
}
fn extract_tag_value(text: &str, tag: &str) -> Option<String> {
let open = format!("<{tag}>");
let close = format!("</{tag}>");
let start = text.find(&open)? + open.len();
let end = start + text[start..].find(&close)?;
let val = text[start..end].trim();
if val.is_empty() {
None
} else {
Some(val.to_string())
}
}
fn extract_params_from_block(block: &str, args: &mut serde_json::Map<String, serde_json::Value>) {
let mut rest = block;
let mut found_any = false;
while let Some(ps) = rest.find("<parameter>") {
rest = &rest[ps + "<parameter>".len()..];
let pe = rest.find("</parameter>").unwrap_or(rest.len());
let param_block = &rest[..pe];
if let (Some(name), Some(value)) = (
extract_tag_value(param_block, "name"),
extract_tag_value(param_block, "value"),
) {
let json_val = serde_json::from_str::<serde_json::Value>(&value)
.unwrap_or(serde_json::Value::String(value));
args.insert(name, json_val);
found_any = true;
}
rest = if pe < rest.len() {
&rest[pe + "</parameter>".len()..]
} else {
""
};
}
if found_any {
return;
}
if let (Some(name), Some(value)) = (
extract_tag_value(block, "name"),
extract_tag_value(block, "value"),
) {
let json_val = serde_json::from_str::<serde_json::Value>(&value)
.unwrap_or(serde_json::Value::String(value));
args.insert(name, json_val);
}
}
pub(super) fn bare_function_end(text: &str) -> Option<usize> {
if text.starts_with("<function>") {
if let Some(p) = text.find("</parameters>") {
return Some(p + "</parameters>".len());
}
if text.contains("<parameters>") {
return None;
}
if let Some(p) = text.find("</function>") {
let after = p + "</function>".len();
if let Some(p2) = text[after..].find("</function>") {
return Some(after + p2 + "</function>".len());
}
return Some(after);
}
} else if text.starts_with("<function=") || text.starts_with("<function ") {
if let Some(p) = text.find("</function>") {
return Some(p + "</function>".len());
}
}
None
}