@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
253 lines (201 loc) • 7.21 kB
text/typescript
import { and, desc, eq, inArray } from 'drizzle-orm';
import {
ChatGroupAgentItem,
ChatGroupItem,
NewChatGroup,
NewChatGroupAgent,
chatGroups,
chatGroupsAgents,
} from '../schemas';
import { LobeChatDatabase } from '../type';
export class ChatGroupModel {
private userId: string;
private db: LobeChatDatabase;
constructor(db: LobeChatDatabase, userId: string) {
this.userId = userId;
this.db = db;
}
// ******* Query Methods ******* //
async findById(id: string): Promise<ChatGroupItem | undefined> {
const item = await this.db.query.chatGroups.findFirst({
where: and(eq(chatGroups.id, id), eq(chatGroups.userId, this.userId)),
});
return item;
}
async query(): Promise<ChatGroupItem[]> {
return this.db.query.chatGroups.findMany({
orderBy: [desc(chatGroups.updatedAt)],
where: eq(chatGroups.userId, this.userId),
});
}
async queryWithMemberDetails(): Promise<any[]> {
const groups = await this.query();
if (groups.length === 0) return [];
const groupIds = groups.map((g) => g.id);
const groupAgents = await this.db.query.chatGroupsAgents.findMany({
where: inArray(chatGroupsAgents.chatGroupId, groupIds),
with: { agent: true },
});
const groupAgentMap = new Map<string, any[]>();
for (const groupAgent of groupAgents) {
if (!groupAgent.agent) continue;
const groupList = groupAgentMap.get(groupAgent.chatGroupId) || [];
groupList.push(groupAgent.agent);
groupAgentMap.set(groupAgent.chatGroupId, groupList);
}
return groups.map((group) => ({
...group,
members: groupAgentMap.get(group.id) || [],
}));
}
async findGroupWithAgents(groupId: string): Promise<{
agents: ChatGroupAgentItem[];
group: ChatGroupItem;
} | null> {
const group = await this.findById(groupId);
if (!group) return null;
const agents = await this.db.query.chatGroupsAgents.findMany({
orderBy: [chatGroupsAgents.order],
where: eq(chatGroupsAgents.chatGroupId, groupId),
});
return { agents, group };
}
// ******* Create Methods ******* //
async create(params: Omit<NewChatGroup, 'userId'>): Promise<ChatGroupItem> {
const [result] = await this.db
.insert(chatGroups)
.values({ ...params, userId: this.userId })
.returning();
return result;
}
async createWithAgents(
groupParams: Omit<NewChatGroup, 'userId'>,
agentIds: string[],
): Promise<{ agents: NewChatGroupAgent[]; group: ChatGroupItem }> {
const group = await this.create(groupParams);
if (agentIds.length === 0) {
return { agents: [], group };
}
const agentParams: NewChatGroupAgent[] = agentIds.map((agentId, index) => ({
agentId,
chatGroupId: group.id,
order: index,
role: 'assistant',
userId: this.userId,
}));
const agents = await this.db.insert(chatGroupsAgents).values(agentParams).returning();
return { agents, group };
}
// ******* Update Methods ******* //
async update(id: string, value: Partial<ChatGroupItem>): Promise<ChatGroupItem> {
const [result] = await this.db
.update(chatGroups)
.set({ ...value, updatedAt: new Date() })
.where(and(eq(chatGroups.id, id), eq(chatGroups.userId, this.userId)))
.returning();
if (!result) {
throw new Error('Chat group not found or access denied');
}
return result;
}
async addAgentToGroup(
groupId: string,
agentId: string,
options?: { order?: number; role?: string },
): Promise<NewChatGroupAgent> {
const params: NewChatGroupAgent = {
agentId,
chatGroupId: groupId,
order: options?.order || 0,
role: options?.role || 'assistant',
userId: this.userId,
};
const [result] = await this.db.insert(chatGroupsAgents).values(params).returning();
return result;
}
async addAgentsToGroup(groupId: string, agentIds: string[]): Promise<ChatGroupAgentItem[]> {
const group = await this.findById(groupId);
if (!group) throw new Error('Group not found');
const existingAgents = await this.getGroupAgents(groupId);
const existingAgentIds = new Set(existingAgents.map((a) => a.id));
const newAgentIds = agentIds.filter((id) => !existingAgentIds.has(id));
if (newAgentIds.length === 0) {
return [];
}
const newAgents: NewChatGroupAgent[] = newAgentIds.map((agentId) => ({
agentId,
chatGroupId: groupId,
enabled: true,
userId: this.userId,
}));
return this.db.insert(chatGroupsAgents).values(newAgents).returning();
}
async removeAgentFromGroup(groupId: string, agentId: string): Promise<void> {
await this.db
.delete(chatGroupsAgents)
.where(and(eq(chatGroupsAgents.chatGroupId, groupId), eq(chatGroupsAgents.agentId, agentId)));
}
async updateAgentInGroup(
groupId: string,
agentId: string,
updates: Partial<Pick<NewChatGroupAgent, 'order' | 'role'>>,
): Promise<NewChatGroupAgent> {
const [result] = await this.db
.update(chatGroupsAgents)
.set({ ...updates, updatedAt: new Date() })
.where(and(eq(chatGroupsAgents.chatGroupId, groupId), eq(chatGroupsAgents.agentId, agentId)))
.returning();
return result;
}
// ******* Delete Methods ******* //
async delete(id: string): Promise<ChatGroupItem> {
// Agents are automatically deleted due to CASCADE constraint
const [result] = await this.db
.delete(chatGroups)
.where(and(eq(chatGroups.id, id), eq(chatGroups.userId, this.userId)))
.returning();
if (!result) {
throw new Error('Chat group not found or access denied');
}
return result;
}
async deleteAll(): Promise<void> {
await this.db.delete(chatGroups).where(eq(chatGroups.userId, this.userId));
}
// ******* Agent Query Methods ******* //
async getGroupAgents(groupId: string): Promise<ChatGroupAgentItem[]> {
return this.db.query.chatGroupsAgents.findMany({
orderBy: [chatGroupsAgents.order],
where: eq(chatGroupsAgents.chatGroupId, groupId),
});
}
async getEnabledGroupAgents(groupId: string): Promise<ChatGroupAgentItem[]> {
return this.db.query.chatGroupsAgents.findMany({
orderBy: [chatGroupsAgents.order],
where: and(eq(chatGroupsAgents.chatGroupId, groupId), eq(chatGroupsAgents.enabled, true)),
});
}
async getGroupsWithAgents(agentIds?: string[]): Promise<ChatGroupItem[]> {
if (!agentIds || agentIds.length === 0) {
return this.query();
}
// Find groups containing any of the specified agents
const groupIds = await this.db
.selectDistinct({ chatGroupId: chatGroupsAgents.chatGroupId })
.from(chatGroupsAgents)
.where(
and(eq(chatGroupsAgents.userId, this.userId), inArray(chatGroupsAgents.agentId, agentIds)),
);
if (groupIds.length === 0) return [];
return this.db.query.chatGroups.findMany({
orderBy: [desc(chatGroups.updatedAt)],
where: and(
inArray(
chatGroups.id,
groupIds.map((g) => g.chatGroupId),
),
eq(chatGroups.userId, this.userId),
),
});
}
}