adk-model 2.3.0

LLM model integrations for Rust Agent Development Kit (ADK-Rust) (Gemini, OpenAI, Claude, DeepSeek, etc.)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
//! Configuration types for OpenAI providers.

use serde::{Deserialize, Serialize};

/// Reasoning effort level for OpenAI reasoning models (e.g., o1, o3).
///
/// Controls how much reasoning effort the model applies. Maps directly to
/// the OpenAI `reasoning_effort` API field.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ReasoningEffort {
    /// Minimal reasoning — fastest, cheapest.
    Low,
    /// Balanced reasoning (default for most reasoning models).
    Medium,
    /// Maximum reasoning — most thorough but slowest.
    High,
}

/// Complete reasoning-effort vocabulary supported across OpenAI model generations.
///
/// This additive type preserves the original exhaustive [`ReasoningEffort`] enum while
/// exposing the `none`, `minimal`, `xhigh`, and `max` values used by newer models.
/// GPT-5.6 supports every value except `minimal` through the Responses API;
/// Chat Completions supports up to `xhigh`. Older GPT-5 models may support
/// `minimal` but not `max`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OpenAIReasoningEffort {
    /// Disable reasoning for the lowest latency.
    None,
    /// Use minimal reasoning on models that retain the legacy setting.
    Minimal,
    /// Use low reasoning effort.
    Low,
    /// Use balanced reasoning effort.
    Medium,
    /// Use high reasoning effort.
    High,
    /// Use extra-high reasoning effort.
    XHigh,
    /// Use maximum reasoning effort on models that support it through Responses.
    Max,
}

impl From<ReasoningEffort> for OpenAIReasoningEffort {
    fn from(value: ReasoningEffort) -> Self {
        match value {
            ReasoningEffort::Low => Self::Low,
            ReasoningEffort::Medium => Self::Medium,
            ReasoningEffort::High => Self::High,
        }
    }
}

/// Configuration for OpenAI API.
///
/// The API key is redacted from `Debug` output and omitted when serializing.
#[derive(Clone, Serialize, Deserialize)]
pub struct OpenAIConfig {
    /// OpenAI API key. Never serialized; deserializing a config without it
    /// yields an empty key.
    #[serde(skip_serializing, default)]
    pub api_key: String,
    /// Model name (e.g., "gpt-5.6-terra", "gpt-5.6-sol").
    pub model: String,
    /// Optional organization ID.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub organization_id: Option<String>,
    /// Optional project ID.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub project_id: Option<String>,
    /// Optional custom base URL for OpenAI-compatible APIs.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_url: Option<String>,
    /// Reasoning effort for OpenAI reasoning models (o1, o3, etc.).
    ///
    /// When set, the `reasoning_effort` field is included in the API request.
    /// Only applicable to reasoning-capable models.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reasoning_effort: Option<ReasoningEffort>,
}

impl std::fmt::Debug for OpenAIConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("OpenAIConfig")
            .field("api_key", &"[REDACTED]")
            .field("model", &self.model)
            .field("organization_id", &self.organization_id)
            .field("project_id", &self.project_id)
            .field("base_url", &self.base_url)
            .field("reasoning_effort", &self.reasoning_effort)
            .finish()
    }
}

impl Default for OpenAIConfig {
    fn default() -> Self {
        Self {
            api_key: String::new(),
            model: crate::catalog::OPENAI_DEFAULT.to_string(),
            organization_id: None,
            project_id: None,
            base_url: None,
            reasoning_effort: None,
        }
    }
}

impl OpenAIConfig {
    /// Create a new OpenAI config with the given API key and model.
    pub fn new(api_key: impl Into<String>, model: impl Into<String>) -> Self {
        Self { api_key: api_key.into(), model: model.into(), ..Default::default() }
    }

    /// Create a config for an OpenAI-compatible API (e.g., Ollama, vLLM).
    pub fn compatible(
        api_key: impl Into<String>,
        base_url: impl Into<String>,
        model: impl Into<String>,
    ) -> Self {
        Self {
            api_key: api_key.into(),
            model: model.into(),
            base_url: Some(base_url.into()),
            ..Default::default()
        }
    }

