feat: AI 对话支持 A2UI 表单/审查/图表与 Agent 工具

This commit is contained in:
2026-08-05 17:11:00 +08:00
parent 644c35ce53
commit 0e6e3e2d96
64 changed files with 8395 additions and 6434 deletions

View File

@@ -0,0 +1,48 @@
export function redactText(value: string): string {
return value
.replace(/1[3-9]\d{9}/g, '[PHONE]')
.replace(/\b\d{17}[\dXx]\b/g, '[ID_CARD]')
.replace(/Bearer\s+[A-Za-z0-9._~+/-]+=*/gi, 'Bearer [REDACTED]')
.replace(/(sk-|api[_-]?key["'=:\s]+)[A-Za-z0-9._-]{8,}/gi, '$1[REDACTED]');
}
export function makeRedactingReplacer(redact: (value: string) => string) {
return (key: string, value: unknown): unknown => {
if (/password|token|secret|api.?key|authorization|phone|mobile|id.?card|身份证/i.test(key)) {
return '[REDACTED]';
}
if (typeof value === 'string') return redact(value);
return value;
};
}
export function parseToolArguments(value: string): unknown {
try {
return JSON.parse(value || '{}') as unknown;
} catch {
return null;
}
}
export function safeToolName(name: string): string {
return name.replace(/[^a-zA-Z0-9_]/g, '_').slice(0, 64) || '_invalid';
}
export function throwIfAborted(signal: AbortSignal): void {
if (signal.aborted) throw signal.reason ?? new Error('aborted');
}
export function errorCode(error: unknown): string {
if (error && typeof error === 'object' && 'status' in error) {
const status = Number(error.status);
if (status === 408) return 'UPSTREAM_TIMEOUT';
if (status >= 400 && status < 500) return 'UPSTREAM_REQUEST_ERROR';
}
return 'UPSTREAM_ERROR';
}
export function assertGeneratedLength(reasoning: string, content: string): void {
if (reasoning.length + content.length > 256 * 1024) {
throw new Error('AI response exceeded limit');
}
}