Tauri项目中实现ChatGPT对话,软软AI中实现ChatGPT接口接入
需求简述
- 在tauri的项目中增加与Chat机器人进行会话的功能
- 可以选择Chat机器人的模型
- 可以实现多会话的功能
- 支持会话的设置,可以进行会话名称修改、删除会话、清空等
- ......等等
先展示下完成之后的截图
整体截图
添加会话
会话设置
sqlite数据存储以及接口封装
数据建模(表结构)
- 会话
- 消息
具体的表结构参见下面语句。(索引可根据需求建立)
CREATE TABLE conversation (`id` INTEGER PRIMARY KEY, `uid` TEXT DEFAULT '', `category` TEXT DEFAULT '', `name` TEXT DEFAULT '', `avatar` TEXT DEFAULT '',`args` TEXT DEFAULT '{}', created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE messages (`id` INTEGER PRIMARY KEY, `conversation_uid` TEXT DEFAULT '', `conversation_category` TEXT DEFAULT '', `sender_type` TEXT DEFAULT '', `sender_id` TEXT DEFAULT '', `avatar` TEXT DEFAULT '', `bot_role` TEXT DEFAULT '',`model_id` TEXT DEFAULT '',`model_options` TEXT DEFAULT '',`text` TEXT DEFAULT '', `typing` TEXT DEFAULT 'false',`purpose_id` TEXT DEFAULT '', `tokens_count` INTEGER DEFAULT 0,created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
CREATE TRIGGER conversation_updated AFTER UPDATE ON conversation
BEGIN
UPDATE conversation SET updated=CURRENT_TIMESTAMP WHERE id = new.id;
END;
CREATE TRIGGER messages_updated AFTER UPDATE ON messages
BEGIN
UPDATE messages SET updated=CURRENT_TIMESTAMP WHERE id = new.id;
END;
会话接口
简单的CRUD代码
import { SQLite } from '@/tauri/sqlite/index';
import { CONVERSATION_DB_NAME, ConversationEntity } from '@/databases/conversation/index';
import { getUuid } from '@/utils';
import { deleteMessagesByConversationUid } from './message-service';
const db = new SQLite(CONVERSATION_DB_NAME);
export const getConversationsByType = async (conversationType: string): Promise<{ total: number, data: Array<ConversationEntity> }> => {
const rows = await db.queryWithArgs<Array<{ [key: string]: any }>>("SELECT id,uid,category,name,avatar,args,created,updated FROM conversation WHERE category=:category order by id", {
":category": conversationType
});
console.log(rows);
let data = rows.map((item, index) => {
let conversation: ConversationEntity = {
id: item.id,
uid: item.uid,
category: item.category,
name: item.name,
avatar: item.avatar,
args: JSON.parse(item.args),
created: item.created,
updated: item.updated,
};
return conversation;
});
return {
total: rows.length,
data: data
};
}
export const createConversation = async (conversationType: string, name: string, avatar?: string, options?: any): Promise<string> => {
let uid = getUuid(false);
console.log(conversationType, name, avatar, options);
let res = await db.execute(`INSERT INTO conversation (uid,category,name,avatar,args)VALUES (:uid,:category,:name,:avatar,:args)`, {
":uid": uid,
":category": conversationType,
":name": name,
":avatar": avatar || "",
":args": options ? JSON.stringify(options) : "{}",
});
console.log(res);
return uid;
}
export const updateConversationByUid = async (uid: string, conversationType: string, name: string, avatar?: string, options?: any) => {
let res = await db.execute(`UPDATE conversation SET category = :category, name = :name, avatar = :avatar, args = :args where uid = :uid`, {
":uid": uid,
":category": conversationType,
":name": name,
":avatar": avatar || "",
":args": options ? JSON.stringify(options) : "{}",
});
console.log(res);
}
export const updateConversationNameByUid = async (uid: string, name: string) => {
let res = await db.execute(`UPDATE conversation SET name = :name where uid = :uid`, {
":uid": uid,
":name": name,
});
console.log(res);
}
export const updateConversationArgsByUid = async (uid: string, args: any) => {
let res = await db.execute(`UPDATE conversation SET args = :args where uid = :uid`, {
":uid": uid,
":args": JSON.stringify(args),
});
console.log(res);
}
export const queryConversationByUid = async (uid: string): Promise<ConversationEntity | null> => {
const rows = await db.queryWithArgs<Array<{ [key: string]: any }>>("SELECT id,uid,category,name,avatar,args,created,updated FROM conversation WHERE uid=:uid order by id", {
":uid": uid
});
console.log(rows);
if (rows && rows.length > 0) {
let item = rows[0];
let conversation: ConversationEntity = {
id: item.id,
uid: item.uid,
category: item.category,
name: item.name,
avatar: item.avatar,
args: JSON.parse(item.args),
created: item.created,
updated: item.updated,
};
return conversation;
} else {
return null;
}
}
export const deleteConversation = async (uid: string): Promise<boolean> => {
//删除会话
let res1 = await db.execute("delete from conversation where uid = :uid", {
":uid": uid
});
//删除会话下所有的消息
let res2 = await deleteMessagesByConversationUid(uid);
return true;
}
消息的CRUD功能类似
ChatGPT的接口
- 这里需要提前申请KEY,然后使用一台外部的机器做代理封装了一个websocket服务接口。
- 在Tauri项目中websocket进行调用
(这里不进行详细描述,有需求可以联系笔者)
会话列表
桌面版需要注意的一些地方点击可以拖动整个窗口,需要再适当的位置增加data-tauri-drag-region。
在会话的标题处增加了一个可以控制会话列表是否折叠的功能。
{
menuFolded ? ('') : (
<div className={styles.left}>
<div data-tauri-drag-region className={styles.height24}></div>
<SideHeader activeModule={conversationType!} className={styles.side_header} onAddConversation={async (conversationType) => {
//弹出对话框
setAddShown(true);
}}></SideHeader>
<SideList className={styles.side}>
{
items && items.map((item: any, index: number) => {
return (
<SideListItem
key={index}
className={classnames(styles.side_item)}
active={item.uid === conversationId}
title={item.name}
avatar={<ConversationIcon conversationType={conversationType} item={item} className={classnames(styles.side_item_avatar)}></ConversationIcon>}
avatarBackground={avatarBackgroundColor(conversationType)}
timestamp={item.created.split(' ')[0]}
rightBar={
<div>{listRightBottom(item, conversationType)}</div>
}
onClick={() => {
setLocalValue(`${conversationType}_conversationId`, item.uid);
setConversationId(item.uid);
}}
></SideListItem>
);
})
}
</SideList>
</div>
)
}
详细的代码可以参见: github.com/rrkeji/rrai…
添加会话
页面的代码可以参见: github.com/rrkeji/rrai…
会话
页面的代码可以参见: github.com/rrkeji/rrai…
页面的一些组件使用antd
这里数据只是存在单机上,后续实现数据存到云OR链上.