Skip to main content

j_cli/command/chat/
model.rs

1use super::theme::ThemeName;
2use crate::config::YamlConfig;
3use crate::error;
4use serde::{Deserialize, Serialize};
5use std::fs;
6use std::path::PathBuf;
7
8// ========== 数据结构 ==========
9
10/// 单个模型提供方配置
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct ModelProvider {
13    /// 显示名称(如 "GPT-4o", "DeepSeek-V3")
14    pub name: String,
15    /// API Base URL(如 "https://api.openai.com/v1")
16    pub api_base: String,
17    /// API Key
18    pub api_key: String,
19    /// 模型名称(如 "gpt-4o", "deepseek-chat")
20    pub model: String,
21}
22
23/// Agent 配置
24#[derive(Debug, Clone, Serialize, Deserialize, Default)]
25pub struct AgentConfig {
26    /// 模型提供方列表
27    #[serde(default)]
28    pub providers: Vec<ModelProvider>,
29    /// 当前选中的 provider 索引
30    #[serde(default)]
31    pub active_index: usize,
32    /// 系统提示词(可选)
33    #[serde(default)]
34    pub system_prompt: Option<String>,
35    /// 是否使用流式输出(默认 true,设为 false 则等回复完整后再显示)
36    #[serde(default = "default_stream_mode")]
37    pub stream_mode: bool,
38    /// 发送给 API 的历史消息数量限制(默认 20 条,避免 token 消耗过大)
39    #[serde(default = "default_max_history_messages")]
40    pub max_history_messages: usize,
41    /// 主题名称(dark / light / midnight)
42    #[serde(default)]
43    pub theme: ThemeName,
44}
45
46fn default_max_history_messages() -> usize {
47    20
48}
49
50/// 默认流式输出
51fn default_stream_mode() -> bool {
52    true
53}
54
55/// 对话消息
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ChatMessage {
58    pub role: String, // "user" | "assistant" | "system"
59    pub content: String,
60}
61
62/// 对话会话
63#[derive(Debug, Clone, Serialize, Deserialize, Default)]
64pub struct ChatSession {
65    pub messages: Vec<ChatMessage>,
66}
67
68// ========== 文件路径 ==========
69
70/// 获取 agent 数据目录: ~/.jdata/agent/data/
71pub fn agent_data_dir() -> PathBuf {
72    let dir = YamlConfig::data_dir().join("agent").join("data");
73    let _ = fs::create_dir_all(&dir);
74    dir
75}
76
77/// 获取 agent 配置文件路径
78pub fn agent_config_path() -> PathBuf {
79    agent_data_dir().join("agent_config.json")
80}
81
82/// 获取对话历史文件路径
83pub fn chat_history_path() -> PathBuf {
84    agent_data_dir().join("chat_history.json")
85}
86
87// ========== 配置读写 ==========
88
89/// 加载 Agent 配置
90pub fn load_agent_config() -> AgentConfig {
91    let path = agent_config_path();
92    if !path.exists() {
93        return AgentConfig::default();
94    }
95    match fs::read_to_string(&path) {
96        Ok(content) => serde_json::from_str(&content).unwrap_or_else(|e| {
97            error!("❌ 解析 agent_config.json 失败: {}", e);
98            AgentConfig::default()
99        }),
100        Err(e) => {
101            error!("❌ 读取 agent_config.json 失败: {}", e);
102            AgentConfig::default()
103        }
104    }
105}
106
107/// 保存 Agent 配置
108pub fn save_agent_config(config: &AgentConfig) -> bool {
109    let path = agent_config_path();
110    if let Some(parent) = path.parent() {
111        let _ = fs::create_dir_all(parent);
112    }
113    match serde_json::to_string_pretty(config) {
114        Ok(json) => match fs::write(&path, json) {
115            Ok(_) => true,
116            Err(e) => {
117                error!("❌ 保存 agent_config.json 失败: {}", e);
118                false
119            }
120        },
121        Err(e) => {
122            error!("❌ 序列化 agent 配置失败: {}", e);
123            false
124        }
125    }
126}
127
128/// 加载对话历史
129pub fn load_chat_session() -> ChatSession {
130    let path = chat_history_path();
131    if !path.exists() {
132        return ChatSession::default();
133    }
134    match fs::read_to_string(&path) {
135        Ok(content) => serde_json::from_str(&content).unwrap_or_else(|_| ChatSession::default()),
136        Err(_) => ChatSession::default(),
137    }
138}
139
140/// 保存对话历史
141pub fn save_chat_session(session: &ChatSession) -> bool {
142    let path = chat_history_path();
143    if let Some(parent) = path.parent() {
144        let _ = fs::create_dir_all(parent);
145    }
146    match serde_json::to_string_pretty(session) {
147        Ok(json) => fs::write(&path, json).is_ok(),
148        Err(_) => false,
149    }
150}