diff --git a/good-fences/src/model.ts b/good-fences/src/model.ts index 739fee9..58885de 100644 --- a/good-fences/src/model.ts +++ b/good-fences/src/model.ts @@ -3,14 +3,24 @@ /** a Workers AI text-generation model with no paid-plan requirement */ export const MODEL = "@cf/openai/gpt-oss-120b"; +/** room for a reasoning model to think and then answer; the default, 256, + * runs out mid-thought (`finish_reason: length`) */ +const MAX_TOKENS = 4096; + 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; + const { choices } = (output ?? {}) as { + choices?: { message?: { content?: unknown }; finish_reason?: unknown }[]; + }; + const [choice] = choices ?? []; + const text = choice?.message?.content; if (typeof text !== "string") { - throw Error(`no reply text in model output with keys: ${Object.keys(output ?? {}).join(", ")}`); + const where = choice + ? `choice with finish_reason ${String(choice.finish_reason)} and message keys: ${Object.keys(choice.message ?? {}).join(", ")}` + : `model output with keys: ${Object.keys(output ?? {}).join(", ")}`; + throw Error(`no reply text in ${where}`); } return text; }; @@ -18,7 +28,7 @@ const replyText = (output: unknown): string => { /** `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] }); + const output = await ai.run(model as Parameters[0], { messages: [...messages], max_tokens: MAX_TOKENS }); return replyText(output); }, });