mirror of
https://github.com/idootop/mi-gpt.git
synced 2025-04-08 19:47:10 +00:00
misc: only save session info to local file
This commit is contained in:
parent
dc8324610e
commit
8606cf47d5
|
@ -1,57 +1,113 @@
|
|||
import { Room, User } from "@prisma/client";
|
||||
import { readJSON, writeJSON } from "../../utils/io";
|
||||
import { deepClone, removeEmpty } from "../../utils/base";
|
||||
import { UserCRUD } from "../db/user";
|
||||
import { RoomCRUD, getRoomID } from "../db/room";
|
||||
import { DeepPartial } from "../../utils/type";
|
||||
import { deepClone } from "../../utils/base";
|
||||
import { diff } from "../../utils/diff";
|
||||
|
||||
export type IBotConfig = DeepPartial<{
|
||||
const kDefaultMaster = {
|
||||
name: "用户",
|
||||
profile: "",
|
||||
};
|
||||
|
||||
const kDefaultBot = {
|
||||
name: "小爱同学",
|
||||
profile: "",
|
||||
};
|
||||
|
||||
interface IBotIndex {
|
||||
botId: string;
|
||||
masterId: string;
|
||||
}
|
||||
|
||||
export interface IBotConfig {
|
||||
bot: User;
|
||||
master: User;
|
||||
room: Room;
|
||||
}>;
|
||||
}
|
||||
|
||||
class _BotConfig {
|
||||
config?: IBotConfig;
|
||||
private botIndex?: IBotIndex;
|
||||
|
||||
private _config_path = ".bot.json";
|
||||
private _index_path = ".bot.json";
|
||||
|
||||
async get() {
|
||||
if (!this.config) {
|
||||
this.config = await readJSON(this._config_path);
|
||||
private async _getIndex(): Promise<IBotIndex | undefined> {
|
||||
if (!this.botIndex) {
|
||||
this.botIndex = await readJSON(this._index_path);
|
||||
}
|
||||
return this.config;
|
||||
return this.botIndex;
|
||||
}
|
||||
|
||||
async update(config: IBotConfig) {
|
||||
let currentConfig: any = await this.get();
|
||||
const oldConfig = deepClone(currentConfig ?? {});
|
||||
if (!currentConfig) {
|
||||
currentConfig = {
|
||||
master: {
|
||||
name: "用户",
|
||||
profile: "",
|
||||
},
|
||||
bot: {
|
||||
name: "小爱同学",
|
||||
profile: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
for (const key of ["bot", "master", "room"]) {
|
||||
currentConfig[key] = {
|
||||
...currentConfig[key],
|
||||
...(config as any)[key],
|
||||
};
|
||||
}
|
||||
const diffs = diff(currentConfig, oldConfig);
|
||||
const diffKeys = diffs.map((e) => e.path[0]);
|
||||
if (diffKeys.length > 0) {
|
||||
const success = await writeJSON(this._config_path, currentConfig);
|
||||
if (success) {
|
||||
return { config: currentConfig, diffs: diffKeys };
|
||||
async get(): Promise<IBotConfig | undefined> {
|
||||
const index = await this._getIndex();
|
||||
if (!index) {
|
||||
// create db records
|
||||
const bot = await UserCRUD.addOrUpdate(kDefaultBot);
|
||||
if (!bot) {
|
||||
console.error("❌ create bot failed");
|
||||
return undefined;
|
||||
}
|
||||
const master = await UserCRUD.addOrUpdate(kDefaultMaster);
|
||||
if (!master) {
|
||||
console.error("❌ create master failed");
|
||||
return undefined;
|
||||
}
|
||||
const defaultRoomName = `${master.name}和${bot.name}的私聊`;
|
||||
const room = await RoomCRUD.addOrUpdate({
|
||||
id: getRoomID([bot, master]),
|
||||
name: defaultRoomName,
|
||||
description: defaultRoomName,
|
||||
});
|
||||
if (!room) {
|
||||
console.error("❌ create room failed");
|
||||
return undefined;
|
||||
}
|
||||
this.botIndex = {
|
||||
botId: bot.id,
|
||||
masterId: master.id,
|
||||
};
|
||||
await writeJSON(this._index_path, this.botIndex);
|
||||
}
|
||||
return { config: oldConfig };
|
||||
const bot = await UserCRUD.get(this.botIndex!.botId);
|
||||
if (!bot) {
|
||||
console.error("❌ find bot failed");
|
||||
return undefined;
|
||||
}
|
||||
const master = await UserCRUD.get(this.botIndex!.masterId);
|
||||
if (!master) {
|
||||
console.error("❌ find master failed");
|
||||
return undefined;
|
||||
}
|
||||
const room = await RoomCRUD.get(getRoomID([bot, master]));
|
||||
if (!room) {
|
||||
console.error("❌ find room failed");
|
||||
return undefined;
|
||||
}
|
||||
return { bot, master, room };
|
||||
}
|
||||
|
||||
async update(
|
||||
config: DeepPartial<IBotConfig>
|
||||
): Promise<IBotConfig | undefined> {
|
||||
let currentConfig = await this.get();
|
||||
if (!currentConfig) {
|
||||
return undefined;
|
||||
}
|
||||
const oldConfig = deepClone(currentConfig);
|
||||
for (const key in currentConfig) {
|
||||
const _key = key as keyof IBotConfig;
|
||||
currentConfig[_key] = {
|
||||
...currentConfig[_key],
|
||||
...removeEmpty(config[_key]),
|
||||
updatedAt: undefined, // reset update date
|
||||
} as any;
|
||||
}
|
||||
let { bot, master, room } = currentConfig;
|
||||
bot = (await UserCRUD.addOrUpdate(currentConfig.bot)) ?? oldConfig.bot;
|
||||
master =
|
||||
(await UserCRUD.addOrUpdate(currentConfig.master)) ?? oldConfig.master;
|
||||
room = (await RoomCRUD.addOrUpdate(currentConfig.room)) ?? oldConfig.room;
|
||||
return { bot, master, room };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,39 +1,28 @@
|
|||
import { Message, Prisma, Room, User } from "@prisma/client";
|
||||
import { UserCRUD } from "../db/user";
|
||||
import { RoomCRUD, getRoomID } from "../db/room";
|
||||
import { Message, Prisma, User } from "@prisma/client";
|
||||
import { MemoryManager } from "./memory";
|
||||
import { MessageCRUD } from "../db/message";
|
||||
import { BotConfig, IBotConfig } from "./config";
|
||||
import { jsonEncode } from "../../utils/base";
|
||||
import { DeepPartial } from "../../utils/type";
|
||||
|
||||
export class ConversationManager {
|
||||
private config: IBotConfig;
|
||||
constructor(config: IBotConfig) {
|
||||
private config: DeepPartial<IBotConfig>;
|
||||
constructor(config: DeepPartial<IBotConfig>) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
async getMemory() {
|
||||
await this.loadOrUpdateConfig();
|
||||
if (!this.isReady) {
|
||||
return undefined;
|
||||
async get(): Promise<Partial<IBotConfig & { memory: MemoryManager }>> {
|
||||
const config = await this.update();
|
||||
if (!config) {
|
||||
return {};
|
||||
}
|
||||
return this.memory;
|
||||
return {
|
||||
...config,
|
||||
memory: new MemoryManager(config.room),
|
||||
};
|
||||
}
|
||||
|
||||
async getRoom() {
|
||||
const { room } = await this.loadOrUpdateConfig();
|
||||
if (!this.isReady) {
|
||||
return undefined;
|
||||
}
|
||||
return room as Room;
|
||||
}
|
||||
|
||||
async getUser(key: "bot" | "master") {
|
||||
const config = await this.loadOrUpdateConfig();
|
||||
if (!this.isReady) {
|
||||
return undefined;
|
||||
}
|
||||
return config[key] as User;
|
||||
async update(config?: DeepPartial<IBotConfig>) {
|
||||
return BotConfig.update(config ?? this.config);
|
||||
}
|
||||
|
||||
async getMessages(options?: {
|
||||
|
@ -47,44 +36,15 @@ export class ConversationManager {
|
|||
*/
|
||||
order?: "asc" | "desc";
|
||||
}) {
|
||||
const room = await this.getRoom();
|
||||
if (!this.isReady) {
|
||||
const { room } = await this.get();
|
||||
if (!room) {
|
||||
return [];
|
||||
}
|
||||
return MessageCRUD.gets({ room, ...options });
|
||||
}
|
||||
|
||||
async onMessage(message: Message) {
|
||||
const memory = await this.getMemory();
|
||||
const { memory } = await this.get();
|
||||
return memory?.addMessage2Memory(message);
|
||||
}
|
||||
|
||||
private memory?: MemoryManager;
|
||||
|
||||
get isReady() {
|
||||
return !!this.memory;
|
||||
}
|
||||
|
||||
async loadOrUpdateConfig() {
|
||||
const { config, diffs } = await BotConfig.update(this.config);
|
||||
if (!config.bot?.id || diffs?.includes("bot")) {
|
||||
config.bot = await UserCRUD.addOrUpdate(config.bot);
|
||||
}
|
||||
if (!config.master?.id || diffs?.includes("master")) {
|
||||
config.master = await UserCRUD.addOrUpdate(config.master);
|
||||
}
|
||||
if (!config.room?.id || diffs?.includes("room")) {
|
||||
const defaultRoomName = `${config.master.name}和${config.bot.name}的私聊`;
|
||||
config.room = await RoomCRUD.addOrUpdate({
|
||||
id: getRoomID([config.bot.id, config.master.id]),
|
||||
name: config.room?.name ?? defaultRoomName,
|
||||
description: config.room?.description ?? defaultRoomName,
|
||||
});
|
||||
}
|
||||
const { config: newConfig } = await BotConfig.update(config);
|
||||
if (!this.memory && config.bot && config.master && config.room) {
|
||||
this.memory = new MemoryManager(config.room);
|
||||
}
|
||||
return newConfig as IBotConfig;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,13 @@ class _LongTermMemoryCRUD {
|
|||
});
|
||||
}
|
||||
|
||||
async get(id: number) {
|
||||
return kPrisma.longTermMemory.findFirst({ where: { id } }).catch((e) => {
|
||||
console.error("❌ get long term memory failed", id, e);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async gets(options?: {
|
||||
room?: Room;
|
||||
owner?: User;
|
||||
|
|
|
@ -18,6 +18,13 @@ class _ShortTermMemoryCRUD {
|
|||
});
|
||||
}
|
||||
|
||||
async get(id: number) {
|
||||
return kPrisma.shortTermMemory.findFirst({ where: { id } }).catch((e) => {
|
||||
console.error("❌ get short term memory failed", id, e);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async gets(options?: {
|
||||
room?: Room;
|
||||
owner?: User;
|
||||
|
|
|
@ -18,6 +18,13 @@ class _MemoryCRUD {
|
|||
});
|
||||
}
|
||||
|
||||
async get(id: number) {
|
||||
return kPrisma.memory.findFirst({ where: { id } }).catch((e) => {
|
||||
console.error("❌ get memory failed", id, e);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async gets(options?: {
|
||||
room?: Room;
|
||||
owner?: User;
|
||||
|
|
|
@ -18,6 +18,13 @@ class _MessageCRUD {
|
|||
});
|
||||
}
|
||||
|
||||
async get(id: number) {
|
||||
return kPrisma.message.findFirst({ where: { id } }).catch((e) => {
|
||||
console.error("❌ get message failed", id, e);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async gets(options?: {
|
||||
room?: Room;
|
||||
sender?: User;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { Prisma, Room, User } from "@prisma/client";
|
||||
import { k404, kPrisma } from ".";
|
||||
|
||||
export function getRoomID(users: string[]) {
|
||||
return users.sort().join("_");
|
||||
export function getRoomID(users: User[]) {
|
||||
return users
|
||||
.map((e) => e.id)
|
||||
.sort()
|
||||
.join("_");
|
||||
}
|
||||
|
||||
class _RoomCRUD {
|
||||
|
@ -24,6 +27,13 @@ class _RoomCRUD {
|
|||
});
|
||||
}
|
||||
|
||||
async get(id: string) {
|
||||
return kPrisma.room.findFirst({ where: { id } }).catch((e) => {
|
||||
console.error("❌ get room failed", id, e);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async gets(options?: {
|
||||
user?: User;
|
||||
take?: number;
|
||||
|
|
|
@ -9,6 +9,13 @@ class _UserCRUD {
|
|||
});
|
||||
}
|
||||
|
||||
async get(id: string) {
|
||||
return kPrisma.user.findFirst({ where: { id } }).catch((e) => {
|
||||
console.error("❌ get user failed", id, e);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async gets(options?: {
|
||||
take?: number;
|
||||
skip?: number;
|
||||
|
@ -23,13 +30,14 @@ class _UserCRUD {
|
|||
take = 10,
|
||||
skip = 0,
|
||||
cursorId,
|
||||
include = { members: true },
|
||||
include = { rooms: true },
|
||||
order = "desc",
|
||||
} = options ?? {};
|
||||
const users = await kPrisma.user
|
||||
.findMany({
|
||||
take,
|
||||
skip,
|
||||
include,
|
||||
cursor: { id: cursorId },
|
||||
orderBy: { createdAt: order },
|
||||
})
|
||||
|
|
|
@ -110,6 +110,9 @@ export function withDefault<T = any>(e: any, defaultValue: T): T {
|
|||
}
|
||||
|
||||
export function removeEmpty<T = any>(data: T): T {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
return data.filter((e) => e != undefined) as any;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ export async function testDB() {
|
|||
description: "王黎的客厅,小爱同学放在角落里",
|
||||
},
|
||||
});
|
||||
const { room } = await manager.loadOrUpdateConfig();
|
||||
const { room } = await manager.get();
|
||||
assert(room, "❌ load config failed");
|
||||
println("✅ hello world!");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user