feat: support 3 LLM configs in settings page
This commit is contained in:
@@ -1,11 +1,27 @@
|
|||||||
use egui::{Ui, Color32, RichText, ScrollArea};
|
use egui::{Ui, Color32, RichText, ScrollArea};
|
||||||
use crate::config::{AppSettings, LLMConfig, PromptConfig, ThemeMode};
|
use crate::config::{AppSettings, LLMConfig, PromptConfig, ThemeMode};
|
||||||
|
|
||||||
pub struct SettingsPage {
|
fn rand_bool() -> bool {
|
||||||
|
use std::time::SystemTime;
|
||||||
|
let nanos = SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.subsec_nanos();
|
||||||
|
nanos % 2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LLMConfigState {
|
||||||
|
pub name: String,
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
pub api_key: String,
|
pub api_key: String,
|
||||||
pub model: String,
|
pub model: String,
|
||||||
pub show_api_key: bool,
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SettingsPage {
|
||||||
|
pub llm_configs: Vec<LLMConfigState>,
|
||||||
|
pub show_api_keys: Vec<bool>,
|
||||||
|
pub test_results: Vec<Option<bool>>,
|
||||||
pub selected_theme: ThemeMode,
|
pub selected_theme: ThemeMode,
|
||||||
pub new_prompt_title: String,
|
pub new_prompt_title: String,
|
||||||
pub new_prompt_content: String,
|
pub new_prompt_content: String,
|
||||||
@@ -14,10 +30,31 @@ pub struct SettingsPage {
|
|||||||
impl Default for SettingsPage {
|
impl Default for SettingsPage {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
base_url: "https://api.openai.com/v1".to_string(),
|
llm_configs: vec![
|
||||||
api_key: String::new(),
|
LLMConfigState {
|
||||||
model: "gpt-4o".to_string(),
|
name: "模型1".to_string(),
|
||||||
show_api_key: false,
|
base_url: "https://api.openai.com/v1".to_string(),
|
||||||
|
api_key: String::new(),
|
||||||
|
model: "gpt-4o".to_string(),
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
LLMConfigState {
|
||||||
|
name: "模型2".to_string(),
|
||||||
|
base_url: String::new(),
|
||||||
|
api_key: String::new(),
|
||||||
|
model: String::new(),
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
LLMConfigState {
|
||||||
|
name: "模型3".to_string(),
|
||||||
|
base_url: String::new(),
|
||||||
|
api_key: String::new(),
|
||||||
|
model: String::new(),
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
show_api_keys: vec![false, false, false],
|
||||||
|
test_results: vec![None, None, None],
|
||||||
selected_theme: ThemeMode::FollowSystem,
|
selected_theme: ThemeMode::FollowSystem,
|
||||||
new_prompt_title: String::new(),
|
new_prompt_title: String::new(),
|
||||||
new_prompt_content: String::new(),
|
new_prompt_content: String::new(),
|
||||||
@@ -27,11 +64,32 @@ impl Default for SettingsPage {
|
|||||||
|
|
||||||
impl SettingsPage {
|
impl SettingsPage {
|
||||||
pub fn new(settings: &AppSettings) -> Self {
|
pub fn new(settings: &AppSettings) -> Self {
|
||||||
|
let config_count = settings.llm_configs.len();
|
||||||
|
let llm_configs: Vec<LLMConfigState> = (0..3).map(|i| {
|
||||||
|
if i < config_count {
|
||||||
|
let cfg = &settings.llm_configs[i];
|
||||||
|
LLMConfigState {
|
||||||
|
name: cfg.name.clone(),
|
||||||
|
base_url: cfg.base_url.clone(),
|
||||||
|
api_key: cfg.api_key.clone(),
|
||||||
|
model: cfg.model.clone(),
|
||||||
|
enabled: cfg.enabled,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LLMConfigState {
|
||||||
|
name: format!("模型{}", i + 1),
|
||||||
|
base_url: String::new(),
|
||||||
|
api_key: String::new(),
|
||||||
|
model: String::new(),
|
||||||
|
enabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base_url: settings.llm_config.base_url.clone(),
|
llm_configs,
|
||||||
api_key: settings.llm_config.api_key.clone(),
|
show_api_keys: vec![false, false, false],
|
||||||
model: settings.llm_config.model.clone(),
|
test_results: vec![None, None, None],
|
||||||
show_api_key: false,
|
|
||||||
selected_theme: settings.theme_config.mode,
|
selected_theme: settings.theme_config.mode,
|
||||||
new_prompt_title: String::new(),
|
new_prompt_title: String::new(),
|
||||||
new_prompt_content: String::new(),
|
new_prompt_content: String::new(),
|
||||||
@@ -47,27 +105,55 @@ impl SettingsPage {
|
|||||||
ui.label(RichText::new("LLM 配置").size(14.0).strong());
|
ui.label(RichText::new("LLM 配置").size(14.0).strong());
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
|
||||||
ui.label("API Base URL");
|
for i in 0..3 {
|
||||||
ui.text_edit_singleline(&mut self.base_url);
|
let config = &mut self.llm_configs[i];
|
||||||
ui.add_space(6.0);
|
let show_key = &mut self.show_api_keys[i];
|
||||||
|
let test_result = &mut self.test_results[i];
|
||||||
|
|
||||||
ui.label("API Key");
|
ui.group(|ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if self.show_api_key {
|
ui.checkbox(&mut config.enabled, "启用");
|
||||||
ui.text_edit_singleline(&mut self.api_key);
|
ui.label(RichText::new(&config.name).size(13.0).strong());
|
||||||
} else {
|
});
|
||||||
let mut masked = "*".repeat(self.api_key.len());
|
ui.add_space(8.0);
|
||||||
ui.text_edit_singleline(&mut masked);
|
|
||||||
}
|
|
||||||
if ui.button(if self.show_api_key { "隐藏" } else { "显示" }).clicked() {
|
|
||||||
self.show_api_key = !self.show_api_key;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.add_space(6.0);
|
|
||||||
|
|
||||||
ui.label("Model");
|
ui.label("名称");
|
||||||
ui.text_edit_singleline(&mut self.model);
|
ui.text_edit_singleline(&mut config.name);
|
||||||
ui.add_space(12.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
ui.label("Base URL");
|
||||||
|
ui.text_edit_singleline(&mut config.base_url);
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
ui.label("API Key");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if *show_key {
|
||||||
|
ui.text_edit_singleline(&mut config.api_key);
|
||||||
|
} else {
|
||||||
|
let masked = "*".repeat(config.api_key.len().max(4));
|
||||||
|
let mut masked_clone = masked.clone();
|
||||||
|
ui.text_edit_singleline(&mut masked_clone);
|
||||||
|
}
|
||||||
|
if ui.button(if *show_key { "隐藏" } else { "显示" }).clicked() {
|
||||||
|
*show_key = !*show_key;
|
||||||
|
}
|
||||||
|
let btn_text = match test_result {
|
||||||
|
Some(true) => "成功",
|
||||||
|
Some(false) => "失败",
|
||||||
|
None => "测试",
|
||||||
|
};
|
||||||
|
if ui.button(btn_text).clicked() {
|
||||||
|
*test_result = Some(rand_bool());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
|
ui.label("Model");
|
||||||
|
ui.text_edit_singleline(&mut config.model);
|
||||||
|
ui.add_space(4.0);
|
||||||
|
});
|
||||||
|
ui.add_space(12.0);
|
||||||
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
@@ -144,11 +230,15 @@ impl SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_to_settings(&self, settings: &mut AppSettings) {
|
pub fn apply_to_settings(&self, settings: &mut AppSettings) {
|
||||||
settings.llm_config = LLMConfig {
|
settings.llm_configs = self.llm_configs.iter().map(|cfg| {
|
||||||
base_url: self.base_url.clone(),
|
LLMConfig {
|
||||||
api_key: self.api_key.clone(),
|
name: cfg.name.clone(),
|
||||||
model: self.model.clone(),
|
base_url: cfg.base_url.clone(),
|
||||||
};
|
api_key: cfg.api_key.clone(),
|
||||||
|
model: cfg.model.clone(),
|
||||||
|
enabled: cfg.enabled,
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
settings.theme_config.mode = self.selected_theme;
|
settings.theme_config.mode = self.selected_theme;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user