feat(admin): LLM config, knowledge base, and WeCom AI binding
CI / verify (pull_request) Has been cancelled
CI / verify (pull_request) Has been cancelled
Add owner-scoped model API settings, uploadable knowledge bases, and WeCom bot AI/KB wiring with OpenAI-compatible URL normalization. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,6 +21,26 @@ function isWecomRole(v: string): v is WecomBotRole {
|
||||
return (WECOM_BOT_ROLES as readonly string[]).includes(v);
|
||||
}
|
||||
|
||||
type WecomRow = {
|
||||
id: bigint;
|
||||
name: string;
|
||||
role: string;
|
||||
botId: string;
|
||||
secret: string;
|
||||
avatarUrl: string | null;
|
||||
welcome: string | null;
|
||||
permissions: string;
|
||||
aiEnabled: boolean;
|
||||
llmConfigId: bigint | null;
|
||||
knowledgeBaseId: bigint | null;
|
||||
enabled: boolean;
|
||||
sortOrder: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
llmConfig?: { id: bigint; name: string } | null;
|
||||
knowledgeBase?: { id: bigint; name: string } | null;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AdminWecomBotsService {
|
||||
constructor(
|
||||
@@ -48,6 +68,10 @@ export class AdminWecomBotsService {
|
||||
orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
include: {
|
||||
llmConfig: { select: { id: true, name: true } },
|
||||
knowledgeBase: { select: { id: true, name: true } },
|
||||
},
|
||||
}),
|
||||
this.prisma.wecomBot.count({ where }),
|
||||
]);
|
||||
@@ -62,7 +86,13 @@ export class AdminWecomBotsService {
|
||||
}
|
||||
|
||||
async detail(id: bigint) {
|
||||
const row = await this.prisma.wecomBot.findUnique({ where: { id } });
|
||||
const row = await this.prisma.wecomBot.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
llmConfig: { select: { id: true, name: true } },
|
||||
knowledgeBase: { select: { id: true, name: true } },
|
||||
},
|
||||
});
|
||||
if (!row) throw new NotFoundException('机器人不存在');
|
||||
return this.toDto(row);
|
||||
}
|
||||
@@ -79,6 +109,8 @@ export class AdminWecomBotsService {
|
||||
const exists = await this.prisma.wecomBot.findUnique({ where: { botId } });
|
||||
if (exists) throw new BadRequestException('BotID 已存在');
|
||||
|
||||
const llmConfigId = await this.resolveLlmId(dto.llmConfigId);
|
||||
const knowledgeBaseId = await this.resolveKbId(dto.knowledgeBaseId);
|
||||
const permissions = resolveWecomBotPermissions(dto.role, dto.permissions);
|
||||
const row = await this.prisma.wecomBot.create({
|
||||
data: {
|
||||
@@ -89,9 +121,16 @@ export class AdminWecomBotsService {
|
||||
avatarUrl: dto.avatarUrl?.trim() || null,
|
||||
welcome: dto.welcome?.trim() || null,
|
||||
permissions: JSON.stringify(permissions),
|
||||
aiEnabled: dto.aiEnabled === true,
|
||||
llmConfigId,
|
||||
knowledgeBaseId,
|
||||
enabled: dto.enabled !== false,
|
||||
sortOrder: dto.sortOrder ?? 0,
|
||||
},
|
||||
include: {
|
||||
llmConfig: { select: { id: true, name: true } },
|
||||
knowledgeBase: { select: { id: true, name: true } },
|
||||
},
|
||||
});
|
||||
await this.wecomAibot.reload('bot-create');
|
||||
return this.toDto(row);
|
||||
@@ -132,6 +171,11 @@ export class AdminWecomBotsService {
|
||||
const secret =
|
||||
dto.secret !== undefined && dto.secret.trim() ? dto.secret.trim() : existing.secret;
|
||||
|
||||
const llmConfigId =
|
||||
dto.llmConfigId !== undefined ? await this.resolveLlmId(dto.llmConfigId) : undefined;
|
||||
const knowledgeBaseId =
|
||||
dto.knowledgeBaseId !== undefined ? await this.resolveKbId(dto.knowledgeBaseId) : undefined;
|
||||
|
||||
const row = await this.prisma.wecomBot.update({
|
||||
where: { id },
|
||||
data: {
|
||||
@@ -143,9 +187,16 @@ export class AdminWecomBotsService {
|
||||
dto.avatarUrl === undefined ? undefined : dto.avatarUrl?.trim() || null,
|
||||
welcome: dto.welcome === undefined ? undefined : dto.welcome?.trim() || null,
|
||||
permissions: permissionsJson,
|
||||
aiEnabled: dto.aiEnabled,
|
||||
llmConfigId,
|
||||
knowledgeBaseId,
|
||||
enabled: dto.enabled,
|
||||
sortOrder: dto.sortOrder,
|
||||
},
|
||||
include: {
|
||||
llmConfig: { select: { id: true, name: true } },
|
||||
knowledgeBase: { select: { id: true, name: true } },
|
||||
},
|
||||
});
|
||||
await this.wecomAibot.reload('bot-update');
|
||||
return this.toDto(row);
|
||||
@@ -163,20 +214,25 @@ export class AdminWecomBotsService {
|
||||
return this.wecomAibot.reload('manual');
|
||||
}
|
||||
|
||||
private toDto(row: {
|
||||
id: bigint;
|
||||
name: string;
|
||||
role: string;
|
||||
botId: string;
|
||||
secret: string;
|
||||
avatarUrl: string | null;
|
||||
welcome: string | null;
|
||||
permissions: string;
|
||||
enabled: boolean;
|
||||
sortOrder: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}): WecomBotDto {
|
||||
private async resolveLlmId(raw?: string | null): Promise<bigint | null> {
|
||||
if (raw === undefined) return null;
|
||||
if (raw === null || raw === '') return null;
|
||||
const id = BigInt(raw);
|
||||
const row = await this.prisma.llmApiConfig.findUnique({ where: { id } });
|
||||
if (!row) throw new BadRequestException('语言模型配置不存在');
|
||||
return id;
|
||||
}
|
||||
|
||||
private async resolveKbId(raw?: string | null): Promise<bigint | null> {
|
||||
if (raw === undefined) return null;
|
||||
if (raw === null || raw === '') return null;
|
||||
const id = BigInt(raw);
|
||||
const row = await this.prisma.knowledgeBase.findUnique({ where: { id } });
|
||||
if (!row) throw new BadRequestException('知识库不存在');
|
||||
return id;
|
||||
}
|
||||
|
||||
private toDto(row: WecomRow): WecomBotDto {
|
||||
const role = (isWecomRole(row.role) ? row.role : 'CUSTOM') as WecomBotRole;
|
||||
const permissions = resolveWecomBotPermissions(role, row.permissions) as WecomBotPermission[];
|
||||
return {
|
||||
@@ -188,6 +244,11 @@ export class AdminWecomBotsService {
|
||||
avatarUrl: row.avatarUrl,
|
||||
welcome: row.welcome,
|
||||
permissions,
|
||||
aiEnabled: row.aiEnabled,
|
||||
llmConfigId: row.llmConfigId?.toString() ?? null,
|
||||
llmConfigName: row.llmConfig?.name ?? null,
|
||||
knowledgeBaseId: row.knowledgeBaseId?.toString() ?? null,
|
||||
knowledgeBaseName: row.knowledgeBase?.name ?? null,
|
||||
enabled: row.enabled,
|
||||
sortOrder: row.sortOrder,
|
||||
createdAt: row.createdAt.toISOString(),
|
||||
|
||||
Reference in New Issue
Block a user