From ec24f3e8e410087b1c442bdd6a8166a69d4595bb Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 8 Oct 2026 23:41:18 -0500 Subject: [PATCH] feat(good-fences): complete(messages) on a Workers AI model makeWorkersAiModel narrows the AI binding to one model's chat completion: the harness gets `complete`, not `run` on any model at any cost. gpt-oss-120b replies in the OpenAI shape (checked with a one-off probe), so the text is choices[0].message.content; anything else throws, naming the keys it got. Green: the model counts 3 rs in strawberry. Co-Authored-By: Claude Opus 5.5 (1M context) --- good-fences/src/model.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 good-fences/src/model.ts diff --git a/good-fences/src/model.ts b/good-fences/src/model.ts new file mode 100644 index 0000000..739fee9 --- /dev/null +++ b/good-fences/src/model.ts @@ -0,0 +1,24 @@ +/** @file The model behind `complete(messages)`: Workers AI, through its binding. */ + +/** a Workers AI text-generation model with no paid-plan requirement */ +export const MODEL = "@cf/openai/gpt-oss-120b"; + +export type Message = Readonly<{ role: "system" | "user" | "assistant"; content: string }>; + +/** the reply text from an OpenAI-style chat completion */ +const replyText = (output: unknown): string => { + const { choices } = (output ?? {}) as { choices?: { message?: { content?: unknown } }[] }; + const text = choices?.[0]?.message?.content; + if (typeof text !== "string") { + throw Error(`no reply text in model output with keys: ${Object.keys(output ?? {}).join(", ")}`); + } + return text; +}; + +/** `complete(messages)` on a Workers AI model; `ai` is the AI binding */ +export const makeWorkersAiModel = (ai: Ai, model: string) => ({ + async complete(messages: readonly Message[]): Promise { + const output = await ai.run(model as Parameters[0], { messages: [...messages] }); + return replyText(output); + }, +});