Tauri项目中实现ChatGPT对话,软软AI中实现ChatGPT接口接入

181 阅读3分钟

Tauri项目中实现ChatGPT对话,软软AI中实现ChatGPT接口接入

需求简述

  • 在tauri的项目中增加与Chat机器人进行会话的功能
  • 可以选择Chat机器人的模型
  • 可以实现多会话的功能
  • 支持会话的设置,可以进行会话名称修改、删除会话、清空等
  • ......等等

先展示下完成之后的截图

整体截图

1683792331069.jpg

添加会话

1683792355135.jpg

会话设置

1683792754877.jpg

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链上.