use crate::cli::ProviderDispatchExplainArgs;
pub(crate) fn run(args: &ProviderDispatchExplainArgs) {
let caps = harn_vm::llm::capabilities::lookup(&args.provider, &args.model);
let wire_format = harn_vm::llm::resolved_dispatch::wire_format_for(&args.provider, &args.model);
let message_wire_format = caps.message_wire_format.as_str().to_string();
let base_url = harn_vm::llm_config::provider_config(&args.provider)
.map(|def| harn_vm::llm_config::resolve_base_url(&def))
.unwrap_or_else(|| default_base_url_for(wire_format));
let base_url_host = host_of(&base_url);
let tool_format = args
.tool_format
.clone()
.or_else(|| caps.preferred_tool_format.clone())
.unwrap_or_else(|| {
if caps.native_tools {
"native".to_string()
} else {
"text".to_string()
}
});
let advertises_thinking = !caps.thinking_modes.is_empty();
let thinking_note = if args.thinking && !advertises_thinking {
Some(format!(
"requested --thinking but {}:{} advertises no thinking modes; the route will not return reasoning content",
args.provider, args.model
))
} else {
None
};
if args.json {
let report = serde_json::json!({
"provider": args.provider,
"model": args.model,
"wire_format": wire_format,
"message_wire_format": message_wire_format,
"native_tool_wire_format": caps.native_tool_wire_format,
"base_url_host": base_url_host,
"tool_format": tool_format,
"native_tools": caps.native_tools,
"advertises_thinking": advertises_thinking,
"thinking_modes": caps.thinking_modes,
"requested_thinking": args.thinking,
"thinking_note": thinking_note,
});
println!(
"{}",
serde_json::to_string_pretty(&report).unwrap_or_else(|_| report.to_string())
);
return;
}
println!("dispatch-explain {}:{}", args.provider, args.model);
println!(" wire_format: {wire_format}");
println!(" message_wire: {message_wire_format}");
println!(" tool_wire: {}", caps.native_tool_wire_format);
println!(" base_url_host: {base_url_host}");
println!(" tool_format: {tool_format}");
println!(" native_tools: {}", caps.native_tools);
println!(
" thinking: advertised={advertises_thinking}{}",
if args.thinking { " (requested)" } else { "" }
);
if let Some(note) = thinking_note {
println!(" NOTE: {note}");
}
}
fn default_base_url_for(wire_format: &str) -> String {
match wire_format {
"anthropic_native" => "https://api.anthropic.com/v1".to_string(),
"gemini" => "https://generativelanguage.googleapis.com/v1beta".to_string(),
"ollama" => "http://localhost:11434".to_string(),
_ => "https://api.openai.com/v1".to_string(),
}
}
fn host_of(base_url: &str) -> String {
base_url
.split("://")
.nth(1)
.and_then(|rest| rest.split('/').next())
.map(str::to_string)
.unwrap_or_else(|| base_url.to_string())
}