fix: persist model on conversation send, fallback resolution, carry model on new conversation

This commit is contained in:
Xianren Studio
2026-08-13 23:44:27 +08:00
parent 00624d81cf
commit 95d7ff1299
6 changed files with 36 additions and 12 deletions
Generated
+1
View File
@@ -5544,6 +5544,7 @@ dependencies = [
"dirs 5.0.1", "dirs 5.0.1",
"futures", "futures",
"reqwest 0.12.28", "reqwest 0.12.28",
"rusqlite",
"serde", "serde",
"serde_json", "serde_json",
"tauri", "tauri",
+1
View File
@@ -29,6 +29,7 @@ reqwest.workspace = true
futures.workspace = true futures.workspace = true
uuid.workspace = true uuid.workspace = true
dirs.workspace = true dirs.workspace = true
rusqlite.workspace = true
xianren-core = { path = "../../crates/core" } xianren-core = { path = "../../crates/core" }
xianren-engine = { path = "../../crates/engine" } xianren-engine = { path = "../../crates/engine" }
xianren-download = { path = "../../crates/download" } xianren-download = { path = "../../crates/download" }
+23 -9
View File
@@ -217,10 +217,12 @@ pub fn list_conversations(state: State<'_, App>) -> Result<Vec<xianren_core::Con
pub fn create_conversation( pub fn create_conversation(
state: State<'_, App>, state: State<'_, App>,
title: String, title: String,
model_id: Option<String>,
) -> Result<xianren_core::Conversation, String> { ) -> Result<xianren_core::Conversation, String> {
let id = uuid::Uuid::new_v4().to_string(); let id = uuid::Uuid::new_v4().to_string();
let db = state.core.db.lock().unwrap(); let db = state.core.db.lock().unwrap();
sessions_db::create_conversation(&db, &id, &title, None, None).map_err(|e| e.to_string())?; sessions_db::create_conversation(&db, &id, &title, model_id.as_deref(), None)
.map_err(|e| e.to_string())?;
sessions_db::get_conversation(&db, &id) sessions_db::get_conversation(&db, &id)
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
.ok_or_else(|| "conversation not found".to_string()) .ok_or_else(|| "conversation not found".to_string())
@@ -481,6 +483,9 @@ pub async fn chat_send(
) )
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
} }
// 始终把当前使用的模型写回会话,编辑/重新生成依赖它
sessions_db::update_conversation_model(&db, &payload.conversation_id, &payload.model_id)
.map_err(|e| e.to_string())?;
let user_message_id = sessions_db::add_message( let user_message_id = sessions_db::add_message(
&db, &db,
&payload.conversation_id, &payload.conversation_id,
@@ -724,6 +729,21 @@ fn estimate_tokens(text: &str) -> usize {
score / 4 + 1 score / 4 + 1
} }
/// 解析会话使用的模型:优先会话记录;未记录时若本地只有一个模型则自动使用,否则提示。
fn resolve_conversation_model(
db: &rusqlite::Connection,
conversation: &xianren_core::Conversation,
) -> Result<String, String> {
if let Some(id) = &conversation.model_id {
return Ok(id.clone());
}
let all = models_db::list(db).map_err(|e| e.to_string())?;
if all.len() == 1 {
return Ok(all[0].id.clone());
}
Err("会话未记录模型:请先在该会话发送一条消息,或重新选择模型后重试".into())
}
/// 重新生成某条助手回复:旧内容存入版本历史,截断后重新生成。 /// 重新生成某条助手回复:旧内容存入版本历史,截断后重新生成。
#[tauri::command] #[tauri::command]
pub async fn regenerate_message( pub async fn regenerate_message(
@@ -768,10 +788,7 @@ pub async fn regenerate_message(
images: m.images.clone(), images: m.images.clone(),
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let model_id = conversation let model_id = resolve_conversation_model(&db, &conversation)?;
.model_id
.clone()
.ok_or_else(|| "conversation has no model".to_string())?;
let model = models_db::get(&db, &model_id) let model = models_db::get(&db, &model_id)
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
.ok_or_else(|| "model not found".to_string())?; .ok_or_else(|| "model not found".to_string())?;
@@ -835,10 +852,7 @@ pub async fn edit_message(
images: m.images, images: m.images,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let model_id = conversation let model_id = resolve_conversation_model(&db, &conversation)?;
.model_id
.clone()
.ok_or_else(|| "conversation has no model".to_string())?;
let model = models_db::get(&db, &model_id) let model = models_db::get(&db, &model_id)
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
.ok_or_else(|| "model not found".to_string())?; .ok_or_else(|| "model not found".to_string())?;
+8
View File
@@ -85,6 +85,14 @@ pub fn touch_conversation(db: &Connection, id: &str) -> Result<()> {
Ok(()) Ok(())
} }
pub fn update_conversation_model(db: &Connection, id: &str, model_id: &str) -> Result<()> {
db.execute(
"UPDATE conversations SET model_id = ?1 WHERE id = ?2",
params![model_id, id],
)?;
Ok(())
}
pub fn delete_conversation(db: &Connection, id: &str) -> Result<()> { pub fn delete_conversation(db: &Connection, id: &str) -> Result<()> {
db.execute("DELETE FROM conversations WHERE id = ?1", params![id])?; db.execute("DELETE FROM conversations WHERE id = ?1", params![id])?;
Ok(()) Ok(())
+2 -2
View File
@@ -194,8 +194,8 @@ export const api = {
settingsSet: (key: string, value: string) => settingsSet: (key: string, value: string) =>
invoke<void>("settings_set", { key, value }), invoke<void>("settings_set", { key, value }),
listConversations: () => invoke<Conversation[]>("list_conversations"), listConversations: () => invoke<Conversation[]>("list_conversations"),
createConversation: (title: string) => createConversation: (title: string, modelId?: string) =>
invoke<Conversation>("create_conversation", { title }), invoke<Conversation>("create_conversation", { title, modelId: modelId ?? null }),
deleteConversation: (id: string) => deleteConversation: (id: string) =>
invoke<void>("delete_conversation", { id }), invoke<void>("delete_conversation", { id }),
getMessages: (conversationId: string) => getMessages: (conversationId: string) =>
+1 -1
View File
@@ -331,7 +331,7 @@ export default function ChatPage() {
} }
async function handleNewConversation() { async function handleNewConversation() {
const conv = await api.createConversation("新会话"); const conv = await api.createConversation("新会话", selectedModelId || undefined);
setActiveConvId(conv.id); setActiveConvId(conv.id);
setMessages([]); setMessages([]);
setError(null); setError(null);