use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use super::config::*;
use super::error::ProtocolError;
use super::request::UnifiedRequest;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProtocolManifest {
#[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
pub schema: Option<String>,
pub id: String,
pub protocol_version: String,
pub endpoint: EndpointDefinition,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub availability: Option<AvailabilityConfig>,
pub capabilities: Capabilities,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
pub status: String, pub category: String, pub official_url: String,
pub support_contact: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth: Option<AuthConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub payload_format: Option<String>,
#[serde(default)]
pub parameter_mappings: HashMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_paths: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub streaming: Option<StreamingConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub features: Option<FeaturesConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoints: Option<HashMap<String, EndpointConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub services: Option<HashMap<String, ServiceConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_families: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_api_family: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub termination: Option<TerminationConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tooling: Option<ToolingConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retry_policy: Option<RetryPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_classification: Option<ErrorClassification>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_headers: Option<RateLimitHeaders>,
#[serde(skip_serializing_if = "Option::is_none")]
pub experimental_features: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capability_profile: Option<serde_json::Value>,
#[serde(default, flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
impl ProtocolManifest {
pub fn supports_capability(&self, capability: &str) -> bool {
match capability {
"streaming" => self.capabilities.streaming,
"tools" => self.capabilities.tools,
"vision" => self.capabilities.vision,
"agentic" => self.capabilities.agentic,
"parallel_tools" => self.capabilities.parallel_tools,
"reasoning" => self.capabilities.reasoning,
"multimodal" => {
self.capabilities.multimodal || self.capabilities.vision || self.capabilities.audio
}
"audio" => self.capabilities.audio,
"structured_output" => self.capabilities.structured_output,
"mcp_client" => self.capabilities.mcp_client,
_ => false,
}
}
pub fn get_base_url(&self) -> &str {
&self.endpoint.base_url
}
pub fn compile_request(
&self,
request: &UnifiedRequest,
) -> Result<serde_json::Value, ProtocolError> {
use crate::utils::PathMapper;
let mut provider_request = serde_json::json!({});
let model_path = self
.parameter_mappings
.get("model")
.map(|s| s.as_str())
.unwrap_or("model");
PathMapper::set_path(
&mut provider_request,
model_path,
serde_json::Value::String(request.model.clone()),
)
.map_err(|e| ProtocolError::ValidationError(format!("Failed to set model: {}", e)))?;
if let Some(temp) = request.temperature {
if let Some(mapped) = self.parameter_mappings.get("temperature") {
PathMapper::set_path(
&mut provider_request,
mapped,
serde_json::Value::Number(serde_json::Number::from_f64(temp).ok_or_else(
|| ProtocolError::ValidationError("Invalid temperature".to_string()),
)?),
)
.map_err(|e| {
ProtocolError::ValidationError(format!("Failed to set temperature: {}", e))
})?;
}
}
if let Some(max) = request.max_tokens {
if let Some(mapped) = self.parameter_mappings.get("max_tokens") {
PathMapper::set_path(
&mut provider_request,
mapped,
serde_json::Value::Number(max.into()),
)
.map_err(|e| {
ProtocolError::ValidationError(format!("Failed to set max_tokens: {}", e))
})?;
}
}
if let Some(mapped) = self.parameter_mappings.get("stream") {
PathMapper::set_path(
&mut provider_request,
mapped,
serde_json::Value::Bool(request.stream),
)
.map_err(|e| ProtocolError::ValidationError(format!("Failed to set stream: {}", e)))?;
}
let messages_path = self
.parameter_mappings
.get("messages")
.map(|s| s.as_str())
.unwrap_or("messages");
let messages: Vec<serde_json::Value> = request
.messages
.iter()
.map(|m| serde_json::to_value(m).unwrap())
.collect();
PathMapper::set_path(
&mut provider_request,
messages_path,
serde_json::Value::Array(messages),
)
.map_err(|e| ProtocolError::ValidationError(format!("Failed to set messages: {}", e)))?;
if let Some(tools) = &request.tools {
if let Some(mapped) = self.parameter_mappings.get("tools") {
let tools_value: Vec<serde_json::Value> = tools
.iter()
.map(|t| serde_json::to_value(t).unwrap())
.collect();
PathMapper::set_path(
&mut provider_request,
mapped,
serde_json::Value::Array(tools_value),
)
.map_err(|e| {
ProtocolError::ValidationError(format!("Failed to set tools: {}", e))
})?;
}
}
if let Some(tool_choice) = &request.tool_choice {
if let Some(mapped) = self.parameter_mappings.get("tool_choice") {
PathMapper::set_path(&mut provider_request, mapped, tool_choice.clone()).map_err(
|e| ProtocolError::ValidationError(format!("Failed to set tool_choice: {}", e)),
)?;
}
}
if let Some(fmt) = &request.response_format {
let patch = fmt.to_openai_format();
if let serde_json::Value::Object(extra) = patch {
for (k, v) in extra {
provider_request[k] = v;
}
}
}
Ok(provider_request)
}
}