fix: show tool parameters and skip duplicate calls

This commit is contained in:
2026-09-27 14:47:55 +08:00
parent 40fd07a1bc
commit e5bf1cf250
7 changed files with 192 additions and 42 deletions
+29 -3
View File
@@ -7,7 +7,7 @@ import { api, post, put, subscribeRun } from './api';
const { Title, Text, Paragraph } = Typography;
const kinds: Record<string, string> = { import: '导入学生材料', course_ingest: '课程资料入库', create_batch: '创建评分批次', start_batch: '启动/重试评分批次', review: '教师复核', export: '导出成绩或反馈', index_retry: '重试索引任务' };
const categories: Record<string, string> = { stage1: '阶段报告1', stage2: '阶段报告2', stage3: '阶段报告3', final_report: '最终报告', presentation: '汇报PPT/PDF' };
const statusColor = (status: string) => status === 'completed' || status === 'ready' ? 'green' : status === 'failed' || status === 'stale' ? 'red' : 'orange';
const statusColor = (status: string) => status === 'completed' || status === 'ready' ? 'green' : status === 'failed' || status === 'stale' ? 'red' : status === 'skipped' ? 'default' : 'orange';
const reportError = (error: unknown) => message.error(error instanceof Error ? error.message : String(error));
function ImportEditor({ action, conversationId, reload }: { action: any; conversationId: number; reload: () => void }) {
@@ -108,6 +108,32 @@ function Citations({ items }: { items: any[] }) {
if (!unique.length) return null;
return <section className="chat-citations" aria-label="引用资料"><details className="chat-citation-group"><summary><Text strong>引用 · {unique.length}</Text></summary><CitationCards items={unique} /></details></section>;
}
const argumentLabels: Record<string, string> = {
query: '查询词 (query)', course_version_id: '课程版本 ID', batch_id: '评分批次 ID', project_id: '学生项目 ID',
score_id: '评分 ID', task_id: '任务 ID', rubric_version_id: '量规版本 ID', criterion_id: '评分项 ID',
project_offset: '项目偏移量', criterion_offset: '评分项偏移量', offset: '偏移量', limit: '数量上限',
status: '状态筛选', student_no: '学号', relative_dir: '目录', score: '分数', format: '格式',
};
function visibleArguments(call: any) {
const args: Record<string, unknown> = call.arguments && typeof call.arguments === 'object' && !Array.isArray(call.arguments)
? { ...call.arguments } : {};
let missing = Number(args.omitted_text_fields || args.omitted_fields || 0);
delete args.omitted_text_fields;
delete args.omitted_fields;
// Older score calls kept their effective offsets in the result but dropped them from arguments.
if (call.name === 'get_scores') for (const field of ['project_offset', 'criterion_offset']) {
if (!(field in args) && typeof call.result?.[field] === 'number') {
args[field] = call.result[field];
missing = Math.max(0, missing - 1);
}
}
if (['search_course_material', 'search_project_material'].includes(call.name) && !('query' in args) && missing > 0) {
args.query = '历史调用未保存查询词';
missing -= 1;
}
if (missing > 0) args['历史未保存参数'] = `${missing} 项`;
return Object.fromEntries(Object.entries(args).map(([field, value]) => [argumentLabels[field] || field, value]));
}
function Tools({ calls }: { calls: any[] }) {
if (!calls?.length) return null;
const active = calls.filter(call => call.status === 'running').length;
@@ -115,9 +141,9 @@ function Tools({ calls }: { calls: any[] }) {
return <section className="chat-tools" aria-label="工具调用"><details className="chat-tool-group"><summary><Text strong>工具调用 · {calls.length}</Text>
{active > 0 && <Tag color="processing">{active} 项进行中</Tag>}{failed > 0 && <Tag color="red">{failed} 项失败</Tag>}</summary>
<div className="chat-tool-list">{calls.map((call, index) => <details key={call.id || index} className="chat-tool"><summary><Space wrap size="small"><Text code>{call.name || '未知工具'}</Text>
<Tag color={statusColor(call.status || 'completed')}>{call.status === 'running' ? '进行中' : call.status === 'failed' ? '失败' : '成功'}</Tag>
<Tag color={statusColor(call.status || 'completed')}>{call.status === 'running' ? '进行中' : call.status === 'failed' ? '失败' : call.status === 'skipped' ? '已跳过' : '成功'}</Tag>
<Text type="secondary">{call.status === 'running' ? '正在执行' : call.error ? String(call.error).slice(0, 80) : call.summary || (typeof call.result === 'string' ? call.result : '已完成')}</Text></Space></summary>
<div className="tool-detail"><Text strong>调用参数</Text><pre>{JSON.stringify(call.arguments || {}, null, 2)}</pre>
<div className="tool-detail"><Text strong>调用参数</Text><pre>{JSON.stringify(visibleArguments(call), null, 2)}</pre>
<Text strong>{call.error ? '错误' : '调用结果'}</Text><pre>{call.error || (typeof call.result === 'string' ? call.result : JSON.stringify(call.result ?? '运行中', null, 2))}</pre></div>
</details>)}</div></details></section>;
}