Files
gongxue-base/apps/server/src/ai-chat/ai-chat.submit-content.ts
wangziqi 24e0ecbdaf feat(ai): 业务上下文感知与 A2UI 链路统一
- 新增代码内业务上下文元数据层(实体字典 + 三大闭环工作流)
- 新增 get_business_context / get_entity_schema / get_pending_tasks 运行时工具
- SYSTEM_PROMPT 与技能目录改为先查业务流程/待办再执行
- A2UI 增加 ai_a2ui_submissions 幂等表、表单过期、ui.artifact 事件
- 提交回灌携带 submissionId / fieldErrors / 下一步建议
- 前端 uiArtifacts 归一化与过期表单禁用
2026-08-06 15:23:23 +08:00

128 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { AiReview } from './entities/ai-review.entity';
import type { AiChatServiceContext } from './ai-chat.types';
export function a2uiSubmitInfo(
metadata: Record<string, unknown> | null,
): {
formId: string;
title: string;
values: Record<string, unknown>;
submissionId?: string;
fieldErrors?: Array<{ field: string; message: string }>;
} | null {
const submit = metadata?.a2uiSubmit;
if (!submit || typeof submit !== 'object' || Array.isArray(submit)) return null;
const record = submit as Record<string, unknown>;
const formId = typeof record.formId === 'string' ? record.formId : '';
const title = typeof record.formTitle === 'string' ? record.formTitle : '表单';
const values =
record.values && typeof record.values === 'object' && !Array.isArray(record.values)
? (record.values as Record<string, unknown>)
: {};
const submissionId =
typeof record.submissionId === 'string' ? record.submissionId : undefined;
const fieldErrors = Array.isArray(record.fieldErrors)
? (record.fieldErrors as Array<{ field: string; message: string }>)
: [];
return { formId, title, values, submissionId, fieldErrors };
}
export function buildFormSubmitModelContent(submit: {
title: string;
values: Record<string, unknown>;
submissionId?: string;
fieldErrors?: Array<{ field: string; message: string }>;
}): string {
let json: string;
try {
json = JSON.stringify(submit.values);
} catch {
json = '[无法序列化]';
}
const submissionId = submit.submissionId ? `\n提交ID${submit.submissionId}` : '';
const fieldErrors = submit.fieldErrors?.length
? `\nfieldErrors: ${JSON.stringify(submit.fieldErrors)}`
: '\nfieldErrors: []';
return `【表单提交:${submit.title}${submissionId}\n提交值JSON${json.slice(0, 32 * 1024)}${fieldErrors}\n用户已在表单中确认你可以执行允许的写操作工具。`;
}
export async function markFormSubmittedOnMessage(
context: AiChatServiceContext,
assistantMessageId: number,
conversationId: number,
): Promise<void> {
const assistant = await context.messages.findOne({
where: { id: assistantMessageId, conversationId },
});
const a2ui = assistant?.metadata?.a2uiForm;
if (assistant && a2ui && typeof a2ui === 'object' && !Array.isArray(a2ui)) {
assistant.metadata = {
...assistant.metadata,
a2uiForm: { ...(a2ui as Record<string, unknown>), status: 'submitted' },
};
await context.messages.save(assistant);
}
}
export function a2uiReviewSubmitInfo(
metadata: Record<string, unknown> | null,
): {
reviewId: string;
reviewTitle: string;
resultMessage: string;
submissionId?: string;
nextSteps?: Array<{ key: string; label: string }>;
} | null {
const submit = metadata?.a2uiReviewSubmit;
if (!submit || typeof submit !== 'object' || Array.isArray(submit)) return null;
const record = submit as Record<string, unknown>;
if (typeof record.reviewId !== 'string') return null;
const submissionId =
typeof record.submissionId === 'string' ? record.submissionId : undefined;
const nextSteps = Array.isArray(record.nextSteps)
? (record.nextSteps as Array<{ key: string; label: string }>)
: [];
return {
reviewId: record.reviewId,
reviewTitle: typeof record.reviewTitle === 'string' ? record.reviewTitle : '批量导入',
resultMessage: typeof record.resultMessage === 'string' ? record.resultMessage : '导入已完成',
submissionId,
nextSteps,
};
}
export function buildReviewSubmitModelContent(submit: {
reviewId: string;
reviewTitle: string;
resultMessage: string;
submissionId?: string;
nextSteps?: Array<{ key: string; label: string }>;
}): string {
const submissionId = submit.submissionId ? `\n提交ID${submit.submissionId}` : '';
const nextSteps = submit.nextSteps?.length
? `\n下一步建议${submit.nextSteps.map((step) => step.label).join('')}`
: '';
return `【批量导入已确认:${submit.reviewTitle}${submissionId}\n${submit.resultMessage}${nextSteps}\n数据已由系统入库不要再次调用写入工具直接向用户汇报导入结果即可。`;
}
export async function markReviewSubmittedOnMessage(
context: AiChatServiceContext,
assistantMessageId: number,
conversationId: number,
review?: AiReview,
): Promise<void> {
const assistant = await context.messages.findOne({
where: { id: assistantMessageId, conversationId },
});
const a2ui = assistant?.metadata?.a2uiReview;
if (assistant && a2ui && typeof a2ui === 'object' && !Array.isArray(a2ui)) {
assistant.metadata = {
...assistant.metadata,
a2uiReview: review
? context.reviewService.serialize(review)
: { ...(a2ui as Record<string, unknown>), status: 'submitted' },
};
await context.messages.save(assistant);
}
}