summaryrefslogtreecommitdiff
path: root/services/ai_service.py
diff options
context:
space:
mode:
Diffstat (limited to 'services/ai_service.py')
-rw-r--r--services/ai_service.py73
1 files changed, 42 insertions, 31 deletions
diff --git a/services/ai_service.py b/services/ai_service.py
index 4b54c15..643a9a2 100644
--- a/services/ai_service.py
+++ b/services/ai_service.py
@@ -30,17 +30,17 @@ async def _chat(messages: list) -> dict:
async def generate_weekly_menu(pantry_context: dict, user_notes: str | None = None) -> dict:
"""Suggest 8-12 diverse recipes for the week, prioritizing expiring pantry items."""
- user_message = f"""Given my current pantry and recent meal history, suggest 8-12 diverse recipes I could make this week.
+ user_message = f"""Plan 8-12 diverse recipes for this week that make the most of what I already have.
PANTRY STATE:
{json.dumps(pantry_context, indent=2)}
-GUIDELINES:
-- Suggest genuinely good, practical dishes — do NOT limit yourself to only pantry ingredients
-- Use pantry ingredients where it makes sense, but freely include recipes that require buying additional items
-- Avoid meals eaten in the last 7 days (see recent_meals)
-- Include a mix of breakfast, lunch, and dinner options
-- Keep each recipe under 60 minutes
+REQUIREMENTS:
+- BUILD recipes around pantry ingredients — most of each recipe should use what I already have
+- Each recipe may call for AT MOST 4-5 ingredients not in my pantry (items I'd pick up at the store)
+- Do not repeat any meal eaten in the last 7 days (see recent_meals)
+- Cover a mix of breakfast, lunch, and dinner
+- Every recipe must be completable in under 60 minutes
- Include full ingredient lists with quantities and units
Respond ONLY with this JSON structure:
@@ -49,15 +49,17 @@ Respond ONLY with this JSON structure:
{{
"name": "...",
"meal_type": "breakfast",
- "ingredients": [{{"name": "...", "quantity": 1.0, "unit": "cup"}}],
+ "ingredients": [{{"name": "...", "quantity": 1.0, "unit": "cup", "in_pantry": true}}],
"description": "1-2 sentence description of the dish's flavor and appeal",
"instructions": "1. ... 2. ... (4-8 numbered steps, 1-2 sentences each)",
"time_minutes": 20,
"serves": 2
}}
],
- "notes": "brief explanation of choices"
-}}"""
+ "notes": "brief explanation of choices and what extra ingredients are needed"
+}}
+
+Set `in_pantry: true` for each ingredient that matches something in the pantry, `false` for items that need to be purchased."""
if user_notes:
user_message += f"\n\nUSER PREFERENCES:\n{user_notes}"
@@ -72,20 +74,21 @@ Respond ONLY with this JSON structure:
async def generate_grocery_list(recipes: list, pantry_context: dict, user_notes: str | None = None) -> dict:
"""Generate a minimal grocery list based on a list of recipes and current pantry."""
- user_message = f"""Given these recipes and current pantry, generate a minimal grocery list.
+ user_message = f"""Generate the grocery list of ONLY the items I need to buy to cook all these recipes.
RECIPES:
{json.dumps(recipes, indent=2)}
-CURRENT PANTRY:
+PANTRY (I already have these — DO NOT include them in the grocery list):
{json.dumps(pantry_context["available_ingredients"], indent=2)}
RULES:
-- Only list ingredients NOT already sufficiently in the pantry
-- Consolidate duplicate ingredients across recipes (buy once)
-- Group items by store section (produce, dairy, protein, pantry, frozen, bakery)
-- Estimate realistic retail costs in USD
-- Prefer bulk when ingredient appears in 3+ recipes
+- For every ingredient in every recipe, cross-check against the pantry. If the pantry has it → SKIP IT. If it's not in the pantry → include it.
+- The goal is a minimal, accurate list of ONLY what is missing — do not list anything already stocked.
+- Consolidate the same ingredient appearing across multiple recipes into one entry (sum the quantities).
+- Group items by store section: produce, dairy, protein, pantry, frozen, bakery.
+- Estimate realistic retail costs in USD.
+- If an ingredient appears in 3+ recipes, note that bulk purchase may save money.
Respond ONLY with this JSON:
{{
@@ -117,16 +120,18 @@ Respond ONLY with this JSON:
async def generate_grocery_list_from_pantry(pantry_context: dict, user_notes: str | None = None) -> dict:
"""Generate a grocery list based on pantry state alone, without a menu plan."""
- user_message = f"""Given my current pantry, generate a practical grocery list to stock up for the week.
+ user_message = f"""Generate a solid weekly grocery list to set me up for a full week of cooking.
CURRENT PANTRY:
{json.dumps(pantry_context["available_ingredients"], indent=2)}
RULES:
-- Suggest ingredients that would complement what's already in the pantry
-- Group items by store section (produce, dairy, protein, pantry, frozen, bakery)
-- Estimate realistic retail costs in USD
-- Focus on versatile staples that enable multiple meals
+- If the pantry is empty or nearly empty, suggest a complete foundational shopping list: proteins, produce, dairy, grains, and pantry staples for a week of balanced meals.
+- If the pantry has some items, build around what's there — prioritize ingredients that complement existing stock and fill obvious gaps.
+- Do NOT list items already in the pantry.
+- Aim for variety: at least 2 proteins, a range of vegetables and fruits, a grain, and pantry staples.
+- Group items by store section: produce, dairy, protein, pantry, frozen, bakery.
+- Estimate realistic retail costs in USD.
Respond ONLY with this JSON:
{{
@@ -138,7 +143,7 @@ Respond ONLY with this JSON:
"store_section": "produce",
"estimated_cost": 3.50,
"used_in_meals": [],
- "reason": "versatile staple"
+ "reason": "weekly staple / fills pantry gap"
}}
],
"total_estimate": 45.00,
@@ -194,8 +199,8 @@ ALREADY IN PLAN (do not suggest these):
{json.dumps(existing_names, indent=2)}
GUIDELINES:
-- Suggest a genuinely good, practical {meal_type} dish
-- Do NOT limit yourself to only pantry ingredients
+- Prefer a recipe that uses pantry ingredients as much as possible — only a few new items to buy
+- Make it a genuinely good, practical {meal_type} dish
- Under 60 minutes
- Must not be any of the dishes already in the plan
@@ -239,9 +244,9 @@ async def get_available_models() -> list:
async def chat_with_commis(message: str, history: list, pantry_context: dict, menu_context: list, grocery_context: list) -> str:
"""Have a conversation with Commis using full kitchen context."""
- system_prompt = f"""You are Commis, a friendly and knowledgeable personal chef assistant. You have full visibility into the user's kitchen — their pantry, this week's recipe suggestions, and their grocery list. Use this context to give personalized, practical cooking advice.
+ system_prompt = f"""You are Commis, the user's personal chef assistant. You have real-time visibility into their kitchen — pantry, this week's planned recipes, grocery list, and recent meal history. THIS context is your primary source of truth when answering cooking questions.
-PANTRY (ingredients on hand):
+PANTRY (ingredients currently on hand):
{json.dumps(pantry_context.get("available_ingredients", []), indent=2)}
EXPIRING SOON:
@@ -250,13 +255,19 @@ EXPIRING SOON:
RECENT MEALS (last 14 days):
{json.dumps(pantry_context.get("recent_meals", []), indent=2)}
-THIS WEEK'S RECIPE SUGGESTIONS:
+THIS WEEK'S PLANNED RECIPES:
{json.dumps(menu_context, indent=2)}
-GROCERY LIST:
+GROCERY LIST (items to buy):
{json.dumps(grocery_context, indent=2)}
-Be conversational, helpful, and concise. Reference specific ingredients and recipes by name when relevant. You do not need to respond with JSON."""
+HOW TO RESPOND:
+- When the user asks what to cook, suggest from THIS WEEK'S PLANNED RECIPES first — they were chosen for the pantry.
+- When the user asks about an ingredient, reference whether it's actually in their pantry.
+- When the user asks about substitutions, check if a pantry item can stand in before suggesting something to buy.
+- When the user asks about shopping, reference the grocery list.
+- Be concise, specific, and practical. Reference actual recipe names and ingredient names from the data above.
+- You do not need to respond with JSON."""
messages = [{"role": "system", "content": system_prompt}]
messages.extend(history)
@@ -306,7 +317,7 @@ ALREADY IN PLAN (do not suggest these):
GUIDELINES:
- Match the description as closely as possible
-- Do NOT limit yourself to only pantry ingredients
+- Prefer ingredients already in the pantry — only a few new items to buy if needed
- Under 60 minutes
- Must not be any dish already in the plan