Skip to main content

modeldriveprotocol_client/
models.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4pub type JsonSchema = Map<String, Value>;
5pub type RpcArguments = Map<String, Value>;
6
7#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
8pub struct AuthContext {
9    #[serde(skip_serializing_if = "Option::is_none")]
10    pub scheme: Option<String>,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub token: Option<String>,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub headers: Option<std::collections::HashMap<String, String>>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub metadata: Option<Map<String, Value>>,
17}
18
19#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
20pub struct ClientInfo {
21    pub id: String,
22    pub name: String,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub description: Option<String>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub version: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub platform: Option<String>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub metadata: Option<Map<String, Value>>,
31}
32
33impl ClientInfo {
34    pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
35        Self {
36            id: id.into(),
37            name: name.into(),
38            description: None,
39            version: None,
40            platform: None,
41            metadata: None,
42        }
43    }
44
45    pub fn apply_override(&self, override_info: Option<ClientInfoOverride>) -> Self {
46        match override_info {
47            None => self.clone(),
48            Some(override_info) => Self {
49                id: override_info.id.unwrap_or_else(|| self.id.clone()),
50                name: override_info.name.unwrap_or_else(|| self.name.clone()),
51                description: override_info.description.or_else(|| self.description.clone()),
52                version: override_info.version.or_else(|| self.version.clone()),
53                platform: override_info.platform.or_else(|| self.platform.clone()),
54                metadata: override_info.metadata.or_else(|| self.metadata.clone()),
55            },
56        }
57    }
58}
59
60#[derive(Clone, Debug, Default)]
61pub struct ClientInfoOverride {
62    pub id: Option<String>,
63    pub name: Option<String>,
64    pub description: Option<String>,
65    pub version: Option<String>,
66    pub platform: Option<String>,
67    pub metadata: Option<Map<String, Value>>,
68}
69
70#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
71pub enum HttpMethod {
72    #[serde(rename = "GET")]
73    Get,
74    #[serde(rename = "POST")]
75    Post,
76    #[serde(rename = "PUT")]
77    Put,
78    #[serde(rename = "PATCH")]
79    Patch,
80    #[serde(rename = "DELETE")]
81    Delete,
82}
83
84impl HttpMethod {
85    pub fn as_str(&self) -> &'static str {
86        match self {
87            Self::Get => "GET",
88            Self::Post => "POST",
89            Self::Put => "PUT",
90            Self::Patch => "PATCH",
91            Self::Delete => "DELETE",
92        }
93    }
94}
95
96#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
97#[serde(tag = "type")]
98pub enum PathDescriptor {
99    #[serde(rename = "endpoint")]
100    Endpoint {
101        path: String,
102        method: HttpMethod,
103        #[serde(skip_serializing_if = "Option::is_none")]
104        description: Option<String>,
105        #[serde(rename = "inputSchema", skip_serializing_if = "Option::is_none")]
106        input_schema: Option<JsonSchema>,
107        #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
108        output_schema: Option<JsonSchema>,
109        #[serde(rename = "contentType", skip_serializing_if = "Option::is_none")]
110        content_type: Option<String>,
111    },
112    #[serde(rename = "skill")]
113    Skill {
114        path: String,
115        #[serde(skip_serializing_if = "Option::is_none")]
116        description: Option<String>,
117        #[serde(rename = "contentType")]
118        content_type: String,
119    },
120    #[serde(rename = "prompt")]
121    Prompt {
122        path: String,
123        #[serde(skip_serializing_if = "Option::is_none")]
124        description: Option<String>,
125        #[serde(rename = "inputSchema", skip_serializing_if = "Option::is_none")]
126        input_schema: Option<JsonSchema>,
127        #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
128        output_schema: Option<JsonSchema>,
129    },
130}
131
132impl PathDescriptor {
133    pub fn path(&self) -> &str {
134        match self {
135            Self::Endpoint { path, .. } | Self::Skill { path, .. } | Self::Prompt { path, .. } => path,
136        }
137    }
138
139    pub fn descriptor_type(&self) -> &'static str {
140        match self {
141            Self::Endpoint { .. } => "endpoint",
142            Self::Skill { .. } => "skill",
143            Self::Prompt { .. } => "prompt",
144        }
145    }
146}
147
148#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
149pub struct ClientDescriptor {
150    pub id: String,
151    pub name: String,
152    pub paths: Vec<PathDescriptor>,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub description: Option<String>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub version: Option<String>,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub platform: Option<String>,
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub metadata: Option<Map<String, Value>>,
161}
162
163impl ClientDescriptor {
164    pub fn from_info(info: &ClientInfo, paths: Vec<PathDescriptor>) -> Self {
165        Self {
166            id: info.id.clone(),
167            name: info.name.clone(),
168            description: info.description.clone(),
169            version: info.version.clone(),
170            platform: info.platform.clone(),
171            metadata: info.metadata.clone(),
172            paths,
173        }
174    }
175}
176
177#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
178pub struct SerializedError {
179    pub code: String,
180    pub message: String,
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub details: Option<Value>,
183}
184
185impl SerializedError {
186    pub fn handler(message: impl Into<String>) -> Self {
187        Self {
188            code: "handler_error".to_string(),
189            message: message.into(),
190            details: None,
191        }
192    }
193}
194
195#[derive(Clone, Debug, PartialEq)]
196pub struct PathRequest {
197    pub params: RpcArguments,
198    pub queries: RpcArguments,
199    pub body: Option<Value>,
200    pub headers: std::collections::HashMap<String, String>,
201}
202
203#[derive(Clone, Debug, PartialEq)]
204pub struct PathInvocationContext {
205    pub request_id: String,
206    pub client_id: String,
207    pub path_type: String,
208    pub method: HttpMethod,
209    pub path: String,
210    pub auth: Option<AuthContext>,
211}
212
213#[derive(Clone, Debug, Default)]
214pub struct EndpointOptions {
215    pub description: Option<String>,
216    pub input_schema: Option<JsonSchema>,
217    pub output_schema: Option<JsonSchema>,
218    pub content_type: Option<String>,
219}
220
221impl EndpointOptions {
222    pub fn new() -> Self {
223        Self::default()
224    }
225
226    pub fn description(mut self, description: impl Into<String>) -> Self {
227        self.description = Some(description.into());
228        self
229    }
230}
231
232#[derive(Clone, Debug)]
233pub struct SkillOptions {
234    pub description: Option<String>,
235    pub content_type: String,
236}
237
238impl Default for SkillOptions {
239    fn default() -> Self {
240        Self {
241            description: None,
242            content_type: "text/markdown".to_string(),
243        }
244    }
245}
246
247impl SkillOptions {
248    pub fn new() -> Self {
249        Self::default()
250    }
251
252    pub fn description(mut self, description: impl Into<String>) -> Self {
253        self.description = Some(description.into());
254        self
255    }
256}
257
258#[derive(Clone, Debug, Default)]
259pub struct PromptOptions {
260    pub description: Option<String>,
261    pub input_schema: Option<JsonSchema>,
262    pub output_schema: Option<JsonSchema>,
263}
264
265impl PromptOptions {
266    pub fn new() -> Self {
267        Self::default()
268    }
269
270    pub fn description(mut self, description: impl Into<String>) -> Self {
271        self.description = Some(description.into());
272        self
273    }
274}