chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
1 parent
719370c8f7
commit
6081e3e325
1743 files changed
+205459
-131704
No files matched your search
@@ -0,0 +1,100 @@
|
||||
// Late join: a client attaches while a run is already underway. It gets the current state first, the conversation
|
||||
// view or the snapshot event, and then only what changes after that.
|
||||
// Run from packages/durable:
|
||||
// node --conditions=source --experimental-strip-types test/examples/21-late-join.ts
|
||||
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
||||
import { Type } from "@earendil-works/pi-ai";
|
||||
import { createModels } from "@earendil-works/pi-ai/models";
|
||||
import { fauxAssistantMessage, fauxProvider, fauxText, fauxToolCall } from "@earendil-works/pi-ai/providers/faux";
|
||||
import {
|
||||
type AgentEvent,
|
||||
createRegistry,
|
||||
defineExtension,
|
||||
defineTool,
|
||||
Harness,
|
||||
type LiveState,
|
||||
MemoryStorage,
|
||||
watchEvents,
|
||||
} from "../../src/index.ts";
|
||||
|
||||
const context = BACKGROUND_CONTEXT;
|
||||
const pause = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
// A tool that prints a line every 100 ms, then a slowly streamed answer.
|
||||
const registry = createRegistry();
|
||||
registry.install(
|
||||
defineExtension({
|
||||
name: "count",
|
||||
tools: [
|
||||
defineTool({
|
||||
name: "count",
|
||||
description: "Counts to ten",
|
||||
parameters: Type.Object({}),
|
||||
execute: async (_args, api) => {
|
||||
for (let n = 1; n <= 10; n++) {
|
||||
api.output(`${n}\n`);
|
||||
await pause(100);
|
||||
}
|
||||
return {};
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
const faux = fauxProvider({ tokensPerSecond: 40, tokenSize: { min: 1, max: 1 } });
|
||||
faux.setResponses([
|
||||
fauxAssistantMessage([fauxToolCall("count", {}, { id: "call-1" })], { stopReason: "toolUse" }),
|
||||
fauxAssistantMessage([fauxText("Counted to ten, and this answer streams slowly.")]),
|
||||
]);
|
||||
const models = createModels();
|
||||
models.setProvider(faux.provider);
|
||||
const harness = await Harness.open(new MemoryStorage(), { models, registry }, context);
|
||||
const root = await harness.root(context, { agent: { model: { provider: "faux", modelId: "faux-1" } } });
|
||||
|
||||
const submission = await root.submit({ type: "input", content: "Count to ten, then tell me." }, context);
|
||||
// Join while the tool is halfway through.
|
||||
await pause(500);
|
||||
|
||||
// Structural client: the view holds the committed transcript and documents, including the running tool's output.
|
||||
const view = await root.viewState(context);
|
||||
const live = view.value.docs["pi.live"] as LiveState;
|
||||
console.log(
|
||||
"view entries:",
|
||||
view.value.entries.map((entry) => entry.kind),
|
||||
);
|
||||
console.log("view tool slot:", live.tools?.[0]?.status, JSON.stringify(live.tools?.[0]?.output));
|
||||
view.subscribe((value) => {
|
||||
const slot = (value.docs["pi.live"] as LiveState).tools?.[0];
|
||||
if (slot?.status === "running") console.log("view output now:", JSON.stringify(slot.output));
|
||||
});
|
||||
|
||||
// Event client: the snapshot event carries the same state; later events apply on top of it.
|
||||
const stream = await watchEvents(harness, root.id, context);
|
||||
console.log(
|
||||
"snapshot tools:",
|
||||
stream.snapshot.tools.map((slot) => `${slot.name} ${slot.status}`),
|
||||
);
|
||||
let output = stream.snapshot.tools[0]?.output ?? "";
|
||||
stream.start(async (events: readonly AgentEvent[]) => {
|
||||
for (const event of events) {
|
||||
if (event.type === "tool_execution_update" && event.output !== undefined) {
|
||||
output =
|
||||
"set" in event.output
|
||||
? event.output.set
|
||||
: output.slice(event.output.trimStart ?? 0) + (event.output.append ?? "");
|
||||
console.log("event output now:", JSON.stringify(output));
|
||||
} else if (event.type === "message_update") {
|
||||
const deltas = event.changes.flatMap((change) => (change.type === "text_delta" ? [change.delta] : []));
|
||||
console.log("event text delta:", JSON.stringify(deltas.join("")));
|
||||
} else {
|
||||
console.log("event:", event.type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await submission.wait(context);
|
||||
await harness.waitForIdle(context);
|
||||
await pause(0);
|
||||
await stream.stop();
|
||||
view.dispose();
|
||||
await harness.close(context);
|
||||
Reference in new issue
Block a user