From 7b3880d2c698fbb8213f9f79c40dd368c51f0dd5 Mon Sep 17 00:00:00 2001 From: Zhang Minghan Date: Tue, 24 Oct 2023 21:21:27 +0800 Subject: [PATCH] add save history model feature --- app/src/components/home/ModelSelector.tsx | 9 +++++++++ app/src/components/home/SideBar.tsx | 9 ++++++--- app/src/conf.ts | 2 +- app/src/conversation/conversation.ts | 22 +++++++++++++++++++++- app/src/conversation/history.ts | 6 +++++- app/src/conversation/manager.ts | 10 ++++++++-- app/src/conversation/types.ts | 2 ++ app/src/events/model.ts | 5 +++++ app/src/i18n.ts | 3 ++- manager/conversation/storage.go | 4 ++-- 10 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 app/src/events/model.ts diff --git a/app/src/components/home/ModelSelector.tsx b/app/src/components/home/ModelSelector.tsx index 9aee97b3..97b2f4f6 100644 --- a/app/src/components/home/ModelSelector.tsx +++ b/app/src/components/home/ModelSelector.tsx @@ -7,6 +7,7 @@ import { selectAuthenticated } from "../../store/auth.ts"; import { useToast } from "../ui/use-toast.ts"; import { useEffect } from "react"; import { Model } from "../../conversation/types.ts"; +import { modelEvent } from "../../events/model.ts"; function GetModel(name: string): Model { return supportModels.find((model) => model.id === name) as Model; @@ -24,6 +25,14 @@ function ModelSelector() { if (auth && model === "GPT-3.5") dispatch(setModel("GPT-3.5-16k")); }, [auth]); + modelEvent.bind((target: string) => { + if (supportModels.find((m) => m.id === target)) { + if (model === target) return; + console.debug(`[chat] toggle model from event: ${target}`); + dispatch(setModel(target)); + } + }); + const list = supportModels.map( (model: Model): SelectItemProps => ({ name: model.id, diff --git a/app/src/components/home/SideBar.tsx b/app/src/components/home/SideBar.tsx index ba582d83..bbba7d24 100644 --- a/app/src/components/home/SideBar.tsx +++ b/app/src/components/home/SideBar.tsx @@ -22,7 +22,7 @@ import { } from "../../conversation/history.ts"; import { Button } from "../ui/button.tsx"; import { setMenu } from "../../store/menu.ts"; -import {Copy, Eraser, LogIn, Plus, RotateCw} from "lucide-react"; +import { Copy, Eraser, LogIn, Plus, RotateCw } from "lucide-react"; import ConversationSegment from "./ConversationSegment.tsx"; import { AlertDialog, @@ -32,7 +32,8 @@ import { AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, - AlertDialogTitle, AlertDialogTrigger, + AlertDialogTitle, + AlertDialogTrigger, } from "../ui/alert-dialog.tsx"; import { getSharedLink, @@ -85,7 +86,9 @@ function SideBar() { - {t("conversation.remove-all-title")} + + {t("conversation.remove-all-title")} + {t("conversation.remove-all-description")} diff --git a/app/src/conf.ts b/app/src/conf.ts index 9cf2523f..d65e2ec8 100644 --- a/app/src/conf.ts +++ b/app/src/conf.ts @@ -1,7 +1,7 @@ import axios from "axios"; import { Model } from "./conversation/types.ts"; -export const version = "3.5.5"; +export const version = "3.5.6"; export const dev: boolean = window.location.hostname === "localhost"; export const deploy: boolean = true; export let rest_api: string = "http://localhost:8094"; diff --git a/app/src/conversation/conversation.ts b/app/src/conversation/conversation.ts index 8e76e730..b316d3ae 100644 --- a/app/src/conversation/conversation.ts +++ b/app/src/conversation/conversation.ts @@ -2,6 +2,9 @@ import { ChatProps, Connection, StreamMessage } from "./connection.ts"; import { Message } from "./types.ts"; import { sharingEvent } from "../events/sharing.ts"; import { connectionEvent } from "../events/connection.ts"; +import { AppDispatch } from "../store"; +import { setMessages } from "../store/chat.ts"; +import { modelEvent } from "../events/model.ts"; type ConversationCallback = (idx: number, message: Message[]) => boolean; @@ -10,6 +13,7 @@ export class Conversation { protected callback?: ConversationCallback; protected idx: number; public id: number; + public model: string; public data: Message[]; public end: boolean; @@ -18,6 +22,7 @@ export class Conversation { this.data = []; this.idx = -1; this.id = id; + this.model = ""; this.end = true; this.connection = new Connection(this.id); @@ -108,10 +113,25 @@ export class Conversation { this.callback = callback; } + public setModel(model?: string) { + if (!model) return; + this.model = model; + } + + public getModel(): string { + return this.model; + } + + public toggle(dispatch: AppDispatch): void { + dispatch(setMessages(this.copyMessages())); + modelEvent.emit(this.getModel()); + } + public triggerCallback() { if (!this.callback) return; const interval = setInterval(() => { - const state = this.callback && this.callback(this.id, this.copyMessages()); + const state = + this.callback && this.callback(this.id, this.copyMessages()); if (state) clearInterval(interval); }, 100); } diff --git a/app/src/conversation/history.ts b/app/src/conversation/history.ts index 5969e785..31be451a 100644 --- a/app/src/conversation/history.ts +++ b/app/src/conversation/history.ts @@ -23,7 +23,11 @@ export async function loadConversation( ): Promise { const resp = await axios.get(`/conversation/load?id=${id}`); if (resp.data.status) return resp.data.data as ConversationInstance; - return { id, name: "", message: [] }; + return { + id, + name: "", + message: [{ role: "assistant", content: "load conversation failed" }], + }; } export async function deleteConversation( diff --git a/app/src/conversation/manager.ts b/app/src/conversation/manager.ts index d56afdab..e8a4535b 100644 --- a/app/src/conversation/manager.ts +++ b/app/src/conversation/manager.ts @@ -57,7 +57,10 @@ export class Manager { public createConversation(id: number): Conversation { console.debug(`[manager] create conversation instance (id: ${id})`); const _this = this; - return new Conversation(id, function (idx: number, message: Message[]): boolean { + return new Conversation(id, function ( + idx: number, + message: Message[], + ): boolean { return _this.callback(idx, message); }); } @@ -66,15 +69,18 @@ export class Manager { if (this.conversations[id]) return; const instance = this.createConversation(id); this.conversations[id] = instance; + const res = await loadConversation(id); instance.load(res.message); + instance.setModel(res.model); } public async toggle(dispatch: AppDispatch, id: number): Promise { if (!this.conversations[id]) await this.add(id); + this.current = id; dispatch(setCurrent(id)); - dispatch(setMessages(this.get(id)!.copyMessages())); + this.get(id)!.toggle(dispatch); } public async delete(dispatch: AppDispatch, id: number): Promise { diff --git a/app/src/conversation/types.ts b/app/src/conversation/types.ts index 1a273b87..6f5db256 100644 --- a/app/src/conversation/types.ts +++ b/app/src/conversation/types.ts @@ -21,6 +21,8 @@ export type ConversationInstance = { id: number; name: string; message: Message[]; + model?: string; + shared?: boolean; }; export type ConversationMapper = Record; diff --git a/app/src/events/model.ts b/app/src/events/model.ts new file mode 100644 index 00000000..6f4292d0 --- /dev/null +++ b/app/src/events/model.ts @@ -0,0 +1,5 @@ +import { EventCommitter } from "./struct.ts"; + +export const modelEvent = new EventCommitter({ + name: "model", +}); diff --git a/app/src/i18n.ts b/app/src/i18n.ts index 27cb89bf..7c8e779b 100644 --- a/app/src/i18n.ts +++ b/app/src/i18n.ts @@ -229,7 +229,8 @@ const resources = { "remove-title": "是否确定?", "remove-description": "此操作无法撤消。这将永久删除对话 ", "remove-all-title": "清除历史", - "remove-all-description": "此操作无法撤消。这将永久删除所有对话,是否继续?", + "remove-all-description": + "此操作无法撤消。这将永久删除所有对话,是否继续?", cancel: "取消", delete: "删除", "delete-conversation": "删除对话", diff --git a/manager/conversation/storage.go b/manager/conversation/storage.go index 45fb13da..9a1d747f 100644 --- a/manager/conversation/storage.go +++ b/manager/conversation/storage.go @@ -62,8 +62,8 @@ func LoadConversation(db *sql.DB, userId int64, conversationId int64) *Conversat SELECT conversation_name, model, data FROM conversation WHERE user_id = ? AND conversation_id = ? `, userId, conversationId).Scan(&conversation.Name, &model, &data) - if value, ok := model.(string); ok { - conversation.Model = value + if value, ok := model.([]byte); ok { + conversation.Model = string(value) } else { conversation.Model = globals.GPT3Turbo }