[router] global tool parser registry (#9840)
This commit is contained in:
@@ -64,6 +64,7 @@ prost-types = "0.13"
|
|||||||
deadpool = { version = "0.12", features = ["managed", "rt_tokio_1"] }
|
deadpool = { version = "0.12", features = ["managed", "rt_tokio_1"] }
|
||||||
backoff = { version = "0.4", features = ["tokio"] }
|
backoff = { version = "0.4", features = ["tokio"] }
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
|
once_cell = "1.21.3"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.12"
|
tonic-build = "0.12"
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ use crate::tool_parser::{
|
|||||||
pub struct PythonicParser {
|
pub struct PythonicParser {
|
||||||
/// Regex to detect tool calls in Pythonic format
|
/// Regex to detect tool calls in Pythonic format
|
||||||
tool_call_regex: Regex,
|
tool_call_regex: Regex,
|
||||||
|
/// Regex to parse function calls - cached for reuse
|
||||||
|
call_regex: Regex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PythonicParser {
|
impl PythonicParser {
|
||||||
@@ -33,7 +35,13 @@ impl PythonicParser {
|
|||||||
let pattern = r"\[[a-zA-Z_]\w*\(";
|
let pattern = r"\[[a-zA-Z_]\w*\(";
|
||||||
let tool_call_regex = Regex::new(pattern).expect("Valid regex pattern");
|
let tool_call_regex = Regex::new(pattern).expect("Valid regex pattern");
|
||||||
|
|
||||||
Self { tool_call_regex }
|
// Compile the function call regex once
|
||||||
|
let call_regex = Regex::new(r"(?s)^([a-zA-Z_]\w*)\((.*)\)$").expect("Valid regex pattern");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
tool_call_regex,
|
||||||
|
call_regex,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract tool calls using bracket counting (similar to MistralParser)
|
/// Extract tool calls using bracket counting (similar to MistralParser)
|
||||||
@@ -120,10 +128,8 @@ impl PythonicParser {
|
|||||||
|
|
||||||
/// Parse a single function call from Python syntax
|
/// Parse a single function call from Python syntax
|
||||||
fn parse_function_call(&self, call_str: &str) -> ToolParserResult<Option<ToolCall>> {
|
fn parse_function_call(&self, call_str: &str) -> ToolParserResult<Option<ToolCall>> {
|
||||||
// Match function_name(args) - use (?s) to make . match newlines
|
// Use cached regex instead of creating new one
|
||||||
let call_regex = Regex::new(r"(?s)^([a-zA-Z_]\w*)\((.*)\)$").unwrap();
|
if let Some(captures) = self.call_regex.captures(call_str.trim()) {
|
||||||
|
|
||||||
if let Some(captures) = call_regex.captures(call_str.trim()) {
|
|
||||||
let function_name = captures.get(1).unwrap().as_str();
|
let function_name = captures.get(1).unwrap().as_str();
|
||||||
let args_str = captures.get(2).unwrap().as_str();
|
let args_str = captures.get(2).unwrap().as_str();
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ use crate::tool_parser::parsers::{
|
|||||||
MistralParser, PythonicParser, QwenParser, Step3Parser,
|
MistralParser, PythonicParser, QwenParser, Step3Parser,
|
||||||
};
|
};
|
||||||
use crate::tool_parser::traits::ToolParser;
|
use crate::tool_parser::traits::ToolParser;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Global singleton registry instance - created once and reused
|
||||||
|
pub static GLOBAL_REGISTRY: Lazy<ParserRegistry> = Lazy::new(ParserRegistry::new_internal);
|
||||||
|
|
||||||
/// Registry for tool parsers and model mappings
|
/// Registry for tool parsers and model mappings
|
||||||
pub struct ParserRegistry {
|
pub struct ParserRegistry {
|
||||||
/// Map of parser name to parser instance
|
/// Map of parser name to parser instance
|
||||||
@@ -17,8 +21,19 @@ pub struct ParserRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ParserRegistry {
|
impl ParserRegistry {
|
||||||
/// Create a new parser registry with default mappings
|
/// Get the global singleton instance
|
||||||
pub fn new() -> Self {
|
pub fn new() -> &'static Self {
|
||||||
|
&GLOBAL_REGISTRY
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance for testing (not the singleton)
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn new_for_testing() -> Self {
|
||||||
|
Self::new_internal()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal constructor for creating the singleton instance
|
||||||
|
fn new_internal() -> Self {
|
||||||
let mut registry = Self {
|
let mut registry = Self {
|
||||||
parsers: HashMap::new(),
|
parsers: HashMap::new(),
|
||||||
model_mapping: HashMap::new(),
|
model_mapping: HashMap::new(),
|
||||||
@@ -202,8 +217,8 @@ impl ParserRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ParserRegistry {
|
impl Default for &'static ParserRegistry {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
ParserRegistry::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ fn test_parser_registry() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parser_registry_pattern_matching() {
|
fn test_parser_registry_pattern_matching() {
|
||||||
let mut registry = ParserRegistry::new();
|
let mut registry = ParserRegistry::new_for_testing();
|
||||||
|
|
||||||
// Test that model mappings work by checking the list
|
// Test that model mappings work by checking the list
|
||||||
registry.map_model("test-model", "json");
|
registry.map_model("test-model", "json");
|
||||||
|
|||||||
Reference in New Issue
Block a user