    /// Set the organization ID.
    pub fn with_organization(mut self, org_id: impl Into<String>) -> Self {
        self.organization_id = Some(org_id.into());
        self
    }

    /// Set the project ID.
    pub fn with_project(mut self, project_id: impl Into<String>) -> Self {
        self.project_id = Some(project_id.into());
        self
    }

    /// Set the reasoning effort for reasoning models (o1, o3, etc.).
    pub fn with_reasoning_effort(mut self, effort: ReasoningEffort) -> Self {
        self.reasoning_effort = Some(effort);
        self
    }
}

/// Configuration for Azure OpenAI Service.
///
/// The API key is redacted from `Debug` output and omitted when serializing.
#[derive(Clone, Serialize, Deserialize)]
pub struct AzureConfig {
    /// Azure OpenAI API key. Never serialized; deserializing a config without
    /// it yields an empty key.
    #[serde(skip_serializing, default)]
    pub api_key: String,
    /// Azure resource endpoint (e.g., `https://my-resource.openai.azure.com`).
    pub api_base: String,
    /// API version (e.g., "2024-02-15-preview").
    pub api_version: String,
    /// Deployment name/ID.
    pub deployment_id: String,
}

impl std::fmt::Debug for AzureConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("AzureConfig")
            .field("api_key", &"[REDACTED]")
            .field("api_base", &self.api_base)
            .field("api_version", &self.api_version)
            .field("deployment_id", &self.deployment_id)
            .finish()
    }
}

impl AzureConfig {
    /// Create a new Azure OpenAI config.
    pub fn new(
        api_key: impl Into<String>,
        api_base: impl Into<String>,
        api_version: impl Into<String>,
        deployment_id: impl Into<String>,
    ) -> Self {
        Self {
            api_key: api_key.into(),
            api_base: api_base.into(),
            api_version: api_version.into(),
            deployment_id: deployment_id.into(),
        }
    }
}

/// Transport mode for the Responses API.
///
/// Controls whether the client uses standard HTTP/SSE or a persistent
/// WebSocket connection for lower-latency agentic workflows.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum ResponsesTransport {
    /// Standard HTTP/SSE (default).
    #[default]
    Http,
    /// Persistent WebSocket connection (requires `openai-ws` feature).
    WebSocket,
}

/// Service tier for processing priority.
///
/// Controls the processing priority for API requests. Priority tier
/// provides lower latency and more consistent token generation speed.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ServiceTier {
    /// Automatic tier selection (default behavior).
    Auto,
    /// Priority processing for lower latency.
    Priority,
}

/// Prompt cache retention duration.
///
/// Controls how long prompt prefixes are cached for repeated requests.
/// Caching reduces costs for requests that share the same prompt prefix.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PromptCacheRetention {
    /// In-memory cache (shortest retention, lowest cost).
    InMemory,
    /// 24-hour cache retention.
    #[serde(rename = "24h")]
    TwentyFourHours,
}

/// Reasoning summary mode for the Responses API.
///
/// Controls whether and how the model generates a summary of its internal
/// reasoning process. Only applicable to o-series reasoning models.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ReasoningSummary {
    /// Let the model decide whether to include a summary.
    Auto,
    /// Include a brief summary of the reasoning.
    Concise,
    /// Include a thorough summary of the reasoning.
    Detailed,
}

/// Configuration for the OpenAI Responses API client.
///
/// # Example
///
/// ```rust,ignore
/// use adk_model::openai::{OpenAIResponsesConfig, ReasoningEffort, ReasoningSummary};
///
/// let config = OpenAIResponsesConfig::new("sk-...", "o3")
///     .with_reasoning_effort(ReasoningEffort::High)
///     .with_reasoning_summary(ReasoningSummary::Concise);
/// ```
///
/// The API key is redacted from `Debug` output and omitted when serializing.
#[derive(Clone, Serialize, Deserialize)]
pub struct OpenAIResponsesConfig {
    /// OpenAI API key. Never serialized; deserializing a config without it
    /// yields an empty key.
    #[serde(skip_serializing, default)]
    pub api_key: String,
    /// Model name (e.g., "o3", "o4-mini", "gpt-4.1").
    pub model: String,
    /// Optional organization ID.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub organization_id: Option<String>,
    /// Optional project ID.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub project_id: Option<String>,
    /// Optional custom base URL.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_url: Option<String>,
    /// Reasoning effort for o-series models.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reasoning_effort: Option<ReasoningEffort>,
    /// Reasoning summary mode for o-series models.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reasoning_summary: Option<ReasoningSummary>,
    /// Transport mode (HTTP or WebSocket).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transport: Option<ResponsesTransport>,
    /// Default service tier for processing priority.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub service_tier: Option<ServiceTier>,
    /// Default prompt cache retention policy.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prompt_cache_retention: Option<PromptCacheRetention>,
    /// Enable Open Responses mode for third-party compatibility.
    ///
    /// When enabled, relaxes strict OpenAI field validation to support
    /// Open Responses-compatible endpoints (LM Studio, Ollama, vLLM).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub open_responses_mode: Option<bool>,
}

