mod anthropic;
pub mod metadata;
mod openai;
pub mod busy_threshold;
pub mod disconnect;
pub mod error;
pub mod frontend_extension;
pub mod generate;
pub mod health;
pub mod metrics;
pub mod openapi_docs;
pub mod realtime;
pub mod service_v2;
pub mod sglang_generate;
pub use axum;
pub use frontend_extension::{
FrontendExtensionContext, FrontendRouteExtension, FrontendRouteSet,
validate_extension_route_path,
};
pub use metrics::Metrics;
use crate::{
preprocessor::OpenAIPreprocessor,
protocols::openai::{ParsingOptions, chat_completions::NvCreateChatCompletionRequest},
};
use dynamo_protocols::types::ChatCompletionToolChoiceOption;
use dynamo_runtime::error::DynamoError;
fn apply_request_tool_call_parsing_options(
parsing_options: ParsingOptions,
request: &NvCreateChatCompletionRequest,
) -> Result<ParsingOptions, DynamoError> {
let tool_call_parsing_enabled = OpenAIPreprocessor::tool_call_parsing_enabled(request);
let tool_choice = request
.inner
.tool_choice
.as_ref()
.unwrap_or(&ChatCompletionToolChoiceOption::Auto);
let converted_tool_choice = crate::preprocessor::tool_choice::convert_tool_choice(tool_choice);
let tools = request.inner.tools.as_deref().unwrap_or(&[]);
let converted_tools = crate::preprocessor::tool_choice::convert_tools(tools);
let uses_structural_tag = crate::preprocessor::structural_tag::structural_tag_decision(
parsing_options.tool_call_parser.as_deref(),
&converted_tool_choice,
&converted_tools,
request.inner.parallel_tool_calls,
parsing_options.structural_tag_mode,
parsing_options.structural_tag_scope,
parsing_options.exclude_tools_when_tool_choice_none,
)?
.is_required();
let guided_tool_constraint = crate::preprocessor::tool_choice::guided_tool_constraint(
request,
parsing_options.tool_call_parser.as_deref(),
parsing_options.reasoning_parser.as_deref(),
uses_structural_tag,
)?;
Ok(parsing_options
.with_guided_tool_constraint(guided_tool_constraint)
.with_tool_call_parsing_enabled(tool_call_parsing_enabled)
.with_tools(converted_tools))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocols::openai::GuidedToolConstraint;
use serde_json::{Value, json};
fn request(tool_choice: Value) -> NvCreateChatCompletionRequest {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": tool_choice,
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}
}]
});
serde_json::from_value(value).expect("request must deserialize")
}
#[test]
fn kimi_k2_required_resolves_to_the_real_structural_tag_decision() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k2".to_string()),
..Default::default()
};
let result =
apply_request_tool_call_parsing_options(parsing_options, &request(json!("required")))
.expect("kimi_k2 + required must resolve a constraint");
assert_eq!(
result.guided_tool_constraint,
GuidedToolConstraint::StructuralTag,
"kimi_k2 + required must use the intrinsic structural tag, not a reconstructed JSON schema"
);
}
#[test]
fn kimi_k2_named_resolves_to_the_real_structural_tag_decision() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k2".to_string()),
..Default::default()
};
let named = json!({"type": "function", "function": {"name": "get_weather"}});
let result = apply_request_tool_call_parsing_options(parsing_options, &request(named))
.expect("kimi_k2 + named must resolve a constraint");
assert_eq!(
result.guided_tool_constraint,
GuidedToolConstraint::StructuralTag,
"kimi_k2 + a named tool choice must use the intrinsic structural tag, not a reconstructed JSON schema"
);
}
#[test]
fn non_structural_tag_parser_still_gets_guided_json_required() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("hermes".to_string()),
..Default::default()
};
let result =
apply_request_tool_call_parsing_options(parsing_options, &request(json!("required")))
.expect("hermes + required must resolve a constraint");
assert_eq!(
result.guided_tool_constraint,
GuidedToolConstraint::GuidedJsonRequired
);
}
#[test]
fn operator_enabled_mode_resolves_structural_tag_for_a_non_kimi_parser() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("qwen3_coder".to_string()),
structural_tag_mode: crate::local_model::runtime_config::StructuralTagMode::On,
..Default::default()
};
let result =
apply_request_tool_call_parsing_options(parsing_options, &request(json!("required")))
.expect("qwen3_coder + required must resolve a constraint");
assert_eq!(
result.guided_tool_constraint,
GuidedToolConstraint::StructuralTag,
"an operator-enabled structural_tag_mode must apply to any registry-supported \
parser, not only the Kimi-intrinsic case"
);
}
#[test]
fn operator_default_mode_off_does_not_resolve_structural_tag_for_a_non_kimi_parser() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("qwen3_coder".to_string()),
..Default::default()
};
let result =
apply_request_tool_call_parsing_options(parsing_options, &request(json!("required")))
.expect("qwen3_coder + required (mode off) must resolve a constraint");
assert_ne!(
result.guided_tool_constraint,
GuidedToolConstraint::StructuralTag
);
}
#[test]
fn tool_choice_none_never_exposes_tool_calls_regardless_of_ban_tag_configuration() {
for exclude_tools_when_tool_choice_none in [true, false] {
let parsing_options = ParsingOptions {
tool_call_parser: Some("qwen3_coder".to_string()),
structural_tag_mode: crate::local_model::runtime_config::StructuralTagMode::On,
exclude_tools_when_tool_choice_none,
..Default::default()
};
let result =
apply_request_tool_call_parsing_options(parsing_options, &request(json!("none")))
.expect("tool_choice=none must resolve a constraint");
assert_eq!(
result.guided_tool_constraint,
GuidedToolConstraint::None,
"tool_choice=none must never report a guided-tool-output constraint \
(exclude_tools_when_tool_choice_none={exclude_tools_when_tool_choice_none})"
);
assert!(
result.suppress_tool_calls,
"tool_choice=none must suppress tool calls \
(exclude_tools_when_tool_choice_none={exclude_tools_when_tool_choice_none})"
);
}
}
#[test]
fn required_tool_choice_with_empty_tools_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": "required",
"tools": []
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("hermes".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"tool_choice=required with no tools must not resolve to a bogus GuidedJsonRequired"
);
}
#[test]
fn named_tool_choice_for_absent_tool_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": {"type": "function", "function": {"name": "does_not_exist"}},
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}
}]
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("hermes".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"a named tool_choice for a tool absent from `tools` must not resolve to a \
bogus constraint"
);
}
#[test]
fn kimi_k2_required_with_empty_tools_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": "required",
"tools": []
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k2".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"kimi_k2 + required with no tools must not resolve to StructuralTag"
);
}
#[test]
fn kimi_k2_named_tool_choice_for_absent_tool_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": {"type": "function", "function": {"name": "does_not_exist"}},
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}
}]
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k2".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"kimi_k2 + a named tool_choice for a tool absent from `tools` must not \
resolve to StructuralTag"
);
}
#[test]
fn kimi_k3_named_tool_choice_for_absent_tool_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": {"type": "function", "function": {"name": "does_not_exist"}},
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}
}]
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k3".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"kimi_k3 + a named tool_choice for a tool absent from `tools` must not \
resolve to StructuralTag"
);
}
#[test]
fn kimi_k3_required_with_empty_tools_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": "required",
"tools": []
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k3".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"kimi_k3 + required with no tools must be rejected before the XTML path"
);
}
#[test]
fn operator_enabled_qwen3_coder_required_with_empty_tools_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": "required",
"tools": []
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("qwen3_coder".to_string()),
structural_tag_mode: crate::local_model::runtime_config::StructuralTagMode::On,
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"operator-enabled qwen3_coder + required with no tools must not resolve \
to StructuralTag"
);
}
#[test]
fn operator_enabled_qwen3_coder_named_tool_choice_for_absent_tool_is_rejected() {
let value = json!({
"model": "test-model",
"messages": [{"role": "user", "content": "test"}],
"tool_choice": {"type": "function", "function": {"name": "does_not_exist"}},
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}
}]
});
let request: NvCreateChatCompletionRequest =
serde_json::from_value(value).expect("request must deserialize");
let parsing_options = ParsingOptions {
tool_call_parser: Some("qwen3_coder".to_string()),
structural_tag_mode: crate::local_model::runtime_config::StructuralTagMode::On,
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request);
assert!(
result.is_err(),
"operator-enabled qwen3_coder + a named tool_choice for a tool absent from \
`tools` must not resolve to StructuralTag"
);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RouteDoc {
method: axum::http::Method,
path: String,
}
impl std::fmt::Display for RouteDoc {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{} {}", self.method, self.path)
}
}
impl RouteDoc {
pub fn new<T: Into<String>>(method: axum::http::Method, path: T) -> Self {
RouteDoc {
method,
path: path.into(),
}
}
pub fn method(&self) -> &axum::http::Method {
&self.method
}
pub fn path(&self) -> &str {
&self.path
}
}