diff --git a/agent/task_worker.py b/agent/task_worker.py index b43566b..77c0bb7 100644 --- a/agent/task_worker.py +++ b/agent/task_worker.py @@ -10,6 +10,7 @@ import asyncio import json import logging import os +import re from datetime import datetime, timezone logger = logging.getLogger("voice-agent.tasks") @@ -102,7 +103,11 @@ class TaskWorker: if not text: reg.fail(self.task_id, "LLM returned empty response") return - if self._is_deferred_answer(text) or self._is_underfilled_news_answer(text): + if ( + self._is_deferred_answer(text) + or self._is_underfilled_news_answer(text) + or self._is_underfilled_recipe_answer(text) + ): reg.add_step( self.task_id, "thinking", @@ -171,6 +176,24 @@ class TaskWorker: ) return any(phrase in lowered for phrase in generic_phrases) + def _is_underfilled_recipe_answer(self, text: str) -> bool: + """A recipe is not usable without quantities and a method.""" + if "recipe" not in self.description.lower(): + return False + lowered = text.lower() + has_quantity = bool( + re.search( + r"\b(?:\d+(?:/\d+)?|one|two|three|half|quarter)\s*" + r"(?:cups?|tablespoons?|tbsp|teaspoons?|tsp|sticks?|ounces?|oz|grams?|g)\b", + lowered, + ) + ) + has_method = any( + verb in lowered + for verb in ("beat ", "mix ", "whip ", "stir ", "blend ", "combine ") + ) + return not (has_quantity and has_method) + def _system_prompt(self) -> str: today = datetime.now(timezone.utc).strftime("%B %d, %Y") return ( @@ -184,7 +207,8 @@ class TaskWorker: "distinct concrete events unless the user requests a different count. For three " "headlines, write exactly three sentences with one event per sentence and no extras. Never pad the " "answer with generic phrases about broad coverage or international perspectives, and " - "respect any requested item count exactly. Only " + "respect any requested item count exactly. For recipes, include specific ingredient " + "quantities and a concise preparation method so the result is actually usable. Only " "produce a final answer once it fulfills the original request. The final answer must " "be 1-3 concise sentences suitable for speaking aloud, with plain text only and no " "markdown or formatting." diff --git a/tests/test_background_tasks.py b/tests/test_background_tasks.py index 36ad530..b790d6a 100644 --- a/tests/test_background_tasks.py +++ b/tests/test_background_tasks.py @@ -65,6 +65,14 @@ def test_deferred_task_answers_are_rejected(): "Nepal began flood recovery, Venezuela signed an oil agreement, and Iran resumed talks." ) + recipe_worker = TaskWorker("test", "Give me a buttercream recipe") + assert recipe_worker._is_underfilled_recipe_answer( + "Use butter, powdered sugar, vanilla, and milk, then mix until smooth." + ) + assert not recipe_worker._is_underfilled_recipe_answer( + "Beat one cup butter with three cups powdered sugar and two tablespoons milk until smooth." + ) + def test_task_event_directories_are_isolated(tmp_path): first = TaskRegistry(tmp_path / "room-one")