Unverified Commit 33d71102 authored by Ayush Agarwal's avatar Ayush Agarwal Committed by GitHub
Browse files

feat: glm47 tool parser (#5897)


Signed-off-by: default avatarayushag <ayushag@nvidia.com>
Signed-off-by: default avatarMatej Kosec <mkosec@nvidia.com>
Signed-off-by: default avatarMarko Kosec <mkosec@nvidia.com>
Co-authored-by: default avatarMatej Kosec <mkosec@nvidia.com>
Co-authored-by: default avatarishandhanani <82981111+ishandhanani@users.noreply.github.com>
parent bc8f1170
...@@ -44,6 +44,7 @@ Parser to Model Mapping ...@@ -44,6 +44,7 @@ Parser to Model Mapping
| deepseek_v3_1 | deepseek-ai/DeepSeek-V3.1 | | deepseek_v3_1 | deepseek-ai/DeepSeek-V3.1 |
| pythonic | meta-llama/Llama-4-* | | pythonic | meta-llama/Llama-4-* |
| jamba | ai21labs/AI21-Jamba-*-1.5, ai21labs/AI21-Jamba-*-1.6, ai21labs/AI21-Jamba-*-1.7, | | jamba | ai21labs/AI21-Jamba-*-1.5, ai21labs/AI21-Jamba-*-1.6, ai21labs/AI21-Jamba-*-1.7, |
| glm47 | zai-org/GLM-4.7 |
## Examples ## Examples
......
...@@ -99,6 +99,37 @@ impl Default for DsmlParserConfig { ...@@ -99,6 +99,37 @@ impl Default for DsmlParserConfig {
} }
} }
/// Configuration for GLM-4.7 style tool call parser
/// Format: <tool_call>function_name<arg_key>param</arg_key><arg_value>value</arg_value></tool_call>
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Glm47ParserConfig {
/// Start token for tool call block (e.g., "<tool_call>")
pub tool_call_start: String,
/// End token for tool call block (e.g., "</tool_call>")
pub tool_call_end: String,
/// Start token for argument key (e.g., "<arg_key>")
pub arg_key_start: String,
/// End token for argument key (e.g., "</arg_key>")
pub arg_key_end: String,
/// Start token for argument value (e.g., "<arg_value>")
pub arg_value_start: String,
/// End token for argument value (e.g., "</arg_value>")
pub arg_value_end: String,
}
impl Default for Glm47ParserConfig {
fn default() -> Self {
Self {
tool_call_start: "<tool_call>".to_string(),
tool_call_end: "</tool_call>".to_string(),
arg_key_start: "<arg_key>".to_string(),
arg_key_end: "</arg_key>".to_string(),
arg_value_start: "<arg_value>".to_string(),
arg_value_end: "</arg_value>".to_string(),
}
}
}
/// Parser-specific configuration /// Parser-specific configuration
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
...@@ -109,6 +140,7 @@ pub enum ParserConfig { ...@@ -109,6 +140,7 @@ pub enum ParserConfig {
Harmony(JsonParserConfig), Harmony(JsonParserConfig),
Typescript, Typescript,
Dsml(DsmlParserConfig), Dsml(DsmlParserConfig),
Glm47(Glm47ParserConfig),
} }
impl ParserConfig { impl ParserConfig {
...@@ -122,6 +154,7 @@ impl ParserConfig { ...@@ -122,6 +154,7 @@ impl ParserConfig {
ParserConfig::Pythonic => vec![], ParserConfig::Pythonic => vec![],
ParserConfig::Typescript => vec![], ParserConfig::Typescript => vec![],
ParserConfig::Dsml(config) => vec![config.function_calls_start.clone()], ParserConfig::Dsml(config) => vec![config.function_calls_start.clone()],
ParserConfig::Glm47(config) => vec![config.tool_call_start.clone()],
} }
} }
...@@ -135,6 +168,7 @@ impl ParserConfig { ...@@ -135,6 +168,7 @@ impl ParserConfig {
ParserConfig::Pythonic => vec![], ParserConfig::Pythonic => vec![],
ParserConfig::Typescript => vec![], ParserConfig::Typescript => vec![],
ParserConfig::Dsml(config) => vec![config.function_calls_end.clone()], ParserConfig::Dsml(config) => vec![config.function_calls_end.clone()],
ParserConfig::Glm47(config) => vec![config.tool_call_end.clone()],
} }
} }
} }
...@@ -314,4 +348,13 @@ impl ToolCallConfig { ...@@ -314,4 +348,13 @@ impl ToolCallConfig {
}), }),
} }
} }
pub fn glm47() -> Self {
// GLM-4.7 format:
// <tool_call>function_name<arg_key>param1</arg_key><arg_value>value1</arg_value></tool_call>
// Reference: https://huggingface.co/zai-org/GLM-4.7/blob/main/chat_template.jinja
Self {
parser_config: ParserConfig::Glm47(Glm47ParserConfig::default()),
}
}
} }
...@@ -19,7 +19,8 @@ use super::pythonic::{ ...@@ -19,7 +19,8 @@ use super::pythonic::{
}; };
use super::response::ToolCallResponse; use super::response::ToolCallResponse;
use super::xml::{ use super::xml::{
detect_tool_call_start_xml, find_tool_call_end_position_xml, try_tool_call_parse_xml, detect_tool_call_start_glm47, detect_tool_call_start_xml, find_tool_call_end_position_glm47,
find_tool_call_end_position_xml, try_tool_call_parse_glm47, try_tool_call_parse_xml,
}; };
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::OnceLock; use std::sync::OnceLock;
...@@ -43,6 +44,7 @@ pub fn get_tool_parser_map() -> &'static HashMap<&'static str, ToolCallConfig> { ...@@ -43,6 +44,7 @@ pub fn get_tool_parser_map() -> &'static HashMap<&'static str, ToolCallConfig> {
map.insert("qwen3_coder", ToolCallConfig::qwen3_coder()); map.insert("qwen3_coder", ToolCallConfig::qwen3_coder());
map.insert("jamba", ToolCallConfig::jamba()); map.insert("jamba", ToolCallConfig::jamba());
map.insert("minimax_m2", ToolCallConfig::minimax_m2()); map.insert("minimax_m2", ToolCallConfig::minimax_m2());
map.insert("glm47", ToolCallConfig::glm47());
map.insert("default", ToolCallConfig::default()); map.insert("default", ToolCallConfig::default());
map.insert("nemotron_nano", ToolCallConfig::qwen3_coder()); // nemotron nano follows qwen3_coder format map.insert("nemotron_nano", ToolCallConfig::qwen3_coder()); // nemotron nano follows qwen3_coder format
map map
...@@ -84,6 +86,11 @@ pub async fn try_tool_call_parse( ...@@ -84,6 +86,11 @@ pub async fn try_tool_call_parse(
let (results, normal_content) = try_tool_call_parse_dsml(message, dsml_config)?; let (results, normal_content) = try_tool_call_parse_dsml(message, dsml_config)?;
Ok((results, normal_content)) Ok((results, normal_content))
} }
ParserConfig::Glm47(glm47_config) => {
let (results, normal_content) =
try_tool_call_parse_glm47(message, glm47_config, tools)?;
Ok((results, normal_content))
}
} }
} }
...@@ -134,6 +141,9 @@ pub fn detect_tool_call_start(chunk: &str, parser_str: Option<&str>) -> anyhow:: ...@@ -134,6 +141,9 @@ pub fn detect_tool_call_start(chunk: &str, parser_str: Option<&str>) -> anyhow::
} }
ParserConfig::Xml(xml_config) => Ok(detect_tool_call_start_xml(chunk, xml_config)), ParserConfig::Xml(xml_config) => Ok(detect_tool_call_start_xml(chunk, xml_config)),
ParserConfig::Dsml(dsml_config) => Ok(detect_tool_call_start_dsml(chunk, dsml_config)), ParserConfig::Dsml(dsml_config) => Ok(detect_tool_call_start_dsml(chunk, dsml_config)),
ParserConfig::Glm47(glm47_config) => {
Ok(detect_tool_call_start_glm47(chunk, glm47_config))
}
}, },
None => anyhow::bail!( None => anyhow::bail!(
"Parser '{}' is not implemented. Available parsers: {:?}", "Parser '{}' is not implemented. Available parsers: {:?}",
...@@ -171,6 +181,9 @@ pub fn find_tool_call_end_position(chunk: &str, parser_str: Option<&str>) -> usi ...@@ -171,6 +181,9 @@ pub fn find_tool_call_end_position(chunk: &str, parser_str: Option<&str>) -> usi
} }
ParserConfig::Xml(xml_config) => find_tool_call_end_position_xml(chunk, xml_config), ParserConfig::Xml(xml_config) => find_tool_call_end_position_xml(chunk, xml_config),
ParserConfig::Dsml(dsml_config) => find_tool_call_end_position_dsml(chunk, dsml_config), ParserConfig::Dsml(dsml_config) => find_tool_call_end_position_dsml(chunk, dsml_config),
ParserConfig::Glm47(glm47_config) => {
find_tool_call_end_position_glm47(chunk, glm47_config)
}
}, },
None => { None => {
// Unknown parser, return full content length // Unknown parser, return full content length
...@@ -211,6 +224,7 @@ mod tests { ...@@ -211,6 +224,7 @@ mod tests {
"jamba", "jamba",
"nemotron_nano", "nemotron_nano",
"minimax_m2", "minimax_m2",
"glm47",
]; ];
for parser in available_parsers { for parser in available_parsers {
assert!(parsers.contains(&parser)); assert!(parsers.contains(&parser));
......
This diff is collapsed.
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
mod glm47_parser;
mod parser; mod parser;
pub use super::response; pub use super::response;
pub use glm47_parser::{
detect_tool_call_start_glm47, find_tool_call_end_position_glm47, try_tool_call_parse_glm47,
};
pub use parser::{ pub use parser::{
detect_tool_call_start_xml, find_tool_call_end_position_xml, try_tool_call_parse_xml, detect_tool_call_start_xml, find_tool_call_end_position_xml, try_tool_call_parse_xml,
}; };
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment