feat(tasks): add skill-templated task graph execution

This commit is contained in:
2026-06-23 10:22:58 +08:00
parent 6843d89b2c
commit 53b13e8eac
53 changed files with 4773 additions and 756 deletions

View File

@ -46,6 +46,48 @@ function timestampLabel(value?: string | null): string {
return date.toLocaleString();
}
const latencyOrder = [
'total_ms',
'router_ms',
'mcp_ms',
'skill_assembly_ms',
'tool_assembly_ms',
'context_build_ms',
'llm_ms',
'tool_ms',
'session_write_ms',
];
function latencyLabel(key: string, locale: string): string {
const labels: Record<string, [string, string]> = {
total_ms: ['总耗时', 'total'],
router_ms: ['路由', 'router'],
mcp_ms: ['MCP', 'MCP'],
skill_assembly_ms: ['技能', 'skills'],
tool_assembly_ms: ['工具选择', 'tool select'],
context_build_ms: ['上下文', 'context'],
llm_ms: ['模型', 'LLM'],
tool_ms: ['工具执行', 'tools'],
session_write_ms: ['写日志', 'writes'],
};
const label = labels[key];
if (!label) return key.replace(/_ms$/, '');
return pickAppText(locale, label[0], label[1]);
}
function formatLatencyMs(value: unknown): string | null {
if (typeof value !== 'number' || !Number.isFinite(value)) return null;
if (value >= 1000) return `${(value / 1000).toFixed(value >= 10000 ? 1 : 2)}s`;
return `${Math.round(value)}ms`;
}
function latencyEntries(latency?: Record<string, number>) {
if (!latency) return [];
return latencyOrder
.map((key) => [key, formatLatencyMs(latency[key])] as const)
.filter((entry): entry is readonly [string, string] => Boolean(entry[1]));
}
export default function LogsPage() {
const { locale } = useAppI18n();
const [sessions, setSessions] = useState<ChatLogSession[]>([]);
@ -142,6 +184,7 @@ export default function LogsPage() {
<div className="space-y-3">
{runs.map((run) => {
const expanded = expandedRuns.has(run.run_id);
const latencies = latencyEntries(run.latency_ms);
return (
<Card key={`${run.session_id}:${run.run_id}`} className="overflow-hidden">
<button
@ -164,6 +207,15 @@ export default function LogsPage() {
<span className="block truncate text-xs text-muted-foreground">
{run.sessionTitle} · {run.run_id}
</span>
{latencies.length ? (
<span className="flex flex-wrap gap-1.5">
{latencies.map(([key, value]) => (
<Badge key={key} variant={key === 'total_ms' ? 'default' : 'outline'} className="font-mono text-[11px]">
{latencyLabel(key, locale)} {value}
</Badge>
))}
</span>
) : null}
</span>
<ChevronDown className={`mt-1 h-4 w-4 shrink-0 text-muted-foreground transition-transform ${expanded ? 'rotate-180' : ''}`} />
</button>

View File

@ -105,6 +105,7 @@ export interface ChatLogRun {
started_at?: string;
ended_at?: string | null;
finish_reason?: string | null;
latency_ms?: Record<string, number>;
events: ChatLogEvent[];
}