systemprompt_models/services/ai/
model.rs1use serde::{Deserialize, Serialize};
14
15use super::config::ResilienceSettings;
16
17const fn default_true() -> bool {
18 true
19}
20
21#[expect(
22 clippy::struct_excessive_bools,
23 reason = "model capability matrix: each bool is an independent provider feature flag, not \
24 state"
25)]
26#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, schemars::JsonSchema)]
27pub struct ModelCapabilities {
28 #[serde(default)]
29 pub vision: bool,
30
31 #[serde(default)]
32 pub audio_input: bool,
33
34 #[serde(default)]
35 pub video_input: bool,
36
37 #[serde(default)]
38 pub image_generation: bool,
39
40 #[serde(default)]
41 pub audio_generation: bool,
42
43 #[serde(default)]
44 pub streaming: bool,
45
46 #[serde(default)]
47 pub tools: bool,
48
49 #[serde(default)]
50 pub structured_output: bool,
51
52 #[serde(default)]
53 pub system_prompts: bool,
54
55 #[serde(default)]
56 pub image_resolution_config: bool,
57}
58
59#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, schemars::JsonSchema)]
60pub struct ModelLimits {
61 #[serde(default)]
62 pub context_window: u32,
63
64 #[serde(default)]
65 pub max_output_tokens: u32,
66
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub max_thinking_budget: Option<u32>,
69}
70
71#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, schemars::JsonSchema)]
72pub struct ModelPricing {
73 #[serde(default)]
74 pub input_per_million: f64,
75
76 #[serde(default)]
77 pub output_per_million: f64,
78
79 #[serde(default)]
80 pub cache_read_per_million: f64,
81
82 #[serde(default)]
83 pub cache_write_per_million: f64,
84
85 #[serde(default)]
86 pub per_image_cents: Option<f64>,
87}
88
89#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, schemars::JsonSchema)]
97#[serde(deny_unknown_fields)]
98pub struct ModelGovernance {
99 #[serde(default)]
100 pub european: bool,
101
102 #[serde(default)]
103 pub no_retain: bool,
104}
105
106impl ModelPricing {
107 #[must_use]
108 pub fn is_billable(&self) -> bool {
109 if self.per_image_cents.is_some_and(|c| c > 0.0) {
110 return true;
111 }
112 self.input_per_million > 0.0 && self.output_per_million > 0.0
113 }
114}
115
116#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
117pub struct ModelDefinition {
118 #[serde(default)]
119 pub capabilities: ModelCapabilities,
120
121 #[serde(default)]
122 pub limits: ModelLimits,
123
124 #[serde(default)]
125 pub pricing: ModelPricing,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct AiProviderConfig {
136 #[serde(default = "default_true")]
137 pub enabled: bool,
138
139 #[serde(default)]
140 pub default_model: String,
141
142 #[serde(default)]
143 pub default_image_model: String,
144
145 #[serde(default)]
146 pub google_search_enabled: bool,
147
148 #[serde(default)]
149 pub resilience: ResilienceSettings,
150}
151
152impl Default for AiProviderConfig {
153 fn default() -> Self {
154 Self {
155 enabled: true,
156 default_model: String::new(),
157 default_image_model: String::new(),
158 google_search_enabled: false,
159 resilience: ResilienceSettings::default(),
160 }
161 }
162}