1use crate::cli::ui::prompt_text_input;
2use crate::system::DoumError::Config;
3use crate::system::error::DoumResult;
4use crate::system::{load_config, save_config};
5use dialoguer::Select;
6use rust_embed::RustEmbed;
7use serde::Deserialize;
8
9#[derive(RustEmbed)]
10#[folder = "static/presets/"]
11struct ModelPresets;
12
13#[derive(Debug, Deserialize, Clone)]
14pub struct ModelInfo {
15 pub id: String,
16 pub name: String,
17 pub description: String,
18}
19
20#[derive(Debug, Deserialize)]
21struct ModelList {
22 models: Vec<ModelInfo>,
23}
24
25pub fn load_presets(provider: &str) -> Vec<ModelInfo> {
27 let filename = format!("{}.toml", provider);
28
29 if let Some(content) = ModelPresets::get(&filename)
30 && let Ok(data) = std::str::from_utf8(content.data.as_ref())
31 && let Ok(list) = toml::from_str::<ModelList>(data)
32 {
33 return list.models;
34 }
35
36 vec![]
37}
38
39pub fn handle_switch_command(provider: Option<String>, model: Option<String>) -> DoumResult<()> {
40 match (provider, model) {
41 (Some(prov), Some(mdl)) => {
43 switch_provider_and_model(&prov, &mdl)?;
44 }
45 (None, None) => {
47 select_provider_and_model()?;
48 }
49 _ => {
51 return Err(Config(
52 "Usage: doum switch [provider] [model] or just doum switch".to_string(),
53 ));
54 }
55 }
56
57 Ok(())
58}
59
60fn select_provider_and_model() -> DoumResult<()> {
62 let providers = vec!["openai", "anthropic"];
63
64 let mut menu_items = Vec::new();
66 let mut mapping = Vec::new(); for provider in &providers {
69 let models = load_presets(provider);
70 for model in models {
71 menu_items.push(format!(
72 "{} - {} | {}",
73 provider.to_uppercase(),
74 model.name,
75 model.description
76 ));
77 mapping.push((provider.to_string(), model.id.clone()));
78 }
79
80 menu_items.push(format!(
82 "{} - Custom (enter manually)",
83 provider.to_uppercase()
84 ));
85 mapping.push((provider.to_string(), "custom".to_string()));
86 }
87
88 let selection = Select::new()
89 .with_prompt("Select provider and model")
90 .items(&menu_items)
91 .default(0)
92 .interact()?;
93
94 let (provider, model_id) = &mapping[selection];
95
96 let model = if model_id == "custom" {
97 prompt_text_input("Enter custom model name", None)?
98 } else {
99 model_id.clone()
100 };
101
102 switch_provider_and_model(provider, &model)?;
103
104 Ok(())
105}
106
107fn switch_provider_and_model(provider: &str, model: &str) -> DoumResult<()> {
109 let mut config = load_config()?;
110
111 if provider != "openai" && provider != "anthropic" {
113 return Err(Config(format!(
114 "Unknown provider: {}. Available: openai, anthropic",
115 provider
116 )));
117 }
118
119 config.llm.provider = provider.to_string();
120 config.llm.model = model.to_string();
121
122 save_config(&config)?;
123
124 println!("✅ Switched to {} - {}", provider, model);
125
126 Ok(())
127}