diff --git a/CHANGELOG.md b/CHANGELOG.md index d41c8f4..b4cab81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to Epicure are documented here. This file is mirrored in-app at `/changelog` (and in the admin dashboard) via `apps/web/lib/changelog.ts` — update both together. +## 0.57.2 — 2026-07-20 20:15 + +### Fixed +- The previous chatbot fix only caught the model spelling a recipe out as prose. The more common case: a short reply that claims a draft exists ("here's a draft, check below") without ever calling the tool, so nothing renders. Now also detects recipe-creation intent from the user's own message and forces the tool call in that case too. + ## 0.57.1 — 2026-07-20 20:00 ### Fixed diff --git a/apps/web/app/api/v1/ai/cooking-chat/route.ts b/apps/web/app/api/v1/ai/cooking-chat/route.ts index fddfff1..1598deb 100644 --- a/apps/web/app/api/v1/ai/cooking-chat/route.ts +++ b/apps/web/app/api/v1/ai/cooking-chat/route.ts @@ -37,6 +37,24 @@ function looksLikeUnstructuredRecipe(text: string): boolean { return listLines.length >= 3; } +// The above only catches the model spelling the whole recipe out. A second, +// more common failure with weaker models: it gives a short reply that even +// *claims* to have drafted something ("here's a draft, check below") without +// ever calling the tool — so nothing renders below it. Rather than guess from +// that claim (fragile across models/phrasing), detect intent from the user's +// own message instead: same noun+verb pattern the system prompt itself uses +// as its createRecipe examples, checked in whichever of the two supported +// locales the request is in. +const RECIPE_INTENT: Record = { + en: { noun: /\brecipe\b/i, verb: /\b(create|make|generate|give|write|save|invent)\b/i }, + fr: { noun: /\brecettes?\b/i, verb: /\b(cr[ée]e?r?|fais|donne|[ée]cri[st]|note[rz]?|sauvegard\w*|enregistr\w*|invente\w*|g[ée]n[èe]re\w*)\b/i }, +}; + +function looksLikeRecipeCreationRequest(question: string, locale: string): boolean { + const pattern = RECIPE_INTENT[locale] ?? RECIPE_INTENT["en"]!; + return pattern.noun.test(question) && pattern.verb.test(question); +} + export async function POST(req: NextRequest) { const { session, response } = await requireSessionOrApiKey(req); if (response) return response; @@ -77,10 +95,14 @@ export async function POST(req: NextRequest) { let final = result.data; // Weaker/free-tier models sometimes ignore the MUST-call-the-tool rule - // above and just write the recipe out in prose instead. Rather than - // surface that (or fail the request), force the tool call on one extra - // pass — same system+prompt, just no longer letting the model opt out. - if (final.toolCalls.length === 0 && looksLikeUnstructuredRecipe(final.text)) { + // above — either by spelling the recipe out in prose, or (more common, + // and easy to miss) giving a short reply that *claims* a draft exists + // without ever calling the tool, so nothing actually renders below it. + // Catch both: the prose-list shape, or the user's own message clearly + // asking for a recipe in the first place. Rather than surface either + // failure, force the tool call on one extra pass — same system+prompt, + // just no longer letting the model opt out. + if (final.toolCalls.length === 0 && (looksLikeUnstructuredRecipe(final.text) || looksLikeRecipeCreationRequest(parsed.data.question, locale))) { try { const forced = await generateText({ model, diff --git a/apps/web/lib/changelog.ts b/apps/web/lib/changelog.ts index 12ceb4a..a0c8be7 100644 --- a/apps/web/lib/changelog.ts +++ b/apps/web/lib/changelog.ts @@ -1,5 +1,5 @@ // Mirrors CHANGELOG.md at the repo root — update both together. -export const APP_VERSION = "0.57.1"; +export const APP_VERSION = "0.57.2"; export type ChangelogEntry = { version: string; @@ -11,6 +11,13 @@ export type ChangelogEntry = { }; export const CHANGELOG: ChangelogEntry[] = [ + { + version: "0.57.2", + date: "2026-07-20 20:15", + fixed: [ + "The previous chatbot fix only caught the model spelling a recipe out as prose. The more common case: a short reply that claims a draft exists (\"here's a draft, check below\") without ever calling the tool, so nothing renders. Now also detects recipe-creation intent from the user's own message and forces the tool call in that case too.", + ], + }, { version: "0.57.1", date: "2026-07-20 20:00", diff --git a/apps/web/package.json b/apps/web/package.json index bd42f74..bb27401 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@epicure/web", - "version": "0.57.1", + "version": "0.57.2", "private": true, "scripts": { "dev": "next dev", diff --git a/package.json b/package.json index e8a5f20..e03ea19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "epicure", - "version": "0.57.1", + "version": "0.57.2", "private": true, "scripts": { "dev": "pnpm --filter web dev",