51 lines
2.9 KiB
TypeScript
51 lines
2.9 KiB
TypeScript
// Fake model drives AgentHarness through 2 whitelisted tools; streams events.
|
|
import { createModels, fauxProvider } from "@earendil-works/pi-ai";
|
|
import { AgentHarness, BACKGROUND_CONTEXT, MemorySessionRepo } from "@earendil-works/pi-agent-core";
|
|
import { downloadSubmit, inventoryQuery, script, submitted } from "./common.ts";
|
|
|
|
const faux = fauxProvider({ provider: "umt-fake" });
|
|
const models = createModels();
|
|
models.setProvider(faux.provider);
|
|
faux.setResponses(script);
|
|
|
|
const repo = new MemorySessionRepo();
|
|
const session = await repo.create({ id: "spike-1" }, BACKGROUND_CONTEXT);
|
|
const { harness } = await AgentHarness.create({
|
|
session, models, model: faux.getModel(),
|
|
systemPrompt: "你是媒体库自动化助手,只能使用提供的工具。",
|
|
tools: [inventoryQuery, downloadSubmit], // explicit whitelist; no discovery
|
|
toolContext: { actorId: "user:1", grants: ["download.submit"] },
|
|
thinkingLevel: "off",
|
|
compaction: { enabled: true, reserveTokens: 4096, keepRecentTokens: 8000 },
|
|
retry: { enabled: true, maxRetries: 2, baseDelayMs: 100 },
|
|
toolExecution: "sequential",
|
|
}, BACKGROUND_CONTEXT);
|
|
|
|
// Server-side authorization gate in addition to the whitelist.
|
|
harness.hooks.on("before_tool", (ev) => {
|
|
console.log(` [hook before_tool] ${ev.toolName} args=${JSON.stringify(ev.args)}`);
|
|
return undefined;
|
|
});
|
|
|
|
const types = ["run_start", "turn_start", "message_update", "tool_start", "tool_update", "tool_end", "turn_end", "run_end", "usage", "fault", "handler_error"] as const;
|
|
for (const t of types) harness.events.on(t as any, (e: any) => {
|
|
if (e.type === "message_update") {
|
|
if (e.event.type === "text_delta") process.stdout.write(` [text_delta] ${JSON.stringify(e.event.delta)}\n`);
|
|
else if (e.event.type === "toolcall_end") console.log(` [toolcall_end] ${e.event.toolCall.name}`);
|
|
return;
|
|
}
|
|
if (e.type === "tool_end") return console.log(`[tool_end] ${e.toolName} isError=${e.isError} -> ${JSON.stringify(e.result.content[0]?.text)}`);
|
|
if (e.type === "tool_start") return console.log(`[tool_start] ${e.toolName} ${JSON.stringify(e.args)}`);
|
|
if (e.type === "usage") return console.log(`[usage] in=${e.row?.usage?.input ?? "?"} totals.input=${e.totals.input} output=${e.totals.output}`);
|
|
if (e.type === "run_end") return console.log(`[run_end] status=${e.status}${e.error ? " error=" + JSON.stringify(e.error) : ""}`);
|
|
console.log(`[${e.type}]${e.lane ? " lane=" + e.lane : ""}`);
|
|
});
|
|
|
|
const lane = await harness.lane("main", BACKGROUND_CONTEXT);
|
|
const result = await lane.prompt("把《Dune》1080p 收进来", undefined, BACKGROUND_CONTEXT);
|
|
console.log("RESULT", JSON.stringify(result));
|
|
console.log("SUBMITTED", JSON.stringify(submitted), "fake calls:", faux.state.callCount);
|
|
const entries = await lane.findEntries(undefined, BACKGROUND_CONTEXT);
|
|
console.log("TRANSCRIPT roles:", entries.map((e: any) => e.type === "message" ? e.message.role : e.type).join(","));
|
|
await harness.close(BACKGROUND_CONTEXT);
|