feat(good-fences): code-mode prompt; the model writes code to mow
codeModeMessages turns what I say plus `about` into a prompt: a neighbor persona, rules for the reply (one async arrow function over `env`), and the lent things' types. Things are remote and calls on one are delivered in order, so a lamp example shows pipelining: await only for a value, or on a batch's last call. With only the rule, the model awaited every call; with the example, it pipelines. aboutMower gives the Mower type with Promise returns. The model test checks the code calls only Mower methods, pushes, and awaits less often than it calls; it saves the code to itest-output/ via a reporter, since the test runs in workerd, which can't write files. Model tests get 60s. Green: both model tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4a85143a66
commit
a2ca59a27e
6 files changed
+113
-4
No files matched your search
@@ -6,16 +6,20 @@ import { codeModeMessages, extractCode } from "../src/code-mode.ts";
|
||||
import { makeWorkersAiModel, MODEL } from "../src/model.ts";
|
||||
import { aboutMower } from "../src/mower.ts";
|
||||
|
||||
test("lent a mower, the neighbor writes code to mow", async () => {
|
||||
test("lent a mower, the neighbor writes code to mow", async ({ annotate }) => {
|
||||
const model = makeWorkersAiModel(env.AI, MODEL);
|
||||
const messages = codeModeMessages({
|
||||
say: "you can use this mower for the weekend",
|
||||
about: { mower: aboutMower },
|
||||
});
|
||||
const code = extractCode(await model.complete(messages));
|
||||
console.log(code);
|
||||
await annotate("the code the model wrote", { body: code, contentType: "text/javascript" });
|
||||
|
||||
const calls = [...code.matchAll(/env\.mower\.(\w+)\(/g)].map(([, method]) => method);
|
||||
// `env.mower.push()`, or `mower.push()` after destructuring env
|
||||
const calls = [...code.matchAll(/\bmower\.(\w+)\(/g)].map(([, method]) => method);
|
||||
expect(calls).toContain("push");
|
||||
for (const method of calls) expect(["start", "push", "turn", "stop"]).toContain(method);
|
||||
// calls pipeline: await only for a value, or once at the end
|
||||
const awaits = code.match(/\bawait\b/g) ?? [];
|
||||
expect(awaits.length).toBeLessThan(calls.length);
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/** @file A vitest reporter that saves annotation bodies (e.g. the code a model
|
||||
* wrote) to `itest-output/`, one file per test per run; tests run in workerd,
|
||||
* which can't write files. */
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import type { Reporter } from "vitest/reporters";
|
||||
|
||||
const outDir = new URL("../itest-output/", import.meta.url);
|
||||
const extFor: Record<string, string> = { "text/javascript": "js", "text/plain": "txt" };
|
||||
const slug = (text: string) => text.replace(/[^\w]+/g, "-").replace(/^-|-$/g, "");
|
||||
|
||||
export default class SaveAnnotations implements Reporter {
|
||||
async onTestCaseAnnotate(...[testCase, annotation]: Parameters<NonNullable<Reporter["onTestCaseAnnotate"]>>) {
|
||||
const { body, contentType = "text/plain" } = annotation.attachment ?? {};
|
||||
if (body === undefined) return;
|
||||
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const file = new URL(`${slug(testCase.name)}-${stamp}.${extFor[contentType] ?? "txt"}`, outDir);
|
||||
await mkdir(outDir, { recursive: true });
|
||||
await writeFile(file, body);
|
||||
console.log(`saved ${annotation.message}: ${file.pathname}`);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user