heartbit_core/config/
provider.rs1#![allow(missing_docs)]
2use serde::Deserialize;
3use std::time::Duration;
4
5#[derive(Debug, Default, Deserialize)]
10pub struct ProviderConfig {
11 #[serde(default)]
12 pub name: String,
13 #[serde(default)]
14 pub model: String,
15 #[serde(default)]
18 pub base_url: Option<String>,
19 #[serde(default)]
22 pub api_key: Option<String>,
23 pub retry: Option<RetryProviderConfig>,
25 #[serde(default)]
28 pub prompt_caching: bool,
29 pub cascade: Option<CascadeConfig>,
32 #[serde(default)]
35 pub circuit: ProviderCircuitConfig,
36}
37
38#[derive(Debug, Clone, Deserialize)]
44pub struct CascadeConfig {
45 #[serde(default)]
47 pub enabled: bool,
48 #[serde(default)]
51 pub tiers: Vec<CascadeTierConfig>,
52 #[serde(default)]
54 pub gate: CascadeGateConfig,
55}
56
57#[derive(Debug, Clone, Deserialize)]
59pub struct CascadeTierConfig {
60 pub model: String,
61}
62
63#[derive(Debug, Clone, Deserialize)]
65#[serde(tag = "type", rename_all = "snake_case")]
66pub enum CascadeGateConfig {
67 Heuristic {
69 #[serde(default = "default_min_output_tokens")]
71 min_output_tokens: u32,
72 #[serde(default = "super::default_true")]
74 accept_tool_calls: bool,
75 #[serde(default = "super::default_true")]
77 escalate_on_max_tokens: bool,
78 },
79}
80
81impl Default for CascadeGateConfig {
82 fn default() -> Self {
83 Self::Heuristic {
84 min_output_tokens: default_min_output_tokens(),
85 accept_tool_calls: true,
86 escalate_on_max_tokens: true,
87 }
88 }
89}
90
91fn default_min_output_tokens() -> u32 {
92 5
93}
94
95#[derive(Debug, Clone, Default, serde::Serialize, Deserialize)]
101#[serde(deny_unknown_fields)]
102pub struct ProviderCircuitConfig {
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub failure_threshold: Option<u32>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub initial_open_duration_seconds: Option<u32>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub max_open_duration_seconds: Option<u32>,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub backoff_multiplier: Option<f64>,
115}
116
117impl From<&ProviderCircuitConfig> for crate::llm::circuit::CircuitConfig {
118 fn from(c: &ProviderCircuitConfig) -> Self {
119 let default = crate::llm::circuit::CircuitConfig::default();
120 Self {
121 failure_threshold: c.failure_threshold.unwrap_or(default.failure_threshold),
122 initial_open_duration: c
123 .initial_open_duration_seconds
124 .map(|s| std::time::Duration::from_secs(u64::from(s)))
125 .unwrap_or(default.initial_open_duration),
126 max_open_duration: c
127 .max_open_duration_seconds
128 .map(|s| std::time::Duration::from_secs(u64::from(s)))
129 .unwrap_or(default.max_open_duration),
130 backoff_multiplier: c.backoff_multiplier.unwrap_or(default.backoff_multiplier),
131 }
132 }
133}
134
135#[derive(Debug, Deserialize)]
137pub struct RetryProviderConfig {
138 #[serde(default = "default_max_retries")]
140 pub max_retries: u32,
141 #[serde(default = "default_base_delay_ms")]
143 pub base_delay_ms: u64,
144 #[serde(default = "default_max_delay_ms")]
146 pub max_delay_ms: u64,
147}
148
149fn default_max_retries() -> u32 {
150 3
151}
152
153fn default_base_delay_ms() -> u64 {
154 500
155}
156
157fn default_max_delay_ms() -> u64 {
158 30_000
159}
160
161impl From<&RetryProviderConfig> for crate::llm::retry::RetryConfig {
162 fn from(r: &RetryProviderConfig) -> Self {
163 Self {
164 max_retries: r.max_retries,
165 base_delay: Duration::from_millis(r.base_delay_ms),
166 max_delay: Duration::from_millis(r.max_delay_ms),
167 }
168 }
169}