modeldriveprotocol-client 2.2.0

Rust client SDK for Model Drive Protocol.
Documentation
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

pub type JsonSchema = Map<String, Value>;
pub type RpcArguments = Map<String, Value>;

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct AuthContext {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scheme: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headers: Option<std::collections::HashMap<String, String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<Map<String, Value>>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ClientInfo {
    pub id: String,
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub platform: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<Map<String, Value>>,
}

impl ClientInfo {
    pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
        Self {
            id: id.into(),
            name: name.into(),
            description: None,
            version: None,
            platform: None,
            metadata: None,
        }
    }

    pub fn apply_override(&self, override_info: Option<ClientInfoOverride>) -> Self {
        match override_info {
            None => self.clone(),
            Some(override_info) => Self {
                id: override_info.id.unwrap_or_else(|| self.id.clone()),
                name: override_info.name.unwrap_or_else(|| self.name.clone()),
                description: override_info.description.or_else(|| self.description.clone()),
                version: override_info.version.or_else(|| self.version.clone()),
                platform: override_info.platform.or_else(|| self.platform.clone()),
                metadata: override_info.metadata.or_else(|| self.metadata.clone()),
            },
        }
    }
}

#[derive(Clone, Debug, Default)]
pub struct ClientInfoOverride {
    pub id: Option<String>,
    pub name: Option<String>,
    pub description: Option<String>,
    pub version: Option<String>,
    pub platform: Option<String>,
    pub metadata: Option<Map<String, Value>>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum HttpMethod {
    #[serde(rename = "GET")]
    Get,
    #[serde(rename = "POST")]
    Post,
    #[serde(rename = "PUT")]
    Put,
    #[serde(rename = "PATCH")]
    Patch,
    #[serde(rename = "DELETE")]
    Delete,
}

impl HttpMethod {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Get => "GET",
            Self::Post => "POST",
            Self::Put => "PUT",
            Self::Patch => "PATCH",
            Self::Delete => "DELETE",
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum PathDescriptor {
    #[serde(rename = "endpoint")]
    Endpoint {
        path: String,
        method: HttpMethod,
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,
        #[serde(rename = "inputSchema", skip_serializing_if = "Option::is_none")]
        input_schema: Option<JsonSchema>,
        #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
        output_schema: Option<JsonSchema>,
        #[serde(rename = "contentType", skip_serializing_if = "Option::is_none")]
        content_type: Option<String>,
    },
    #[serde(rename = "skill")]
    Skill {
        path: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,
        #[serde(rename = "contentType")]
        content_type: String,
    },
    #[serde(rename = "prompt")]
    Prompt {
        path: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,
        #[serde(rename = "inputSchema", skip_serializing_if = "Option::is_none")]
        input_schema: Option<JsonSchema>,
        #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
        output_schema: Option<JsonSchema>,
    },
}

impl PathDescriptor {
    pub fn path(&self) -> &str {
        match self {
            Self::Endpoint { path, .. } | Self::Skill { path, .. } | Self::Prompt { path, .. } => path,
        }
    }

    pub fn descriptor_type(&self) -> &'static str {
        match self {
            Self::Endpoint { .. } => "endpoint",
            Self::Skill { .. } => "skill",
            Self::Prompt { .. } => "prompt",
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ClientDescriptor {
    pub id: String,
    pub name: String,
    pub paths: Vec<PathDescriptor>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub platform: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<Map<String, Value>>,
}

impl ClientDescriptor {
    pub fn from_info(info: &ClientInfo, paths: Vec<PathDescriptor>) -> Self {
        Self {
            id: info.id.clone(),
            name: info.name.clone(),
            description: info.description.clone(),
            version: info.version.clone(),
            platform: info.platform.clone(),
            metadata: info.metadata.clone(),
            paths,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SerializedError {
    pub code: String,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<Value>,
}

impl SerializedError {
    pub fn handler(message: impl Into<String>) -> Self {
        Self {
            code: "handler_error".to_string(),
            message: message.into(),
            details: None,
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct PathRequest {
    pub params: RpcArguments,
    pub queries: RpcArguments,
    pub body: Option<Value>,
    pub headers: std::collections::HashMap<String, String>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct PathInvocationContext {
    pub request_id: String,
    pub client_id: String,
    pub path_type: String,
    pub method: HttpMethod,
    pub path: String,
    pub auth: Option<AuthContext>,
}

#[derive(Clone, Debug, Default)]
pub struct EndpointOptions {
    pub description: Option<String>,
    pub input_schema: Option<JsonSchema>,
    pub output_schema: Option<JsonSchema>,
    pub content_type: Option<String>,
}

impl EndpointOptions {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn description(mut self, description: impl Into<String>) -> Self {
        self.description = Some(description.into());
        self
    }
}

#[derive(Clone, Debug)]
pub struct SkillOptions {
    pub description: Option<String>,
    pub content_type: String,
}

impl Default for SkillOptions {
    fn default() -> Self {
        Self {
            description: None,
            content_type: "text/markdown".to_string(),
        }
    }
}

impl SkillOptions {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn description(mut self, description: impl Into<String>) -> Self {
        self.description = Some(description.into());
        self
    }
}

#[derive(Clone, Debug, Default)]
pub struct PromptOptions {
    pub description: Option<String>,
    pub input_schema: Option<JsonSchema>,
    pub output_schema: Option<JsonSchema>,
}

impl PromptOptions {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn description(mut self, description: impl Into<String>) -> Self {
        self.description = Some(description.into());
        self
    }
}