impl std::fmt::Debug for OpenAIResponsesConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("OpenAIResponsesConfig")
            .field("api_key", &"[REDACTED]")
            .field("model", &self.model)
            .field("organization_id", &self.organization_id)
            .field("project_id", &self.project_id)
            .field("base_url", &self.base_url)
            .field("reasoning_effort", &self.reasoning_effort)
            .field("reasoning_summary", &self.reasoning_summary)
            .field("transport", &self.transport)
            .field("service_tier", &self.service_tier)
            .field("prompt_cache_retention", &self.prompt_cache_retention)
            .field("open_responses_mode", &self.open_responses_mode)
            .finish()
    }
}

impl OpenAIResponsesConfig {
    /// Create a new Responses API config with the given API key and model.
    pub fn new(api_key: impl Into<String>, model: impl Into<String>) -> Self {
        Self {
            api_key: api_key.into(),
            model: model.into(),
            organization_id: None,
            project_id: None,
            base_url: None,
            reasoning_effort: None,
            reasoning_summary: None,
            transport: None,
            service_tier: None,
            prompt_cache_retention: None,
            open_responses_mode: None,
        }
    }

    /// Set the organization ID.
    #[must_use]
    pub fn with_organization(mut self, org_id: impl Into<String>) -> Self {
        self.organization_id = Some(org_id.into());
        self
    }

    /// Set the project ID.
    #[must_use]
    pub fn with_project(mut self, project_id: impl Into<String>) -> Self {
        self.project_id = Some(project_id.into());
        self
    }

    /// Set the base URL.
    #[must_use]
    pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
        self.base_url = Some(base_url.into());
        self
    }

    /// Set the reasoning effort for o-series models.
    #[must_use]
    pub fn with_reasoning_effort(mut self, effort: ReasoningEffort) -> Self {
        self.reasoning_effort = Some(effort);
        self
    }

    /// Set the reasoning summary mode for o-series models.
    #[must_use]
    pub fn with_reasoning_summary(mut self, summary: ReasoningSummary) -> Self {
        self.reasoning_summary = Some(summary);
        self
    }

    /// Set the transport mode (HTTP or WebSocket).
    ///
    /// Defaults to HTTP when not set. WebSocket requires the `openai-ws` feature.
    #[must_use]
    pub fn with_transport(mut self, transport: ResponsesTransport) -> Self {
        self.transport = Some(transport);
        self
    }

    /// Set the default service tier for processing priority.
    ///
    /// Priority tier provides lower latency and more consistent token generation.
    #[must_use]
    pub fn with_service_tier(mut self, tier: ServiceTier) -> Self {
        self.service_tier = Some(tier);
        self
    }

    /// Set the default prompt cache retention policy.
    ///
    /// Controls how long prompt prefixes are cached for cost optimization.
    #[must_use]
    pub fn with_prompt_cache_retention(mut self, retention: PromptCacheRetention) -> Self {
        self.prompt_cache_retention = Some(retention);
        self
    }

    /// Enable or disable Open Responses mode.
    ///
    /// When enabled, relaxes strict OpenAI field validation for compatibility
    /// with third-party Open Responses-compatible endpoints.
    #[must_use]
    pub fn with_open_responses_mode(mut self, enabled: bool) -> Self {
        self.open_responses_mode = Some(enabled);
        self
    }
}