use std::{collections::HashMap, sync::Arc};
use parking_lot::RwLock;
use tokio::sync::Mutex;
use crate::{
parsers::{
CohereParser, DeepSeek31Parser, DeepSeekParser, Glm4MoeParser, JsonParser, KimiK2Parser,
LlamaParser, MinimaxM2Parser, MistralParser, PassthroughParser, PythonicParser,
QwenCoderParser, QwenParser, Step3Parser,
},
traits::ToolParser,
};
pub type PooledParser = Arc<Mutex<Box<dyn ToolParser>>>;
type ParserCreator = Arc<dyn Fn() -> Box<dyn ToolParser> + Send + Sync>;
#[derive(Clone)]
pub struct ParserRegistry {
creators: Arc<RwLock<HashMap<String, ParserCreator>>>,
pool: Arc<RwLock<HashMap<String, PooledParser>>>,
model_mapping: Arc<RwLock<HashMap<String, String>>>,
default_parser: Arc<RwLock<String>>,
}
impl ParserRegistry {
pub fn new() -> Self {
Self {
creators: Arc::new(RwLock::new(HashMap::new())),
pool: Arc::new(RwLock::new(HashMap::new())),
model_mapping: Arc::new(RwLock::new(HashMap::new())),
default_parser: Arc::new(RwLock::new("passthrough".to_string())),
}
}
pub fn register_parser<F>(&self, name: &str, creator: F)
where
F: Fn() -> Box<dyn ToolParser> + Send + Sync + 'static,
{
let mut creators = self.creators.write();
creators.insert(name.to_string(), Arc::new(creator));
}
pub fn map_model(&self, model: impl Into<String>, parser: impl Into<String>) {
let mut mapping = self.model_mapping.write();
mapping.insert(model.into(), parser.into());
}
pub fn get_pooled_parser(&self, name: &str) -> Option<PooledParser> {
{
let pool = self.pool.read();
if let Some(parser) = pool.get(name) {
return Some(Arc::clone(parser));
}
}
let creators = self.creators.read();
if let Some(creator) = creators.get(name) {
let parser = Arc::new(Mutex::new(creator()));
let mut pool = self.pool.write();
pool.insert(name.to_string(), Arc::clone(&parser));
Some(parser)
} else {
None
}
}
pub fn has_parser(&self, name: &str) -> bool {
let creators = self.creators.read();
creators.contains_key(name)
}
pub fn create_parser(&self, name: &str) -> Option<Box<dyn ToolParser>> {
let creators = self.creators.read();
creators.get(name).map(|creator| creator())
}
pub fn has_parser_for_model(&self, model: &str) -> bool {
{
let mapping = self.model_mapping.read();
if let Some(parser_name) = mapping.get(model) {
let creators = self.creators.read();
if creators.contains_key(parser_name) {
return true;
}
}
}
let model_mapping = self.model_mapping.read();
let best_match = model_mapping
.iter()
.filter(|(pattern, _)| {
pattern.ends_with('*') && model.starts_with(&pattern[..pattern.len() - 1])
})
.max_by_key(|(pattern, _)| pattern.len());
if let Some((_, parser_name)) = best_match {
let creators = self.creators.read();
if creators.contains_key(parser_name) {
return true;
}
}
false
}
pub fn create_for_model(&self, model: &str) -> Option<Box<dyn ToolParser>> {
{
let mapping = self.model_mapping.read();
if let Some(parser_name) = mapping.get(model) {
if let Some(parser) = self.create_parser(parser_name) {
return Some(parser);
}
}
}
let model_mapping = self.model_mapping.read();
let best_match = model_mapping
.iter()
.filter(|(pattern, _)| {
pattern.ends_with('*') && model.starts_with(&pattern[..pattern.len() - 1])
})
.max_by_key(|(pattern, _)| pattern.len());
if let Some((_, parser_name)) = best_match {
if let Some(parser) = self.create_parser(parser_name) {
return Some(parser);
}
}
let default = self.default_parser.read().clone();
self.create_parser(&default)
}
pub fn get_pooled_for_model(&self, model: &str) -> Option<PooledParser> {
{
let mapping = self.model_mapping.read();
if let Some(parser_name) = mapping.get(model) {
if let Some(parser) = self.get_pooled_parser(parser_name) {
return Some(parser);
}
}
}
let model_mapping = self.model_mapping.read();
let best_match = model_mapping
.iter()
.filter(|(pattern, _)| {
pattern.ends_with('*') && model.starts_with(&pattern[..pattern.len() - 1])
})
.max_by_key(|(pattern, _)| pattern.len());
if let Some((_, parser_name)) = best_match {
if let Some(parser) = self.get_pooled_parser(parser_name) {
return Some(parser);
}
}
let default = self.default_parser.read().clone();
self.get_pooled_parser(&default)
}
pub fn clear_pool(&self) {
let mut pool = self.pool.write();
pool.clear();
}
pub fn set_default_parser(&self, name: impl Into<String>) {
let mut default = self.default_parser.write();
*default = name.into();
}
}
impl Default for ParserRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct ParserFactory {
registry: ParserRegistry,
}
impl ParserFactory {
pub fn new() -> Self {
let registry = ParserRegistry::new();
registry.register_parser("passthrough", || Box::new(PassthroughParser::new()));
registry.register_parser("json", || Box::new(JsonParser::new()));
registry.register_parser("mistral", || Box::new(MistralParser::new()));
registry.register_parser("qwen", || Box::new(QwenParser::new()));
registry.register_parser("qwen_coder", || Box::new(QwenCoderParser::new()));
registry.register_parser("pythonic", || Box::new(PythonicParser::new()));
registry.register_parser("llama", || Box::new(LlamaParser::new()));
registry.register_parser("deepseek", || Box::new(DeepSeekParser::new()));
registry.register_parser("deepseek31", || Box::new(DeepSeek31Parser::new()));
registry.register_parser("glm45_moe", || Box::new(Glm4MoeParser::glm45()));
registry.register_parser("glm47_moe", || Box::new(Glm4MoeParser::glm47()));
registry.register_parser("step3", || Box::new(Step3Parser::new()));
registry.register_parser("kimik2", || Box::new(KimiK2Parser::new()));
registry.register_parser("minimax_m2", || Box::new(MinimaxM2Parser::new()));
registry.register_parser("cohere", || Box::new(CohereParser::new()));
Self::register_default_mappings(®istry);
Self { registry }
}
fn register_default_mappings(registry: &ParserRegistry) {
registry.map_model("gpt-4*", "json");
registry.map_model("gpt-3.5*", "json");
registry.map_model("gpt-4o*", "json");
registry.map_model("claude-*", "json");
registry.map_model("mistral-*", "mistral");
registry.map_model("mixtral-*", "mistral");
registry.map_model("Qwen/Qwen3-Coder*", "qwen_coder");
registry.map_model("Qwen3-Coder*", "qwen_coder");
registry.map_model("qwen3-coder*", "qwen_coder");
registry.map_model("Qwen/Qwen2.5-Coder*", "qwen_coder");
registry.map_model("Qwen2.5-Coder*", "qwen_coder");
registry.map_model("qwen2.5-coder*", "qwen_coder");
registry.map_model("qwen*", "qwen");
registry.map_model("Qwen*", "qwen");
registry.map_model("llama-4*", "pythonic");
registry.map_model("meta-llama-4*", "pythonic");
registry.map_model("llama-3.2*", "llama");
registry.map_model("meta-llama-3.2*", "llama");
registry.map_model("llama-*", "json");
registry.map_model("meta-llama-*", "json");
registry.map_model("deepseek-v3*", "deepseek");
registry.map_model("deepseek-ai/DeepSeek-V3*", "deepseek");
registry.map_model("deepseek-v3.1*", "deepseek31");
registry.map_model("deepseek-ai/DeepSeek-V3.1*", "deepseek31");
registry.map_model("deepseek-*", "pythonic");
registry.map_model("glm-4.5*", "glm45_moe");
registry.map_model("glm-4.6*", "glm45_moe");
registry.map_model("glm-4.7*", "glm47_moe");
registry.map_model("glm-*", "json");
registry.map_model("step3*", "step3");
registry.map_model("Step-3*", "step3");
registry.map_model("kimi-k2*", "kimik2");
registry.map_model("Kimi-K2*", "kimik2");
registry.map_model("moonshot*/Kimi-K2*", "kimik2");
registry.map_model("minimax*", "minimax_m2");
registry.map_model("MiniMax*", "minimax_m2");
registry.map_model("command-r*", "cohere");
registry.map_model("command-r-plus*", "cohere");
registry.map_model("command-a*", "cohere");
registry.map_model("c4ai-command*", "cohere");
registry.map_model("cohere*", "cohere");
registry.map_model("CohereForAI*", "cohere");
registry.map_model("gemini-*", "json");
registry.map_model("palm-*", "json");
registry.map_model("gemma-*", "json");
}
#[expect(
clippy::expect_used,
reason = "passthrough parser is registered in new(); None indicates a bug in registration logic"
)]
pub fn get_pooled(&self, model_id: &str) -> PooledParser {
self.registry
.get_pooled_for_model(model_id)
.unwrap_or_else(|| {
self.registry
.get_pooled_parser("passthrough")
.expect("Passthrough parser should always be registered")
})
}
pub fn registry(&self) -> &ParserRegistry {
&self.registry
}
pub fn clear_pool(&self) {
self.registry.clear_pool();
}
pub fn get_parser(&self, model_id: &str) -> Option<Arc<dyn ToolParser>> {
let parser_type = {
let mapping = self.registry.model_mapping.read();
if let Some(parser_name) = mapping.get(model_id) {
parser_name.clone()
} else {
let best_match = mapping
.iter()
.filter(|(pattern, _)| {
pattern.ends_with('*')
&& model_id.starts_with(&pattern[..pattern.len() - 1])
})
.max_by_key(|(pattern, _)| pattern.len());
if let Some((_, parser_name)) = best_match {
parser_name.clone()
} else {
self.registry.default_parser.read().clone()
}
}
};
let creators = self.registry.creators.read();
creators.get(&parser_type).map(|creator| {
let boxed_parser = creator();
Arc::from(boxed_parser)
})
}
pub fn has_parser(&self, name: &str) -> bool {
self.registry.creators.read().contains_key(name)
}
pub fn list_parsers(&self) -> Vec<String> {
self.registry.creators.read().keys().cloned().collect()
}
}
impl Default for ParserFactory {
fn default() -> Self {
Self::new()
}
}