feat: refactor copilot module (#14537)

This commit is contained in:
DarkSky
2026-03-02 13:57:55 +08:00
committed by GitHub
parent 60acd81d4b
commit c5d622531c
92 changed files with 5759 additions and 2170 deletions
@@ -4,7 +4,6 @@ import { BadRequestException, NotFoundException } from '@nestjs/common';
import {
Args,
Field,
Float,
ID,
InputType,
Mutation,
@@ -15,7 +14,6 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import { AiPromptRole } from '@prisma/client';
import { GraphQLJSON, SafeIntResolver } from 'graphql-scalars';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
@@ -313,57 +311,6 @@ class CopilotQuotaType {
used!: number;
}
registerEnumType(AiPromptRole, {
name: 'CopilotPromptMessageRole',
});
@InputType('CopilotPromptConfigInput')
@ObjectType()
class CopilotPromptConfigType {
@Field(() => Float, { nullable: true })
frequencyPenalty!: number | null;
@Field(() => Float, { nullable: true })
presencePenalty!: number | null;
@Field(() => Float, { nullable: true })
temperature!: number | null;
@Field(() => Float, { nullable: true })
topP!: number | null;
}
@InputType('CopilotPromptMessageInput')
@ObjectType()
class CopilotPromptMessageType {
@Field(() => AiPromptRole)
role!: AiPromptRole;
@Field(() => String)
content!: string;
@Field(() => GraphQLJSON, { nullable: true })
params!: Record<string, string> | null;
}
@ObjectType()
class CopilotPromptType {
@Field(() => String)
name!: string;
@Field(() => String)
model!: string;
@Field(() => String, { nullable: true })
action!: string | null;
@Field(() => CopilotPromptConfigType, { nullable: true })
config!: CopilotPromptConfigType | null;
@Field(() => [CopilotPromptMessageType])
messages!: CopilotPromptMessageType[];
}
@ObjectType()
class CopilotModelType {
@Field(() => String)
@@ -638,13 +585,8 @@ export class CopilotResolver {
);
}
@Mutation(() => String, {
description: 'Create a chat session',
})
@CallMetric('ai', 'chat_session_create')
async createCopilotSession(
@CurrentUser() user: CurrentUser,
@Args({ name: 'options', type: () => CreateChatSessionInput })
private async createCopilotSessionInternal(
user: CurrentUser,
options: CreateChatSessionInput
): Promise<string> {
// permission check based on session type
@@ -666,6 +608,42 @@ export class CopilotResolver {
});
}
@Mutation(() => String, {
description: 'Create a chat session',
deprecationReason: 'use `createCopilotSessionWithHistory` instead',
})
@CallMetric('ai', 'chat_session_create')
async createCopilotSession(
@CurrentUser() user: CurrentUser,
@Args({ name: 'options', type: () => CreateChatSessionInput })
options: CreateChatSessionInput
): Promise<string> {
return await this.createCopilotSessionInternal(user, options);
}
@Mutation(() => CopilotHistoriesType, {
description: 'Create a chat session and return full session payload',
})
@CallMetric('ai', 'chat_session_create_with_history')
async createCopilotSessionWithHistory(
@CurrentUser() user: CurrentUser,
@Args({ name: 'options', type: () => CreateChatSessionInput })
options: CreateChatSessionInput
): Promise<CopilotHistoriesType> {
const sessionId = await this.createCopilotSessionInternal(user, options);
const session = await this.chatSession.getSessionInfo(sessionId);
if (!session) {
throw new NotFoundException('Session not found');
}
return {
...session,
messages: session.messages.map(message => ({
...message,
id: message.id,
})) as ChatMessageType[],
};
}
@Mutation(() => String, {
description: 'Update a chat session',
})
@@ -939,31 +917,10 @@ export class UserCopilotResolver {
}
}
@InputType()
class CreateCopilotPromptInput {
@Field(() => String)
name!: string;
@Field(() => String)
model!: string;
@Field(() => String, { nullable: true })
action!: string | null;
@Field(() => CopilotPromptConfigType, { nullable: true })
config!: CopilotPromptConfigType | null;
@Field(() => [CopilotPromptMessageType])
messages!: CopilotPromptMessageType[];
}
@Admin()
@Resolver(() => String)
export class PromptsManagementResolver {
constructor(
private readonly cron: CopilotCronJobs,
private readonly promptService: PromptService
) {}
constructor(private readonly cron: CopilotCronJobs) {}
@Mutation(() => Boolean, {
description: 'Trigger generate missing titles cron job',
@@ -980,48 +937,4 @@ export class PromptsManagementResolver {
await this.cron.triggerCleanupTrashedDocEmbeddings();
return true;
}
@Query(() => [CopilotPromptType], {
description: 'List all copilot prompts',
})
async listCopilotPrompts() {
const prompts = await this.promptService.list();
return prompts.filter(
p =>
p.messages.length > 0 &&
// ignore internal prompts
!p.name.startsWith('workflow:') &&
!p.name.startsWith('debug:') &&
!p.name.startsWith('chat:') &&
!p.name.startsWith('action:')
);
}
@Mutation(() => CopilotPromptType, {
description: 'Create a copilot prompt',
})
async createCopilotPrompt(
@Args({ type: () => CreateCopilotPromptInput, name: 'input' })
input: CreateCopilotPromptInput
) {
await this.promptService.set(
input.name,
input.model,
input.messages,
input.config
);
return this.promptService.get(input.name);
}
@Mutation(() => CopilotPromptType, {
description: 'Update a copilot prompt',
})
async updateCopilotPrompt(
@Args('name') name: string,
@Args('messages', { type: () => [CopilotPromptMessageType] })
messages: CopilotPromptMessageType[]
) {
await this.promptService.update(name, { messages, modified: true });
return this.promptService.get(name);
}
}