#![allow(dead_code)]
#[macro_use]
mod error;
mod glm_xml;
mod json;
mod parameters;
mod utils;
use std::collections::{BTreeMap, btree_map};
pub use error::{Result, ToolParserError};
pub use glm_xml::Glm47MoeToolParser;
pub use json::{HermesToolParser, Qwen3XmlToolParser};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Tool {
pub name: String,
pub description: Option<String>,
pub parameters: Value,
pub strict: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToolCallDelta {
pub tool_index: usize,
pub name: Option<String>,
pub arguments: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ToolParserOutput {
pub normal_text: String,
pub calls: Vec<ToolCallDelta>,
}
pub type ToolParseResult = ToolParserOutput;
impl ToolParserOutput {
pub fn append(&mut self, mut other: Self) {
self.normal_text.push_str(&other.normal_text);
self.calls.append(&mut other.calls);
}
pub fn coalesce_calls(mut self) -> Self {
let mut merged = BTreeMap::<usize, ToolCallDelta>::new();
let mut order = Vec::new();
for call in self.calls {
match merged.entry(call.tool_index) {
btree_map::Entry::Vacant(entry) => {
order.push(call.tool_index);
entry.insert(call);
}
btree_map::Entry::Occupied(mut entry) => {
let existing = entry.get_mut();
if existing.name.is_none() {
existing.name = call.name;
}
existing.arguments.push_str(&call.arguments);
}
}
}
self.calls = order
.into_iter()
.filter_map(|tool_index| merged.remove(&tool_index))
.collect();
self
}
}
pub trait ToolParser: Send {
fn create(tools: &[Tool]) -> Result<Box<dyn ToolParser>>
where
Self: Sized + 'static;
fn preserve_special_tokens(&self) -> bool {
false
}
fn tool_call_id(&self, _tool_index: usize) -> Option<&str> {
None
}
fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()>;
fn push(&mut self, chunk: &str) -> Result<ToolParseResult> {
let mut output = ToolParserOutput::default();
self.parse_into(chunk, &mut output)?;
Ok(output)
}
fn finish(&mut self) -> Result<ToolParserOutput>;
fn reset(&mut self) -> String;
fn parse_complete(&mut self, text: &str) -> Result<ToolParserOutput> {
let mut output = self.push(text)?;
output.append(self.finish()?);
Ok(output.coalesce_calls())
}
}