Skip to main content

ai_lib_rust/protocol/
config.rs

1//! Protocol configuration structures
2//!
3//! This module contains all the configuration-related structures used in protocol manifests.
4
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// Structured endpoint definition (v1.1+ extension)
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct EndpointDefinition {
11    pub base_url: String,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub protocol: Option<String>, // https, http, ws, wss
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub timeout_ms: Option<u32>,
16}
17
18/// Endpoint configuration for specific operations
19#[derive(Debug, Clone, Serialize)]
20pub struct EndpointConfig {
21    pub path: String,
22    pub method: String,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub adapter: Option<String>,
25}
26
27impl<'de> Deserialize<'de> for EndpointConfig {
28    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
29    where
30        D: serde::Deserializer<'de>,
31    {
32        #[derive(Deserialize)]
33        #[serde(untagged)]
34        enum Input {
35            // Shorthand: endpoint: "/v1/chat/completions"
36            Path(String),
37            // Full form
38            Obj {
39                path: String,
40                #[serde(default = "default_method")]
41                method: String,
42                #[serde(default)]
43                adapter: Option<String>,
44            },
45        }
46
47        match Input::deserialize(deserializer)? {
48            Input::Path(path) => Ok(EndpointConfig {
49                path,
50                method: default_method(),
51                adapter: None,
52            }),
53            Input::Obj {
54                path,
55                method,
56                adapter,
57            } => Ok(EndpointConfig {
58                path,
59                method,
60                adapter,
61            }),
62        }
63    }
64}
65
66fn default_method() -> String {
67    "POST".to_string()
68}
69
70/// Service configuration for auxiliary endpoints
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ServiceConfig {
73    pub path: String,
74    #[serde(default = "default_method_get")]
75    pub method: String,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub headers: Option<HashMap<String, String>>,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub query_params: Option<HashMap<String, String>>,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub response_binding: Option<String>,
82}
83
84fn default_method_get() -> String {
85    "GET".to_string()
86}
87
88/// Capabilities object format (v1.1+)
89/// Required fields: streaming, tools, vision
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct Capabilities {
92    pub streaming: bool,
93    pub tools: bool,
94    pub vision: bool,
95    #[serde(default, skip_serializing_if = "is_false")]
96    pub agentic: bool,
97    #[serde(default, skip_serializing_if = "is_false")]
98    pub parallel_tools: bool,
99    #[serde(default, skip_serializing_if = "is_false")]
100    pub reasoning: bool,
101    #[serde(default, skip_serializing_if = "is_false")]
102    pub multimodal: bool,
103    #[serde(default, skip_serializing_if = "is_false")]
104    pub audio: bool,
105}
106
107fn is_false(b: &bool) -> bool {
108    !*b
109}
110
111/// Authentication configuration
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct AuthConfig {
114    #[serde(rename = "type")]
115    pub auth_type: String,
116    #[serde(default, skip_serializing_if = "Option::is_none")]
117    pub token_env: Option<String>,
118    #[serde(default, skip_serializing_if = "Option::is_none")]
119    pub key_env: Option<String>,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub param_name: Option<String>,
122    #[serde(default, skip_serializing_if = "Option::is_none")]
123    pub header_name: Option<String>,
124    #[serde(default, skip_serializing_if = "Option::is_none")]
125    pub extra_headers: Option<Vec<HeaderConfig>>,
126}
127
128/// Header configuration for extra headers
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct HeaderConfig {
131    pub name: String,
132    pub value: String,
133}
134
135/// Streaming configuration
136#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct StreamingConfig {
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub event_format: Option<String>,
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub decoder: Option<DecoderConfig>,
142    #[serde(skip_serializing_if = "Option::is_none")]
143    pub frame_selector: Option<String>,
144    /// Common path for content delta in streaming frames (provider-specific)
145    #[serde(default, skip_serializing_if = "Option::is_none")]
146    pub content_path: Option<String>,
147    /// Common path for tool call delta in streaming frames (provider-specific)
148    #[serde(default, skip_serializing_if = "Option::is_none")]
149    pub tool_call_path: Option<String>,
150    /// Common path for usage metadata in streaming frames (provider-specific)
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub usage_path: Option<String>,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub candidate: Option<CandidateConfig>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub accumulator: Option<AccumulatorConfig>,
157    #[serde(default)]
158    pub event_map: Vec<EventMapRule>,
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub stop_condition: Option<String>,
161}
162
163/// Decoder configuration for streaming
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct DecoderConfig {
166    pub format: String,
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub strategy: Option<String>,
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub delimiter: Option<String>,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub prefix: Option<String>,
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub done_signal: Option<String>,
175}
176
177/// Candidate configuration for multi-candidate responses
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct CandidateConfig {
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub candidate_id_path: Option<String>,
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub fan_out: Option<bool>,
184}
185
186/// Accumulator configuration for stateful parsing
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct AccumulatorConfig {
189    #[serde(default)]
190    pub stateful_tool_parsing: bool,
191    #[serde(skip_serializing_if = "Option::is_none")]
192    pub key_path: Option<String>,
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub flush_on: Option<String>,
195}
196
197/// Event mapping rule for streaming events
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct EventMapRule {
200    #[serde(rename = "match")]
201    pub match_expr: String,
202    pub emit: String,
203    #[serde(default, skip_serializing_if = "Option::is_none")]
204    pub fields: Option<HashMap<String, String>>,
205}
206
207/// Features configuration
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct FeaturesConfig {
210    #[serde(default, skip_serializing_if = "Option::is_none")]
211    pub multi_candidate: Option<MultiCandidateConfig>,
212    #[serde(default, skip_serializing_if = "Option::is_none")]
213    pub response_mapping: Option<ResponseMappingConfig>,
214}
215
216/// Multi-candidate configuration
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct MultiCandidateConfig {
219    pub support_type: String,
220    #[serde(default, skip_serializing_if = "Option::is_none")]
221    pub param_name: Option<String>,
222    #[serde(default, skip_serializing_if = "Option::is_none")]
223    pub max_concurrent: Option<u32>,
224}
225
226/// Response mapping configuration
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct ResponseMappingConfig {
229    #[serde(default, skip_serializing_if = "Option::is_none")]
230    pub tool_calls: Option<ToolCallsMapping>,
231    #[serde(default, skip_serializing_if = "Option::is_none")]
232    pub error: Option<ErrorMapping>,
233}
234
235/// Tool calls mapping configuration
236#[derive(Debug, Clone, Serialize, Deserialize)]
237pub struct ToolCallsMapping {
238    pub path: String,
239    #[serde(default, skip_serializing_if = "Option::is_none")]
240    pub filter: Option<String>,
241    pub fields: HashMap<String, String>,
242    #[serde(default, skip_serializing_if = "Option::is_none")]
243    pub array_fan_out: Option<bool>,
244}
245
246/// Error mapping configuration
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct ErrorMapping {
249    #[serde(default, skip_serializing_if = "Option::is_none")]
250    pub message_path: Option<String>,
251    #[serde(default, skip_serializing_if = "Option::is_none")]
252    pub code_path: Option<String>,
253    #[serde(default, skip_serializing_if = "Option::is_none")]
254    pub type_path: Option<String>,
255}
256
257/// Termination configuration
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct TerminationConfig {
260    pub source_field: String,
261    #[serde(default, skip_serializing_if = "Option::is_none")]
262    pub mapping: Option<HashMap<String, String>>,
263}
264
265/// Tooling configuration
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct ToolingConfig {
268    pub source_model: String,
269    #[serde(default, skip_serializing_if = "Option::is_none")]
270    pub tool_use: Option<ToolUseMapping>,
271    #[serde(default, skip_serializing_if = "Option::is_none")]
272    pub tool_result: Option<ToolResultMapping>,
273}
274
275/// Tool use mapping
276#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct ToolUseMapping {
278    #[serde(default, skip_serializing_if = "Option::is_none")]
279    pub id_path: Option<String>,
280    #[serde(default, skip_serializing_if = "Option::is_none")]
281    pub name_path: Option<String>,
282    #[serde(default, skip_serializing_if = "Option::is_none")]
283    pub input_path: Option<String>,
284    #[serde(default, skip_serializing_if = "Option::is_none")]
285    pub input_format: Option<String>,
286}
287
288/// Tool result mapping
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct ToolResultMapping {
291    #[serde(default, skip_serializing_if = "Option::is_none")]
292    pub id_path: Option<String>,
293    #[serde(default, skip_serializing_if = "Option::is_none")]
294    pub name_path: Option<String>,
295    #[serde(default, skip_serializing_if = "Option::is_none")]
296    pub response_path: Option<String>,
297}
298
299/// Retry policy configuration
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct RetryPolicy {
302    pub strategy: String,
303    #[serde(default, skip_serializing_if = "Option::is_none")]
304    pub max_retries: Option<u32>,
305    #[serde(default, skip_serializing_if = "Option::is_none")]
306    pub min_delay_ms: Option<u32>,
307    #[serde(default, skip_serializing_if = "Option::is_none")]
308    pub max_delay_ms: Option<u32>,
309    #[serde(default, skip_serializing_if = "Option::is_none")]
310    pub jitter: Option<String>,
311    #[serde(default, skip_serializing_if = "Option::is_none")]
312    pub retry_on_http_status: Option<Vec<u16>>,
313    #[serde(default, skip_serializing_if = "Option::is_none")]
314    pub retry_on_error_status: Option<Vec<String>>,
315}
316
317/// Error classification configuration
318#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct ErrorClassification {
320    #[serde(default, skip_serializing_if = "Option::is_none")]
321    pub by_http_status: Option<HashMap<String, String>>,
322    #[serde(default, skip_serializing_if = "Option::is_none")]
323    pub by_error_status: Option<HashMap<String, String>>,
324}
325
326/// Availability and health checking configuration (v1.1+ extension)
327/// Required fields: required, regions, check
328#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct AvailabilityConfig {
330    pub required: bool,
331    pub regions: Vec<String>, // cn, global, us, eu
332    pub check: HealthCheckConfig,
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub notes: Option<Vec<String>>,
335}
336
337/// Health check endpoint configuration
338/// Required fields: method, path, expected_status
339#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct HealthCheckConfig {
341    pub method: String, // HEAD, GET
342    pub path: String,
343    pub expected_status: Vec<u16>,
344    #[serde(skip_serializing_if = "Option::is_none")]
345    pub timeout_ms: Option<u32>,
346}
347
348/// Rate limit headers configuration
349#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct RateLimitHeaders {
351    #[serde(default, skip_serializing_if = "Option::is_none")]
352    pub requests_limit: Option<String>,
353    #[serde(default, skip_serializing_if = "Option::is_none")]
354    pub requests_remaining: Option<String>,
355    #[serde(default, skip_serializing_if = "Option::is_none")]
356    pub requests_reset: Option<String>,
357    #[serde(default, skip_serializing_if = "Option::is_none")]
358    pub tokens_limit: Option<String>,
359    #[serde(default, skip_serializing_if = "Option::is_none")]
360    pub tokens_remaining: Option<String>,
361    #[serde(default, skip_serializing_if = "Option::is_none")]
362    pub tokens_reset: Option<String>,
363    #[serde(default, skip_serializing_if = "Option::is_none")]
364    pub retry_after: Option<String>,
365}