fc_dioxus/behavior/
context.rs1use std::collections::HashMap;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct ModelChoice {
12 pub id: String,
14 pub name: String,
16 pub reasoning: bool,
18 pub parameters: Vec<(String, String)>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct ProviderChoice {
25 pub id: String,
27 pub name: String,
29 pub models: Vec<ModelChoice>,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum Effort {
36 Low,
38 Medium,
40 High,
42}
43impl Effort {
44 pub const ALL: [Self; 3] = [Self::Low, Self::Medium, Self::High];
46 #[must_use]
48 pub const fn as_str(self) -> &'static str {
49 match self {
50 Self::Low => "low",
51 Self::Medium => "medium",
52 Self::High => "high",
53 }
54 }
55 #[must_use]
57 pub const fn label(self) -> &'static str {
58 match self {
59 Self::Low => "Low",
60 Self::Medium => "Medium",
61 Self::High => "High",
62 }
63 }
64 #[must_use]
66 pub fn from_value(value: &str) -> Option<Self> {
67 Self::ALL.into_iter().find(|e| e.as_str() == value)
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct ContextState {
74 pub provider: String,
76 pub model: String,
78 pub effort: Option<Effort>,
80 pub parameters: Vec<(String, String)>,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Default)]
86pub struct ContextModel {
87 providers: Vec<ProviderChoice>,
88 provider: String,
89 model: String,
90 effort: Option<Effort>,
91 remembered: HashMap<String, String>,
93}
94
95impl ContextModel {
96 #[must_use]
98 pub fn new(providers: Vec<ProviderChoice>) -> Self {
99 let first = providers.first().map(|p| p.id.clone()).unwrap_or_default();
100 let mut model = Self {
101 providers,
102 ..Self::default()
103 };
104 model.set_provider(&first);
105 model
106 }
107 #[must_use]
109 pub fn providers(&self) -> &[ProviderChoice] {
110 &self.providers
111 }
112 #[must_use]
114 pub fn provider(&self) -> Option<&ProviderChoice> {
115 self.providers.iter().find(|p| p.id == self.provider)
116 }
117 #[must_use]
119 pub fn model(&self) -> Option<&ModelChoice> {
120 self.provider()?.models.iter().find(|m| m.id == self.model)
121 }
122 #[must_use]
124 pub fn stored_effort(&self) -> Option<Effort> {
125 self.effort
126 }
127 #[must_use]
129 pub fn state(&self) -> ContextState {
130 let model = self.model();
131 ContextState {
132 provider: self.provider.clone(),
133 model: self.model.clone(),
134 effort: model.filter(|m| m.reasoning).and(self.effort),
135 parameters: model.map(|m| m.parameters.clone()).unwrap_or_default(),
136 }
137 }
138 pub fn set_provider(&mut self, provider: &str) {
141 if !self.providers.iter().any(|p| p.id == provider) {
142 return;
143 }
144 if !self.provider.is_empty() {
145 self.remembered
146 .insert(self.provider.clone(), self.model.clone());
147 }
148 let previous = std::mem::take(&mut self.model);
149 provider.clone_into(&mut self.provider);
150 let available: Vec<String> = self
151 .provider()
152 .map(|p| p.models.iter().map(|m| m.id.clone()).collect())
153 .unwrap_or_default();
154 let candidates = [self.remembered.get(provider).cloned(), Some(previous)];
155 self.model = candidates
156 .into_iter()
157 .flatten()
158 .find(|id| !id.is_empty() && available.contains(id))
159 .or_else(|| available.first().cloned())
160 .unwrap_or_default();
161 self.default_effort();
162 }
163 pub fn set_model(&mut self, model: &str) {
165 if self
166 .provider()
167 .is_some_and(|p| p.models.iter().any(|m| m.id == model))
168 {
169 model.clone_into(&mut self.model);
170 self.default_effort();
171 }
172 }
173 pub fn set_effort(&mut self, effort: Effort) {
175 if self.model().is_some_and(|m| m.reasoning) {
176 self.effort = Some(effort);
177 }
178 }
179 fn default_effort(&mut self) {
182 if self.model().is_some_and(|m| m.reasoning) && self.effort.is_none() {
183 self.effort = Some(Effort::Medium);
184 }
185 }
186 #[must_use]
188 pub fn notice(&self) -> Option<&'static str> {
189 if self.model().is_some_and(|m| m.reasoning) {
190 None
191 } else if self.effort.is_some() {
192 Some("Effort is not used by this model")
193 } else {
194 Some("This model has no effort setting")
195 }
196 }
197}