Files
agent-loop/async-agent-lab/live/run.ts
2026-11-12 15:29:12 +08:00

68 lines
2.4 KiB
TypeScript

import { mkdtempSync } from 'node:fs';
import { join } from 'node:path';
import type { LLM } from '../agent/llm.ts';
import { RUNS_DIR } from '../agent/workspace.ts';
import { Runtime } from './runtime.ts';
import { asynchronous, runAsync } from './scenarios/async.ts';
import { background, runBackground } from './scenarios/background.ts';
import { blocking, runBlocking } from './scenarios/blocking.ts';
import { sync, runSync } from './scenarios/sync.ts';
import type { LiveBody, LiveEvent } from './types.ts';
export const scenarios = [
{ ...sync, execute: runSync },
{ ...background, execute: runBackground },
{ ...blocking, execute: runBlocking },
{ ...asynchronous, execute: runAsync },
];
export class LiveRun {
readonly id: string;
readonly workspace: string;
readonly events: LiveEvent[] = [];
readonly listeners = new Set<(event: LiveEvent) => void>();
readonly runtime: Runtime;
private startedAt = Date.now();
private closed = false;
private scenario: (typeof scenarios)[number];
private llm: LLM;
constructor(scenario: (typeof scenarios)[number], llm: LLM) {
this.scenario = scenario;
this.llm = llm;
this.workspace = mkdtempSync(join(RUNS_DIR, `${scenario.id}-`));
this.id = this.workspace.split('/').at(-1)!;
this.runtime = new Runtime(scenario, this.workspace, llm, (event) => this.record(event));
this.record({ type: 'run.start', id: this.id, scenario: scenario.id, workspace: this.workspace, startedAt: this.startedAt });
}
private record(body: LiveBody) {
if (this.closed) return;
const secret = this.llm.provider.apiKey;
const serialized = JSON.stringify(body);
const safe = secret && serialized.includes(secret) ? JSON.parse(serialized.replaceAll(secret, '[已隐藏]')) as LiveBody : body;
const event = { ...safe, seq: this.events.length + 1, t: Date.now() - this.startedAt };
this.events.push(event);
for (const listener of this.listeners) listener(event);
}
async start() {
this.record({ type: 'run.state', state: 'running' });
try {
const summary = await this.scenario.execute(this.runtime);
this.record({ type: 'run.state', state: 'done', summary });
} catch (error) {
this.record({ type: 'run.state', state: 'error', note: (error as Error).message });
} finally {
this.closed = true;
this.runtime.stop();
}
}
stop() {
this.record({ type: 'run.state', state: 'stopped' });
this.closed = true;
this.runtime.stop();
}
}