1use crate::config::types::{ReasoningEffortLevel, VerbosityLevel};
2use serde::{Deserialize, Serialize};
3use serde_json::{Value, json};
4use std::sync::Arc;
5
6use super::{Message, ToolDefinition};
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
11pub struct FallbackModel {
12 pub model: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub max_tokens: Option<u32>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub thinking: Option<AnthropicThinkingConfig>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, Default)]
24#[serde(tag = "type", rename_all = "lowercase")]
25pub enum AnthropicThinkingConfig {
26 #[default]
27 Disabled,
28 Enabled {
29 budget_tokens: u32,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 display: Option<String>,
32 },
33 Adaptive {
34 #[serde(skip_serializing_if = "Option::is_none")]
35 display: Option<String>,
36 },
37}
38
39#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
40#[serde(rename_all = "snake_case")]
41pub enum PromptCacheProfile {
42 BudgetContinuation,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
46#[serde(rename_all = "snake_case")]
47pub enum AnthropicThinkingModeOverride {
48 #[default]
49 Inherit,
50 Disabled,
51 Adaptive,
52 ManualBudget(u32),
53}
54
55#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
56#[serde(rename_all = "snake_case")]
57pub enum AnthropicThinkingDisplayOverride {
58 #[default]
59 Inherit,
60 Summarized,
61 Omitted,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
65#[serde(rename_all = "snake_case")]
66pub enum AnthropicOptionalStringOverride {
67 #[default]
68 Inherit,
69 Omit,
70 Explicit(String),
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
74#[serde(rename_all = "snake_case")]
75pub enum AnthropicOptionalU32Override {
76 #[default]
77 Inherit,
78 Omit,
79 Explicit(u32),
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
83pub struct AnthropicRequestOverrides {
84 #[serde(default)]
85 pub thinking_mode: AnthropicThinkingModeOverride,
86 #[serde(default)]
87 pub thinking_display: AnthropicThinkingDisplayOverride,
88 #[serde(default)]
89 pub effort: AnthropicOptionalStringOverride,
90 #[serde(default)]
91 pub task_budget_tokens: AnthropicOptionalU32Override,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, Default)]
96
97pub struct LLMRequest {
98 pub messages: Vec<Message>,
99 pub system_prompt: Option<Arc<String>>,
100 pub tools: Option<Arc<Vec<ToolDefinition>>>,
101 pub model: String,
102 pub max_tokens: Option<u32>,
103 pub temperature: Option<f32>,
104 pub stream: bool,
105
106 pub output_format: Option<Value>,
109
110 pub tool_choice: Option<ToolChoice>,
113
114 pub parallel_tool_calls: Option<bool>,
116
117 pub parallel_tool_config: Option<Box<ParallelToolConfig>>,
119
120 pub reasoning_effort: Option<ReasoningEffortLevel>,
123
124 pub effort: Option<String>,
129
130 pub verbosity: Option<VerbosityLevel>,
133
134 pub do_sample: Option<bool>,
136 pub top_p: Option<f32>,
137 pub top_k: Option<i32>,
138 pub presence_penalty: Option<f32>,
139 pub frequency_penalty: Option<f32>,
140 pub stop_sequences: Option<Vec<String>>,
141 pub thinking_budget: Option<u32>,
144
145 pub betas: Option<Vec<String>>,
147
148 pub context_management: Option<Value>,
150
151 pub prefill: Option<String>,
154
155 pub character_reinforcement: bool,
157
158 pub character_name: Option<String>,
160
161 pub coding_agent_settings: Option<Box<CodingAgentSettings>>,
163
164 pub metadata: Option<Value>,
167
168 pub previous_response_id: Option<String>,
171
172 pub response_store: Option<bool>,
175
176 pub responses_include: Option<Vec<String>>,
179
180 pub service_tier: Option<String>,
183
184 pub prompt_cache_key: Option<String>,
187
188 pub prompt_cache_profile: Option<PromptCacheProfile>,
190
191 pub fallbacks: Option<Vec<FallbackModel>>,
194
195 pub fallback_credit_token: Option<String>,
200
201 pub anthropic_request_overrides: Option<AnthropicRequestOverrides>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
208pub struct ResponsesCompactionOptions {
209 pub instructions: Option<String>,
211 pub max_output_tokens: Option<u32>,
213 pub reasoning_effort: Option<ReasoningEffortLevel>,
215 pub verbosity: Option<VerbosityLevel>,
217 pub responses_include: Option<Vec<String>>,
219 pub response_store: Option<bool>,
221 pub service_tier: Option<String>,
223 pub prompt_cache_key: Option<String>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, Default)]
229pub struct CodingAgentSettings {
230 pub force_xml_tags: bool,
232 pub prefill_thought: bool,
234 pub allow_uncertainty: bool,
236 pub strict_grounding: bool,
238 pub long_context_optimization: bool,
240 pub use_xml_document_format: bool,
242 pub force_quote_grounding: bool,
244 pub role_specialization: Option<String>,
246 pub enforce_structured_thought: bool,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
254#[serde(untagged)]
255#[derive(Default)]
256pub enum ToolChoice {
257 #[default]
260 Auto,
261
262 None,
265
266 Any,
269
270 Specific(SpecificToolChoice),
273
274 AllowedTools(AllowedToolsChoice),
281}
282
283#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
284#[serde(rename_all = "lowercase")]
285pub enum AllowedToolsMode {
286 Auto,
287 Required,
288}
289
290impl AllowedToolsMode {
291 pub fn as_str(self) -> &'static str {
292 match self {
293 Self::Auto => "auto",
294 Self::Required => "required",
295 }
296 }
297
298 fn anthropic_mode(self) -> &'static str {
299 match self {
300 Self::Auto => "auto",
301 Self::Required => "any",
302 }
303 }
304
305 fn gemini_mode(self) -> &'static str {
306 match self {
307 Self::Auto => "auto",
308 Self::Required => "any",
309 }
310 }
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct AllowedToolsChoice {
315 pub mode: AllowedToolsMode,
316 pub tools: Vec<String>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct SpecificToolChoice {
322 #[serde(rename = "type")]
323 pub tool_type: String, pub function: SpecificFunctionChoice,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct SpecificFunctionChoice {
331 pub name: String,
332}
333
334impl ToolChoice {
335 pub fn auto() -> Self {
337 Self::Auto
338 }
339
340 pub fn none() -> Self {
342 Self::None
343 }
344
345 pub fn any() -> Self {
347 Self::Any
348 }
349
350 pub fn function(name: String) -> Self {
352 Self::Specific(SpecificToolChoice {
353 tool_type: "function".to_owned(),
354 function: SpecificFunctionChoice { name },
355 })
356 }
357
358 pub fn allowed_tools_auto(tools: Vec<String>) -> Self {
359 Self::AllowedTools(AllowedToolsChoice {
360 mode: AllowedToolsMode::Auto,
361 tools,
362 })
363 }
364
365 pub fn allows_parallel_tools(&self) -> bool {
368 match self {
369 Self::Auto => true,
371 Self::Any => true,
373 Self::AllowedTools(choice) => matches!(choice.mode, AllowedToolsMode::Auto),
374 Self::Specific(_) => false,
376 Self::None => false,
378 }
379 }
380
381 pub fn description(&self) -> &'static str {
383 match self {
384 Self::Auto => "Model decides when to use tools (allows parallel)",
385 Self::None => "No tools will be used",
386 Self::Any => "At least one tool must be used (allows parallel)",
387 Self::AllowedTools(_) => "Model decides when to use the currently allowed tool subset",
388 Self::Specific(_) => "Specific tool must be used (no parallel)",
389 }
390 }
391
392 const OPENAI_STYLE_PROVIDERS: &'static [&'static str] = &[
394 "openai",
395 "deepseek",
396 "huggingface",
397 "mistral",
398 "openrouter",
399 "zai",
400 "moonshot",
401 "stepfun",
402 "evolink",
403 "lmstudio",
404 "llamacpp",
405 ];
406
407 #[inline]
409 pub fn to_provider_format(&self, provider: &str) -> Value {
410 if Self::OPENAI_STYLE_PROVIDERS.contains(&provider) {
411 return self.to_openai_format();
412 }
413
414 match provider {
415 "anthropic" => self.to_anthropic_format(),
416 "gemini" => self.to_gemini_format(),
417 _ => self.to_openai_format(), }
419 }
420
421 #[inline]
422 fn to_openai_format(&self) -> Value {
423 match self {
424 Self::Auto => json!("auto"),
425 Self::None => json!("none"),
426 Self::Any => json!("required"),
427 Self::Specific(choice) => json!(choice),
428 Self::AllowedTools(choice) => json!(choice.mode.as_str()),
429 }
430 }
431
432 #[inline]
433 fn to_anthropic_format(&self) -> Value {
434 match self {
435 Self::Auto => json!({"type": "auto"}),
436 Self::None => json!({"type": "none"}),
437 Self::Any => json!({"type": "any"}),
438 Self::Specific(choice) => json!({"type": "tool", "name": &choice.function.name}),
439 Self::AllowedTools(choice) => json!({"type": choice.mode.anthropic_mode()}),
440 }
441 }
442
443 #[inline]
444 fn to_gemini_format(&self) -> Value {
445 match self {
446 Self::Auto => json!({"mode": "auto"}),
447 Self::None => json!({"mode": "none"}),
448 Self::Any => json!({"mode": "any"}),
449 Self::AllowedTools(choice) => json!({"mode": choice.mode.gemini_mode()}),
450 Self::Specific(choice) => {
451 json!({"mode": "any", "allowed_function_names": [&choice.function.name]})
452 }
453 }
454 }
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct ParallelToolConfig {
461 pub disable_parallel_tool_use: bool,
464
465 pub max_parallel_tools: Option<usize>,
468
469 pub encourage_parallel: bool,
471}
472
473impl Default for ParallelToolConfig {
474 fn default() -> Self {
475 Self {
476 disable_parallel_tool_use: false,
477 max_parallel_tools: Some(5), encourage_parallel: true,
479 }
480 }
481}
482
483impl ParallelToolConfig {
484 pub fn anthropic_optimized() -> Self {
486 Self {
487 disable_parallel_tool_use: false,
488 max_parallel_tools: None, encourage_parallel: true,
490 }
491 }
492
493 pub fn sequential_only() -> Self {
495 Self {
496 disable_parallel_tool_use: true,
497 max_parallel_tools: Some(1),
498 encourage_parallel: false,
499 }
500 }
501}