use serde_json::{Map, Value};
use crate::services::gateway::protocol::canonical::{
CanonicalTool, CanonicalToolChoice, ThinkingConfig,
};
use crate::services::gateway::protocol::inbound::InboundParseError;
const TOOL_CHOICE_EXPECTED: &str = "expected an object with type auto|any|tool";
pub(super) fn parse_tool(value: &Value) -> CanonicalTool {
CanonicalTool {
name: value
.get("name")
.and_then(Value::as_str)
.unwrap_or("")
.to_owned(),
description: value
.get("description")
.and_then(Value::as_str)
.map(str::to_owned),
input_schema: value
.get("input_schema")
.cloned()
.unwrap_or(Value::Object(Map::new())),
}
}
pub(super) fn parse_tool_choice(
request: &Value,
) -> Result<Option<CanonicalToolChoice>, InboundParseError> {
request
.get("tool_choice")
.map(parse_present_tool_choice)
.transpose()
}
fn parse_present_tool_choice(value: &Value) -> Result<CanonicalToolChoice, InboundParseError> {
let unsupported = || InboundParseError::Unsupported {
field: "tool_choice",
detail: TOOL_CHOICE_EXPECTED.to_owned(),
};
let kind = value
.get("type")
.and_then(Value::as_str)
.ok_or_else(unsupported)?;
match kind {
"auto" => Ok(CanonicalToolChoice::Auto),
"any" => Ok(CanonicalToolChoice::Any),
"none" => Ok(CanonicalToolChoice::None),
"required" => Ok(CanonicalToolChoice::Required),
"tool" => value
.get("name")
.and_then(Value::as_str)
.map(|n| CanonicalToolChoice::Tool(n.to_owned()))
.ok_or_else(|| InboundParseError::Unsupported {
field: "tool_choice",
detail: "expected a `name` for tool_choice type tool".to_owned(),
}),
_ => Err(unsupported()),
}
}
pub(super) fn parse_thinking(value: &Value) -> ThinkingConfig {
let kind = value.get("type").and_then(Value::as_str).unwrap_or("");
let enabled = kind == "enabled";
let budget_tokens = value
.get("budget_tokens")
.and_then(Value::as_u64)
.map(|v| v as u32);
ThinkingConfig {
enabled,
budget_tokens,
}
}