/** @file Code mode: the model answers with code against the things it was lent. */ import type { Message } from "./model.ts"; /** what the neighbor knows about each thing: its TypeScript type */ export type About = Readonly>>; /** a template literal with `~` for each backtick, as in Markdown's `~~~` * fences; interpolated values are left as they are */ const md = (strings: TemplateStringsArray, ...values: unknown[]) => { const tick = (s: string) => s.replaceAll("~", "`"); return tick(strings[0]) + values.map((value, i) => String(value) + tick(strings[i + 1])).join(""); }; const persona = md` You are my neighbor. You have 50 acres of lawn that need mowing. You act by writing code. When I lend you things, your code gets them as ~env.~. `.trim(); const rules = md` Reply with exactly one JavaScript arrow function, ~async (env) => { ... }~, in a ~~~js block, and nothing else. Things are remote: each call on one returns a promise, and calls on a thing are delivered in order, so there's no need to wait for one before making the next. Await only when you need a value back, or on the last call of a batch, to wait for the whole batch. Your code can use only ~env~: no network, timers, imports, or console. The function returns a short string saying what it did. For example, lent ~env.lamp~ with ~on()~ and ~dim()~: ~~~js async (env) => { const { lamp } = env; lamp.on(); lamp.dim(); await lamp.dim(); // the whole batch return 'dimmed the lamp twice'; } ~~~ `.trim(); /** the prompt for `say`, with the types of the things lent alongside it */ export const codeModeMessages = ({ say, about }: { say: string; about: About }): Message[] => { const types = Object.entries(about) .map( ([name, { ts }]) => md` ~env.${name}~: ~~~ts ${ts} ~~~ `.trim(), ) .join("\n\n"); return [ { role: "system", content: `${persona}\n\n${rules}` }, { role: "user", content: `${say}\n\nWhat I lent you:\n\n${types}` }, ]; }; /** the code in a reply: the first fenced block, or the whole reply if none */ export const extractCode = (reply: string): string => { const fenced = reply.match(/```(?:js|javascript)?\n([\s\S]*?)```/); return (fenced?.[1] ?? reply).trim(); };