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