64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
// 一次运行:准备工作区、创建 Agent、给每个事件打上序号和相对时间。
|
|
// 本地服务和命令行脚本都用它。
|
|
import { randomBytes } from 'node:crypto';
|
|
import type { RunRecording, TraceBody, TraceEvent } from '../shared/types.ts';
|
|
import { Agent } from './agent.ts';
|
|
import type { LLM } from './llm.ts';
|
|
import { getLevel } from './levels.ts';
|
|
import { prepareWorkspace } from './workspace.ts';
|
|
|
|
export class Run {
|
|
readonly runId: string;
|
|
readonly level: number;
|
|
readonly task: string;
|
|
readonly startedAt = Date.now();
|
|
readonly events: TraceEvent[] = [];
|
|
readonly agent: Agent;
|
|
readonly workspace: string;
|
|
private listeners = new Set<(event: TraceEvent) => void>();
|
|
private seq = 0;
|
|
|
|
constructor(llm: LLM, level: number, task?: string) {
|
|
const config = getLevel(level);
|
|
this.level = level;
|
|
this.task = task?.trim() || config.defaultTask;
|
|
this.runId = `L${level}-${new Date().toISOString().slice(11, 19).replaceAll(':', '')}-${randomBytes(2).toString('hex')}`;
|
|
this.workspace = prepareWorkspace(this.runId);
|
|
this.agent = new Agent({ level: config, workspace: this.workspace, llm, emit: (body) => this.record(body) });
|
|
this.record({
|
|
type: 'run.start',
|
|
runId: this.runId,
|
|
level,
|
|
model: llm.provider.model,
|
|
baseURL: llm.provider.baseURL,
|
|
tools: this.agent.tools.specs(),
|
|
startedAt: this.startedAt,
|
|
});
|
|
}
|
|
|
|
start(): void {
|
|
this.agent.sendUserMessage(this.task);
|
|
void this.agent.run();
|
|
}
|
|
|
|
stop(): void {
|
|
this.agent.stop();
|
|
this.record({ type: 'run.stop', reason: 'stopped' });
|
|
}
|
|
|
|
subscribe(listener: (event: TraceEvent) => void): () => void {
|
|
this.listeners.add(listener);
|
|
return () => this.listeners.delete(listener);
|
|
}
|
|
|
|
recording(): RunRecording {
|
|
return { runId: this.runId, level: this.level, task: this.task, startedAt: this.startedAt, events: this.events };
|
|
}
|
|
|
|
private record(body: TraceBody): void {
|
|
const event = { seq: ++this.seq, t: Date.now() - this.startedAt, ...body } as TraceEvent;
|
|
this.events.push(event);
|
|
for (const listener of this.listeners) listener(event);
|
|
}
|
|
}
|