use serde_json::{json, Map, Value};
use crate::canonical::{CanonicalError, CanonicalRequest, Tool, ToolChoice};
use crate::protocol::json::finish_body;
use crate::protocol::{ProviderCtx, WireRequest};
mod messages;
pub(super) const REQUEST_PATH: &str = "/chat/completions";
pub(super) fn encode(
req: &CanonicalRequest,
ctx: &ProviderCtx,
) -> Result<WireRequest, CanonicalError> {
let mut body = Map::new();
body.insert("model".into(), json!(ctx.model));
body.insert("messages".into(), messages::messages_value(req)?);
if !req.tools.is_empty() {
body.insert("tools".into(), tools_value(&req.tools)); }
if let Some(tc) = tool_choice_value(&req.tool_choice) {
body.insert("tool_choice".into(), tc); }
if let Some(p) = req.parallel_tool_calls {
body.insert("parallel_tool_calls".into(), json!(p)); }
if let Some(n) = req.max_tokens {
body.insert("max_tokens".into(), json!(n)); }
if let Some(t) = req.temperature {
body.insert("temperature".into(), json!(t));
}
if let Some(p) = req.top_p {
body.insert("top_p".into(), json!(p));
}
if let Some(r) = req.reasoning {
body.insert("reasoning_effort".into(), json!(r.as_str())); }
if !req.stop.is_empty() {
body.insert("stop".into(), json!(req.stop)); }
body.insert("stream".into(), json!(req.stream.unwrap_or(false)));
if req.stream.unwrap_or(false) {
body.insert("stream_options".into(), json!({"include_usage": true}));
}
for (k, v) in &req.extra {
body.entry(k.clone()).or_insert_with(|| v.clone()); }
Ok(finish_body(body, format!("{}{REQUEST_PATH}", ctx.base_url)))
}
fn tools_value(tools: &[Tool]) -> Value {
Value::Array(
tools
.iter()
.map(|t| {
let mut f = json!({"name": t.name, "parameters": t.input_schema});
if let Some(d) = &t.description {
f["description"] = json!(d);
}
json!({"type": "function", "function": f})
})
.collect(),
)
}
fn tool_choice_value(tc: &ToolChoice) -> Option<Value> {
Some(match tc {
ToolChoice::Auto => return None,
ToolChoice::Any => json!("required"),
ToolChoice::None => json!("none"),
ToolChoice::Tool { name } => json!({"type": "function", "function": {"name": name}}),
})
}