char-slop/berry

multipurpose self-modifiable agentic harness

git clone https://git.t4t.associates/char-slop/berry

Charlotte Somcodemode: omit duplicate input schemas for direct tools70e18bf

main
5.1 KiB123 linesraw
1import { assert, assertEquals, assertObjectMatch, assertStringIncludes } from "@std/assert";
2import { Type } from "@earendil-works/pi-ai";
3import { getCurrentTools } from "@earendil-works/pi-ai/utils/transcript";
4import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai/providers/faux";
5import { defineExtension } from "@berry/api";
6import { lastToolResult, script, sectionsSeen, setup, toolsOffered, turn } from "./fixture.ts";
7
8Deno.test("conversation tools follow configuration in both catalogs and validate the current schema", async () => {
9  await using h = await setup();
10  let calls = 0;
11  await h.berry.install(defineExtension<boolean, { enabled: boolean }>({
12    name: "dynamic",
13    initial: () => false,
14    apply: (_, event) => event.enabled,
15    setup(ext) {
16      ext.configuration({
17        parameters: Type.Object({ enabled: Type.Boolean({ default: false }) }),
18        values: (enabled) => ({ enabled }),
19        events: ({ enabled }) => [{ enabled }],
20      });
21      return (conv) => {
22        conv.tool({
23          name: "answer",
24          description: conv.state ? "configured answer" : "initial answer",
25          parameters: Type.Object({ value: conv.state ? Type.Number() : Type.String() }),
26          returns: Type.String(),
27          expose: "both",
28          execute: ({ value }) => {
29            calls++;
30            return String(value);
31          },
32          text: (value) => `answer: ${value}`,
33        });
34        if (conv.state) {
35          conv.tool({
36            name: "private",
37            description: "direct only",
38            parameters: Type.Object({}),
39            expose: "model",
40            execute: () => "private answer",
41          });
42        }
43      };
44    },
45  }));
46  const tid = await h.berry.create({ extensions: { dynamic: {} }, model: h.model });
47  const other = await h.berry.create({ extensions: { dynamic: {} }, model: h.model });
48  const direct = (value: string | number) =>
49    fauxAssistantMessage(fauxToolCall("answer", { value }), { stopReason: "toolUse" });
50  const initial = await turn(h, tid, [direct("hello")]);
51  assert(toolsOffered(initial[0]).includes("answer"));
52  assertStringIncludes(lastToolResult(initial[1]).text, "answer: hello");
53
54  await h.berry.configureExtension(tid, "dynamic", { enabled: true });
55  const configured = await turn(h, tid, [
56    direct("stale"),
57    direct(42),
58    script("return await tools.answer({ value: 43 })"),
59    script("return await tools.private({})"),
60  ]);
61  assert(lastToolResult(configured[1]).isError);
62  assertStringIncludes(lastToolResult(configured[2]).text, "answer: 42");
63  assertStringIncludes(lastToolResult(configured[3]).text, "43");
64  assert(lastToolResult(configured[4]).isError);
65  assert(toolsOffered(configured[0]).includes("private"));
66  const catalog =
67    sectionsSeen(configured[0]).findLast((section) => section.codemode !== undefined)?.codemode ??
68      "";
69  assertStringIncludes(catalog, "answer: string");
70  assert(!catalog.includes("value:"));
71  assert(!catalog.includes("private"));
72  const offered = getCurrentTools(configured[0]);
73  const answer = offered.find((tool) => tool.name === "answer")!;
74  assertEquals(answer.description, "configured answer");
75  assertObjectMatch(answer.parameters, { properties: { value: { type: "number" } } });
76  assertEquals(calls, 3);
77
78  const unchanged = await turn(h, other, [direct("still a string")]);
79  assert(!toolsOffered(unchanged[0]).includes("private"));
80  assertStringIncludes(lastToolResult(unchanged[1]).text, "still a string");
81  await h.berry.configureExtension(tid, "dynamic", { enabled: false });
82  const removed = await turn(h, tid, [
83    fauxAssistantMessage(fauxToolCall("private", {}), { stopReason: "toolUse" }),
84  ]);
85  assert(!toolsOffered(removed[0]).includes("private"));
86  assert(lastToolResult(removed[1]).isError);
87});
88
89Deno.test("conversation registrations replace static tools and emitted state changes the next request", async () => {
90  await using h = await setup();
91  await h.berry.install(defineExtension<boolean, boolean>({
92    name: "replacement",
93    initial: () => false,
94    apply: (_, event) => event,
95    setup(ext) {
96      ext.tool({
97        name: "replace",
98        description: "static",
99        parameters: Type.Object({}),
100        expose: "both",
101        execute: () => "must not execute",
102      });
103      return (conv) =>
104        conv.tool({
105          name: "replace",
106          description: "dynamic",
107          parameters: Type.Object({}),
108          expose: conv.state ? "codemode" : "both",
109          async execute(_, ctx) {
110            await ctx.emit(true);
111            return "replacement";
112          },
113        });
114    },
115  }));
116  const tid = await h.berry.create({ extensions: { replacement: {} }, model: h.model });
117  const direct = fauxAssistantMessage(fauxToolCall("replace", {}), { stopReason: "toolUse" });
118  const seen = await turn(h, tid, [direct, direct, script("return await tools.replace({})")]);
119  assertStringIncludes(lastToolResult(seen[1]).text, "replacement");
120  assert(!toolsOffered(seen[1]).includes("replace"));
121  assert(lastToolResult(seen[2]).isError);
122  assertStringIncludes(lastToolResult(seen[3]).text, "replacement");
123});