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:
99
third_party/pi/packages/durable/test/examples/07-configuration.ts
vendored
Normal file
99
third_party/pi/packages/durable/test/examples/07-configuration.ts
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// Per-conversation agent choices and Harness-wide settings.
|
||||
// Run from packages/durable:
|
||||
// node --conditions=source --experimental-strip-types test/examples/07-configuration.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 {
|
||||
AgentDoc,
|
||||
createRegistry,
|
||||
defineExtension,
|
||||
defineTool,
|
||||
Harness,
|
||||
type HarnessSettings,
|
||||
MemoryStorage,
|
||||
section,
|
||||
type ToolRegistration,
|
||||
} from "../../src/index.ts";
|
||||
|
||||
const context = BACKGROUND_CONTEXT;
|
||||
|
||||
// Apps may attach their own metadata to tools, such as a prompt snippet.
|
||||
type AppTool = ToolRegistration & { readonly snippet?: string };
|
||||
|
||||
function exampleTool(name: string, description: string): AppTool {
|
||||
const tool = defineTool({
|
||||
name,
|
||||
description,
|
||||
parameters: Type.Object({ path: Type.String() }),
|
||||
execute: async (args) => ({ output: [{ type: "text", text: `${name} ${args.path}` }] }),
|
||||
});
|
||||
return { ...tool, snippet: `Use ${name} for files.` };
|
||||
}
|
||||
|
||||
const read = exampleTool("read", "Read a file");
|
||||
const write = exampleTool("write", "Write a file");
|
||||
const grep = exampleTool("grep", "Search files");
|
||||
const Files = defineExtension<AppTool>({ name: "files", tools: [read, write] });
|
||||
const Search = defineExtension<AppTool>({ name: "search", tools: [grep] });
|
||||
// Sections see the offered tools as app tools, so one can render their snippets.
|
||||
const Snippets = defineExtension<AppTool>({
|
||||
name: "snippets",
|
||||
sections: [section("tool_snippets", (input) => input.agent.tools.map((tool) => tool.snippet).join("\n"))],
|
||||
});
|
||||
|
||||
const registry = createRegistry<AppTool>();
|
||||
registry.install(Files);
|
||||
registry.install(Search);
|
||||
registry.install(Snippets);
|
||||
|
||||
// Settings are Harness-wide run policy, read at every use and never stored.
|
||||
// A getter makes a value live, such as one backed by the app's settings file.
|
||||
let timeoutMs = 60_000;
|
||||
const settings: HarnessSettings = {
|
||||
get stream() {
|
||||
return { timeoutMs };
|
||||
},
|
||||
retry: { maxRetries: 5 },
|
||||
toolExecution: "sequential",
|
||||
};
|
||||
const harness = await Harness.open(new MemoryStorage(), { models: createModels(), registry, settings }, context);
|
||||
const root = await harness.root(context);
|
||||
|
||||
// A new conversation stores no choices and follows the host: every installed
|
||||
// extension, all of their tools.
|
||||
const tools = async () => (await root.agent(context)).tools.map((tool) => tool.name);
|
||||
console.log("default tools:", await tools());
|
||||
|
||||
// configure() stores choices in the conversation's pi.agent document, one
|
||||
// commit per call. Extensions and tools are passed as objects and stored by
|
||||
// name, so a typo cannot slip in.
|
||||
await root.configure(
|
||||
{
|
||||
model: { provider: "anthropic", modelId: "claude-sonnet-4-5" },
|
||||
thinkingLevel: "high",
|
||||
tools: [write, read],
|
||||
},
|
||||
context,
|
||||
);
|
||||
console.log("stored:", await harness.snapshot(AgentDoc, root.id, context));
|
||||
const agent = await root.agent(context);
|
||||
console.log("model:", agent.model, "thinking:", agent.thinkingLevel, "tools:", await tools());
|
||||
|
||||
// Deselect an extension; `null` clears a stored field back to the host default.
|
||||
await root.configure({ extensions: { remove: [Search] }, tools: null }, context);
|
||||
console.log("without search:", await tools());
|
||||
|
||||
// Stored names outlive the code. The conversation selects exactly these two
|
||||
// extensions; uninstalling "files" leaves it selected, and requests just stop
|
||||
// offering its tools until it is back.
|
||||
await root.configure({ extensions: [Files, Search] }, context);
|
||||
registry.uninstall(Files);
|
||||
console.log("files uninstalled:", await tools());
|
||||
registry.install(Files);
|
||||
console.log("files reinstalled:", await tools());
|
||||
|
||||
// Settings changes need no commit; the next request uses the new timeout.
|
||||
timeoutMs = 120_000;
|
||||
|
||||
await harness.close(context);
|
||||
Reference in New Issue
Block a user