* modify chat table & migrate * refactor layout * update layout * actions for chats & agents * refactor chat form * refactor chat form * update chat form * rename * refactor types & locales * refactor tts engine * refactor * fix config * refactor chat form * refactor chat member form * fixing * refactor ask agent * chat in conversation * fix chat message update * may update chat member * update chat member from message * refacto group propmt * chat member gpt settings * update ui * more config for chat * add locales * update chat agent form * add locales for agent form * update UI * auto reply for text input * update chat * update chat input * rename colomns * update chat * udpate agent message * add chat member * add/remove chat member * fix chat member * refactor * auto update chat name * fix chat update * refactor chat column * fix chat * add agent loading * use fresh new prompt when ask agent * add chat forwarder * refactor chat * fix * add copilot * toggle copilot * add copilot chat * avoid open the same chat at the same time * update copilot header * add agent introduction in the first place of chat * rename column * update style * refactor * invoke all agents in group after asking * chat sidebar collopse * may select chat in copilot * update ui * new chat from agent * upgrade deps * refactor chat & chatAgent * add limit for chat member create * update chat form * update db & migration * fix up * fix group chat * fix panel warning * display chat agent type * tts message * fit tts agent * refactor * chat fowarder * update UI * setup default values for tts agent * fix chat member add/remove * edit tts agent * display chat date * Fix UI * add system message * refactor * fix hook * refactor * touch chat&agent when new message created * fix auto reply * migrate conversation to chat * add migrate api * fix migrate * update migrate * refactor * refactor * refactor * fix delete agent * add hotkey for copilot * fix bugs * upgrade deps * refactor tts hook * stop auto playback when azure transcribed * refactor * clean up * fix UI * fix conversation migrate * handle error * update model * declare types * audo backup db file when started * fix db backup * refactor db migration * fix UI * refactor * fix chat auto update name * fix authorization lost when hot reload * refactor * refactor * fix tts form * keep agent avatar up to date * clean code
278 lines
7.0 KiB
TypeScript
278 lines
7.0 KiB
TypeScript
import {
|
|
AfterCreate,
|
|
AfterDestroy,
|
|
BeforeDestroy,
|
|
Table,
|
|
Column,
|
|
Default,
|
|
IsUUID,
|
|
Model,
|
|
HasMany,
|
|
DataType,
|
|
AllowNull,
|
|
BeforeSave,
|
|
} from "sequelize-typescript";
|
|
import {
|
|
Chat,
|
|
ChatAgent,
|
|
ChatMember,
|
|
ChatMessage,
|
|
Message,
|
|
Speech,
|
|
UserSetting,
|
|
} from "@main/db/models";
|
|
import mainWindow from "@main/window";
|
|
import log from "@main/logger";
|
|
import { t } from "i18next";
|
|
import { SttEngineOptionEnum, UserSettingKeyEnum } from "@/types/enums";
|
|
import { DEFAULT_GPT_CONFIG } from "@/constants";
|
|
|
|
const logger = log.scope("db/models/conversation");
|
|
@Table({
|
|
modelName: "Conversation",
|
|
tableName: "conversations",
|
|
underscored: true,
|
|
timestamps: true,
|
|
})
|
|
export class Conversation extends Model<Conversation> {
|
|
@IsUUID(4)
|
|
@Default(DataType.UUIDV4)
|
|
@Column({ primaryKey: true, type: DataType.UUID })
|
|
id: string;
|
|
|
|
@AllowNull(false)
|
|
@Column(DataType.STRING)
|
|
name: string;
|
|
|
|
@AllowNull(false)
|
|
@Column(DataType.ENUM("openai", "ollama", "google-generative-ai"))
|
|
engine: string;
|
|
|
|
@AllowNull(false)
|
|
@Column(DataType.JSON)
|
|
configuration: {
|
|
model: string;
|
|
type: "gpt" | "tts";
|
|
roleDefinition?: string;
|
|
temperature?: number;
|
|
maxTokens?: number;
|
|
} & { [key: string]: any };
|
|
|
|
@Column(DataType.VIRTUAL)
|
|
get type(): "gpt" | "tts" {
|
|
return this.getDataValue("configuration").type || "gpt";
|
|
}
|
|
|
|
@Column(DataType.VIRTUAL)
|
|
get model(): string {
|
|
return this.getDataValue("configuration").model;
|
|
}
|
|
|
|
@Column(DataType.VIRTUAL)
|
|
get roleDefinition(): string {
|
|
return this.getDataValue("configuration").roleDefinition;
|
|
}
|
|
|
|
@Column(DataType.VIRTUAL)
|
|
get language(): string {
|
|
return this.getDataValue("configuration").tts?.language;
|
|
}
|
|
|
|
@HasMany(() => Message)
|
|
messages: Message[];
|
|
|
|
async migrateToChat() {
|
|
const source = `conversations://${this.id}`;
|
|
let agent = await ChatAgent.findOne({
|
|
where: {
|
|
source,
|
|
},
|
|
});
|
|
|
|
if (agent) return;
|
|
|
|
const gpt = {
|
|
engine: this.engine,
|
|
model: this.configuration.model,
|
|
temperature: this.configuration.temperature,
|
|
maxCompletionTokens: this.configuration.maxTokens,
|
|
presencePenalty: this.configuration.presencePenalty,
|
|
frequencyPenalty: this.configuration.frequencyPenalty,
|
|
historyBufferSize: this.configuration.historyBufferSize,
|
|
numberOfChoices: this.configuration.numberOfChoices,
|
|
};
|
|
|
|
if (!["openai", "enjoyai"].includes(this.engine)) {
|
|
const defaultGptEngine = await UserSetting.get(
|
|
UserSettingKeyEnum.GPT_ENGINE
|
|
);
|
|
gpt.engine = defaultGptEngine?.name || DEFAULT_GPT_CONFIG.engine;
|
|
gpt.model = defaultGptEngine?.models?.default || DEFAULT_GPT_CONFIG.model;
|
|
}
|
|
|
|
const tts = {
|
|
engine: this.configuration.tts?.engine || "enjoyai",
|
|
model: this.configuration.tts?.model || "openai/tts-1",
|
|
language: this.language,
|
|
voice: this.configuration.tts?.voice || "alloy",
|
|
};
|
|
|
|
agent = await ChatAgent.create({
|
|
name:
|
|
this.configuration.type === "tts" ? tts.voice || this.name : this.name,
|
|
type: this.configuration.type === "tts" ? "TTS" : "GPT",
|
|
source,
|
|
description: "",
|
|
config:
|
|
this.configuration.type === "tts"
|
|
? {
|
|
tts,
|
|
}
|
|
: {
|
|
prompt: this.configuration.roleDefinition,
|
|
},
|
|
});
|
|
|
|
const transaction = await Conversation.sequelize.transaction();
|
|
|
|
try {
|
|
const chat = await Chat.create(
|
|
{
|
|
name: t("newChat"),
|
|
type: this.type === "tts" ? "TTS" : "CONVERSATION",
|
|
config: {
|
|
stt: SttEngineOptionEnum.ENJOY_AZURE,
|
|
},
|
|
},
|
|
{
|
|
transaction,
|
|
}
|
|
);
|
|
const chatMember = await ChatMember.create(
|
|
{
|
|
chatId: chat.id,
|
|
userId: agent.id,
|
|
userType: "ChatAgent",
|
|
config:
|
|
this.configuration.type === "tts"
|
|
? {
|
|
tts,
|
|
}
|
|
: {
|
|
gpt,
|
|
tts,
|
|
},
|
|
},
|
|
{
|
|
transaction,
|
|
hooks: false,
|
|
}
|
|
);
|
|
|
|
const messages = await Message.findAll({
|
|
where: {
|
|
conversationId: this.id,
|
|
},
|
|
include: [
|
|
{
|
|
association: "speeches",
|
|
model: Speech,
|
|
where: { sourceType: "Message" },
|
|
required: false,
|
|
},
|
|
],
|
|
order: [["createdAt", "ASC"]],
|
|
});
|
|
|
|
for (const message of messages) {
|
|
const chatMessage = await ChatMessage.create(
|
|
{
|
|
chatId: chat.id,
|
|
content: message.content,
|
|
role: message.role === "user" ? "USER" : "AGENT",
|
|
state: "completed",
|
|
memberId: message.role === "assistant" ? chatMember.id : null,
|
|
agentId: message.role === "assistant" ? agent.id : null,
|
|
createdAt: message.createdAt,
|
|
updatedAt: message.updatedAt,
|
|
},
|
|
{
|
|
transaction,
|
|
hooks: false,
|
|
}
|
|
);
|
|
if (chat.type === "TTS") {
|
|
for (const speech of message.speeches) {
|
|
await speech.update(
|
|
{
|
|
sourceId: chatMessage.id,
|
|
sourceType: "ChatMessage",
|
|
},
|
|
{ transaction }
|
|
);
|
|
}
|
|
}
|
|
}
|
|
await transaction.commit();
|
|
} catch (error) {
|
|
await transaction.rollback();
|
|
logger.error(error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
@BeforeSave
|
|
static validateConfiguration(conversation: Conversation) {
|
|
if (conversation.type === "tts") {
|
|
if (!conversation.configuration.tts) {
|
|
throw new Error(t("models.conversation.ttsConfigurationIsRequired"));
|
|
}
|
|
if (!conversation.configuration.tts.engine) {
|
|
throw new Error(t("models.conversation.ttsEngineIsRequired"));
|
|
}
|
|
if (!conversation.configuration.tts.model) {
|
|
throw new Error("models.conversation.ttsModelIsRequired");
|
|
}
|
|
|
|
conversation.engine = conversation.configuration.tts.engine;
|
|
conversation.configuration.model = conversation.configuration.tts.engine;
|
|
}
|
|
|
|
if (!conversation.configuration.model)
|
|
throw new Error(t("models.conversation.modelIsRequired"));
|
|
}
|
|
|
|
@AfterCreate
|
|
static notifyForCreate(conversation: Conversation) {
|
|
this.notify(conversation, "create");
|
|
}
|
|
|
|
@BeforeDestroy
|
|
static destroyAllMessages(conversation: Conversation) {
|
|
Message.findAll({ where: { conversationId: conversation.id } }).then(
|
|
(messages) => {
|
|
messages.forEach((message) => message.destroy());
|
|
}
|
|
);
|
|
}
|
|
|
|
@AfterDestroy
|
|
static notifyForDestroy(conversation: Conversation) {
|
|
this.notify(conversation, "destroy");
|
|
}
|
|
|
|
static notify(
|
|
conversation: Conversation,
|
|
action: "create" | "update" | "destroy"
|
|
) {
|
|
if (!mainWindow.win) return;
|
|
|
|
mainWindow.win.webContents.send("db-on-transaction", {
|
|
model: "Conversation",
|
|
id: conversation.id,
|
|
action: action,
|
|
record: conversation.toJSON(),
|
|
});
|
|
}
|
|
}
|