- A2uiSubmissionsService/DataSource/BusinessContextService 等构造参数 由 import type 改为值导入,确保 Nest 能解析依赖 - 验证:dev 模式后端正常启动,相关 243 个测试通过
198 lines
5.8 KiB
TypeScript
198 lines
5.8 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { DataSource, Repository } from 'typeorm';
|
|
import { AiConfigService } from '../ai-config/ai-config.service';
|
|
import { AgentToolExecutor } from '../agent-tools/agent-tool.executor';
|
|
import {
|
|
AgentToolContextFactory,
|
|
type AgentSkillDescriptor,
|
|
} from '../agent-tools/agent-tool.types';
|
|
import { AuthorizationService, CaslAbilityFactory, type AuthenticatedUser } from '../authorization';
|
|
import { AiAttachmentService } from './ai-attachment.service';
|
|
import { ImportsService } from '../imports/imports.service';
|
|
import { AiChartService } from './ai-chart.service';
|
|
import { AiExcelReaderService } from './ai-excel-reader.service';
|
|
import { AiFormService } from './ai-form.service';
|
|
import { AiReviewService } from './ai-review.service';
|
|
import { A2uiSubmissionsService } from './ai-a2ui-submissions.service';
|
|
import { AiModelStreamService } from './ai-model-stream.service';
|
|
import { OperationLogsService } from '../operation-logs/operation-logs.service';
|
|
import { AiConversation, AiMessage, AiToolRun } from './entities';
|
|
import type {
|
|
GenerationInput,
|
|
ModelContentPart,
|
|
ModelMessage,
|
|
} from './ai-chat.types';
|
|
import {
|
|
requireOwnedConversation,
|
|
acquireConversation,
|
|
normalizeTitle,
|
|
titleFromMessage,
|
|
metadataSkillKey,
|
|
assertSkillAvailable,
|
|
truncateText,
|
|
serializeMessage,
|
|
} from './ai-chat.conversations';
|
|
import { buildContext, buildUserContent } from './ai-chat.streaming';
|
|
import { executeGeneration } from './ai-chat.generation';
|
|
import {
|
|
assertGeneratedLength,
|
|
errorCode,
|
|
makeRedactingReplacer,
|
|
parseToolArguments,
|
|
redactText,
|
|
safeToolName,
|
|
throwIfAborted,
|
|
} from './ai-chat.helpers';
|
|
import { AiChatServiceBase } from './ai-chat.service-base';
|
|
|
|
@Injectable()
|
|
export class AiChatService extends AiChatServiceBase {
|
|
private readonly redactingReplacer = makeRedactingReplacer((value) => this.redactText(value));
|
|
|
|
constructor(
|
|
@InjectRepository(AiConversation)
|
|
conversations: Repository<AiConversation>,
|
|
@InjectRepository(AiMessage)
|
|
messages: Repository<AiMessage>,
|
|
@InjectRepository(AiToolRun)
|
|
toolRuns: Repository<AiToolRun>,
|
|
dataSource: DataSource,
|
|
configService: AiConfigService,
|
|
toolExecutor: AgentToolExecutor,
|
|
modelStream: AiModelStreamService,
|
|
attachmentService: AiAttachmentService,
|
|
formService: AiFormService,
|
|
reviewService: AiReviewService,
|
|
chartService: AiChartService,
|
|
abilityFactory: CaslAbilityFactory,
|
|
authorization: AuthorizationService,
|
|
a2uiSubmissions?: A2uiSubmissionsService,
|
|
excelReader?: AiExcelReaderService,
|
|
importsService?: ImportsService,
|
|
opLog?: OperationLogsService,
|
|
) {
|
|
super(
|
|
conversations,
|
|
messages,
|
|
toolRuns,
|
|
dataSource,
|
|
configService,
|
|
toolExecutor,
|
|
modelStream,
|
|
attachmentService,
|
|
formService,
|
|
reviewService,
|
|
chartService,
|
|
abilityFactory,
|
|
authorization,
|
|
a2uiSubmissions,
|
|
excelReader,
|
|
importsService,
|
|
opLog,
|
|
);
|
|
}
|
|
|
|
listSkills(user: AuthenticatedUser): AgentSkillDescriptor[] {
|
|
return this.toolExecutor.listSkills(AgentToolContextFactory.fromAuthenticatedUser(user));
|
|
}
|
|
|
|
serializeMessage(message: AiMessage): Record<string, unknown> {
|
|
return serializeMessage(this, message);
|
|
}
|
|
|
|
redactText(value: string): string {
|
|
return redactText(value);
|
|
}
|
|
|
|
summarize(value: unknown): string | null {
|
|
if (value === undefined || value === null) return null;
|
|
let json: string;
|
|
try {
|
|
json = JSON.stringify(value, this.redactingReplacer);
|
|
} catch {
|
|
return '[无法序列化]';
|
|
}
|
|
return this.redactText(json).slice(0, 2000);
|
|
}
|
|
|
|
safeStructured(value: unknown): unknown {
|
|
if (value === undefined || value === null) return null;
|
|
try {
|
|
return JSON.parse(JSON.stringify(value, this.redactingReplacer)) as unknown;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
parseToolArguments(value: string): unknown {
|
|
return parseToolArguments(value);
|
|
}
|
|
|
|
safeToolName(name: string): string {
|
|
return safeToolName(name);
|
|
}
|
|
|
|
throwIfAborted(signal: AbortSignal): void {
|
|
return throwIfAborted(signal);
|
|
}
|
|
|
|
errorCode(error: unknown): string {
|
|
return errorCode(error);
|
|
}
|
|
|
|
assertGeneratedLength(reasoning: string, content: string): void {
|
|
return assertGeneratedLength(reasoning, content);
|
|
}
|
|
|
|
buildContext(
|
|
conversationId: number,
|
|
focusUserMessageId: number,
|
|
focusContent: string | ModelContentPart[],
|
|
skillKey: string | null,
|
|
supportsVision: boolean,
|
|
): Promise<ModelMessage[]> {
|
|
return buildContext(this, conversationId, focusUserMessageId, focusContent, skillKey, supportsVision);
|
|
}
|
|
|
|
buildUserContent(
|
|
text: string,
|
|
attachments: any[],
|
|
supportsVision: boolean,
|
|
): Promise<string | ModelContentPart[]> {
|
|
return buildUserContent(this, text, attachments, supportsVision);
|
|
}
|
|
|
|
truncateText(value: string, max: number): string {
|
|
return truncateText(this, value, max);
|
|
}
|
|
|
|
metadataSkillKey(metadata: Record<string, unknown> | null): string | null {
|
|
return metadataSkillKey(this, metadata);
|
|
}
|
|
|
|
normalizeTitle(title?: string): string {
|
|
return normalizeTitle(this, title);
|
|
}
|
|
|
|
titleFromMessage(message: string): string {
|
|
return titleFromMessage(this, message);
|
|
}
|
|
|
|
assertSkillAvailable(user: AuthenticatedUser, skillKey?: string | null): void {
|
|
return assertSkillAvailable(this, user, skillKey);
|
|
}
|
|
|
|
requireOwnedConversation(userId: number, id: number): Promise<AiConversation> {
|
|
return requireOwnedConversation(this, userId, id);
|
|
}
|
|
|
|
acquireConversation(conversationId: number): Promise<void> {
|
|
return acquireConversation(this, conversationId);
|
|
}
|
|
|
|
executeGeneration(input: GenerationInput): Promise<void> {
|
|
return executeGeneration(this, input);
|
|
}
|
|
}
|