fix: catch the more common tool-skip case — short reply, no tool call (v0.57.2)
The previous fix's looksLikeUnstructuredRecipe only fires when the model
spells the whole recipe out as a numbered/bulleted list — it does nothing
for a short reply that just *claims* a draft exists ("Voici une recette
de Bœuf Bourguignon... brouillon à vérifier ci-dessous") without ever
calling the tool, which is what the user actually hit.
Detecting the model's claimed compliance is fragile (varies by phrasing/
model), so instead detect intent from the user's own message — the same
noun+verb pattern ("recipe"+create/make/... or "recette"+créer/fais/...)
the system prompt's own createRecipe examples already describe. Forces
the retry whenever there's no tool call AND either signal fires.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string, { noun: RegExp; verb: RegExp }> = {
|
||||
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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@epicure/web",
|
||||
"version": "0.57.1",
|
||||
"version": "0.57.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
Reference in New Issue
Block a user