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(
mut parsing_options: ParsingOptions,
request: &NvCreateChatCompletionRequest,
) -> Result<ParsingOptions, DynamoError> {
let unified_family =
crate::protocols::openai::chat_completions::unified_parser::selected_batch_family(
parsing_options.tool_call_parser.as_deref(),
parsing_options.reasoning_parser.as_deref(),
);
parsing_options.reasoning_disabled = OpenAIPreprocessor::request_disables_reasoning_with_family(
request,
parsing_options.tool_call_parser.as_deref(),
parsing_options.reasoning_parser.as_deref(),
parsing_options.default_thinking_mode.as_deref(),
unified_family,
);
parsing_options.structured_response =
OpenAIPreprocessor::has_structured_response_format(request);
parsing_options.tool_choice = request.inner.tool_choice.clone();
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 effective_tools = crate::preprocessor::tool_choice::effective_tools(&request.inner)?;
let converted_tools = crate::preprocessor::tool_choice::convert_tools(effective_tools.as_ref());
let structural_tag_config = parsing_options.structural_tag.as_ref();
let has_structured_output = request
.inner
.response_format
.as_ref()
.is_some_and(|format| !matches!(format, dynamo_protocols::types::ResponseFormat::Text));
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,
structural_tag_config,
has_structured_output,
parsing_options.exclude_tools_when_tool_choice_none,
)?
.is_required();
let guided_tool_constraint =
crate::preprocessor::tool_choice::guided_tool_constraint_with_effective_tools(
request,
parsing_options.tool_call_parser.as_deref(),
parsing_options.reasoning_parser.as_deref(),
uses_structural_tag,
effective_tools.as_ref(),
)?;
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};
#[test]
fn raw_batch_reasoning_policy_respects_deployment_default_and_client_override() {
let mut options =
ParsingOptions::new(Some("gemma4".to_string()), Some("gemma4".to_string()));
options.default_thinking_mode = Some("enabled".to_string());
assert!(
!apply_request_tool_call_parsing_options(options.clone(), &request(json!("auto")))
.unwrap()
.reasoning_disabled
);
let mut disabled = request(json!("auto"));
disabled.thinking = Some(json!(false));
assert!(
apply_request_tool_call_parsing_options(options, &disabled)
.unwrap()
.reasoning_disabled
);
}
#[test]
fn muse_auto_batch_reasoning_policy_honors_disabled_request() {
if crate::test_utils::run_isolated(
concat!(
module_path!(),
"::muse_auto_batch_reasoning_policy_honors_disabled_request"
),
&[(
dynamo_runtime::config::environment_names::llm::DYN_PARSER_VERSION,
"auto",
)],
) {
return;
}
let mut req = request(json!("auto"));
req.chat_template_args = Some(std::collections::HashMap::from([(
"thinking".to_string(),
json!(false),
)]));
let result = apply_request_tool_call_parsing_options(
ParsingOptions::new(Some("muse_glimmer".to_string()), None),
&req,
)
.unwrap();
assert!(result.reasoning_disabled);
}
#[test]
fn structured_response_schema_roots_reconstruct_response_policy() {
for kind in ["object", "array", "string", "number", "boolean", "null"] {
let req: NvCreateChatCompletionRequest = serde_json::from_value(json!({
"model":"test", "messages":[{"role":"user", "content":"answer"}],
"response_format":{"type":"json_schema", "json_schema":{"name":"answer", "schema":{"type":kind}}}
})).unwrap();
let result = apply_request_tool_call_parsing_options(
ParsingOptions::new(Some("qwen3".into()), Some("qwen3".into())),
&req,
)
.unwrap();
assert!(result.structured_response, "{kind}");
assert_eq!(
result.guided_tool_constraint,
GuidedToolConstraint::None,
"{kind}"
);
}
}
#[test]
fn unified_request_controls_use_canonical_family_for_each_selector_shape() {
use crate::protocols::openai::chat_completions::unified_parser;
if crate::test_utils::run_isolated(
concat!(
module_path!(),
"::unified_request_controls_use_canonical_family_for_each_selector_shape"
),
&[(
dynamo_runtime::config::environment_names::llm::DYN_PARSER_VERSION,
"2",
)],
) {
return;
}
for selector in unified_parser::FAMILY_NAMES.iter() {
let family = unified_parser::canonical_family(selector).unwrap();
for (tool, reasoning) in [
(Some(*selector), None),
(None, Some(*selector)),
(Some(*selector), Some(*selector)),
] {
for thinking in [None, Some(false), Some(true)] {
let mut req = request(json!("auto"));
req.thinking = thinking.map(|value| json!(value));
req.normalize_reasoning_template_args().unwrap();
let options =
ParsingOptions::new(tool.map(str::to_owned), reasoning.map(str::to_owned));
let result = apply_request_tool_call_parsing_options(options, &req).unwrap();
assert_eq!(
result.reasoning_disabled,
if family == "gemma4" {
thinking != Some(true)
} else {
thinking == Some(false)
},
"{selector} {tool:?} {reasoning:?} {thinking:?}"
);
}
}
}
}
#[test]
fn unified_deepseek_retains_renderer_chat_and_thinking_modes() {
for family in ["deepseek_v4", "deepseek-v4", "deepseek_v41"] {
for mode in ["chat", "thinking"] {
let mut req = request(json!("auto"));
req.chat_template_args = Some(std::collections::HashMap::from([(
"thinking_mode".to_owned(),
json!(mode),
)]));
let options = ParsingOptions::new(Some(family.into()), Some(family.into()));
let result = apply_request_tool_call_parsing_options(options, &req).unwrap();
assert_eq!(result.reasoning_disabled, mode == "chat", "{family} {mode}");
}
}
}
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")
}
fn dynamic_request(tool_choice: Option<Value>) -> NvCreateChatCompletionRequest {
let mut value = json!({
"model": "test-model",
"messages": [
{
"role": "system",
"content": "",
"tools": [{
"name": "lookup",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
"additionalProperties": false
},
"strict": true
}]
},
{"role": "user", "content": "test"}
]
});
if let Some(tool_choice) = tool_choice {
value["tool_choice"] = tool_choice;
}
serde_json::from_value(value).expect("dynamic request must deserialize")
}
#[test]
fn kimi_k3_dynamic_tools_enable_response_parsing_without_a_top_level_sentinel() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k3".to_string()),
..Default::default()
};
let result =
apply_request_tool_call_parsing_options(parsing_options, &dynamic_request(None))
.expect("dynamic tools must configure parsing");
assert!(!result.suppress_tool_calls);
assert_eq!(result.tools.len(), 1);
assert_eq!(result.tools[0].name, "lookup");
assert_eq!(result.tools[0].strict, Some(true));
}
#[test]
fn kimi_k3_named_dynamic_tool_uses_structural_tag_policy() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k3".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(
parsing_options,
&dynamic_request(Some(
json!({"type": "function", "function": {"name": "lookup"}}),
)),
)
.expect("named dynamic tool choice must configure parsing");
assert!(!result.suppress_tool_calls);
assert_eq!(
result.guided_tool_constraint,
GuidedToolConstraint::StructuralTag
);
assert_eq!(result.tools.len(), 1);
assert_eq!(result.tools[0].name, "lookup");
}
#[test]
fn dynamic_tools_with_tool_choice_none_still_suppress_calls() {
let parsing_options = ParsingOptions {
tool_call_parser: Some("kimi_k3".to_string()),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(
parsing_options,
&dynamic_request(Some(json!("none"))),
)
.expect("tool_choice none must remain valid");
assert!(result.suppress_tool_calls);
assert_eq!(result.guided_tool_constraint, GuidedToolConstraint::None);
assert_eq!(
result.tools.len(),
1,
"declarations remain available to parsers"
);
}
#[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 retain its native structural tag when mode is off"
);
}
#[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 retain its native structural tag when mode is off"
);
}
#[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: Some(Default::default()),
..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-configured structural tag must apply to any registry-supported \
parser, not only the Kimi-intrinsic case"
);
}
#[test]
fn parsing_options_default_mode_off_does_not_resolve_structural_tag() {
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 tools_with_structured_output_are_classified_as_structural_tag() {
let mut request = request(json!("auto"));
request.inner.response_format = Some(
serde_json::from_value(json!({
"type": "json_schema",
"json_schema": {
"name": "response",
"schema": {"type": "object"}
}
}))
.expect("response_format must deserialize"),
);
let config = crate::local_model::runtime_config::StructuralTagConfig {
allow_tool_calls_with_structured_output: true,
..Default::default()
};
let parsing_options = ParsingOptions {
tool_call_parser: Some("qwen3_coder".to_string()),
structural_tag: Some(config),
..Default::default()
};
let result = apply_request_tool_call_parsing_options(parsing_options, &request)
.expect("tools with structured output must resolve a constraint");
assert_eq!(
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: Some(Default::default()),
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: Some(Default::default()),
..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: Some(Default::default()),
..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
}
}