AI_summarizer / app.py
Pratyush
new updated model
13b74a3
"""
Recipe Processing API for Hugging Face
This is the main application file for the recipe processing and product matching
system. It provides a FastAPI application that can be deployed on Hugging Face Spaces.
"""
import os
import json
import logging
from fastapi import FastAPI, HTTPException, File, UploadFile, Form, Body
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any, List
from recipe_processor import RecipeProcessor
from product_retriever_improved import ProductRetriever
import uvicorn
# Set up logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Create FastAPI app
app = FastAPI(
title="Recipe Processing API",
description="API for processing recipes and matching ingredients with products",
version="1.0.0",
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allow all origins
allow_credentials=True,
allow_methods=["*"], # Allow all methods
allow_headers=["*"], # Allow all headers
)
# Define API models
class ProcessRecipeRequest(BaseModel):
recipe_data: Dict[str, Any] = Field(..., description="Recipe data to process")
woolworths_api_key: Optional[str] = Field(None, description="Woolworths API key")
coles_api_key: Optional[str] = Field(None, description="Coles API key")
summarize: bool = Field(True, description="Whether to summarize recipe text")
add_products: bool = Field(True, description="Whether to add product matches")
class SupabaseSettings(BaseModel):
supabase_url: str = Field(..., description="Supabase URL")
supabase_key: str = Field(..., description="Supabase API key")
class ProcessMealPlanRequest(BaseModel):
supabase_url: str = Field(..., description="Supabase URL")
supabase_key: str = Field(..., description="Supabase API key")
session_id: Optional[str] = Field(None, description="Session ID (optional, if None gets latest)")
woolworths_api_key: Optional[str] = Field(None, description="Woolworths API key")
coles_api_key: Optional[str] = Field(None, description="Coles API key")
summarize: bool = Field(True, description="Whether to summarize recipe text")
add_products: bool = Field(True, description="Whether to add product matches")
# API endpoints
@app.get("/")
async def root():
"""API root endpoint"""
return {
"message": "Recipe Processing API is running",
"endpoints": [
"/api/process_recipe",
"/api/process_meal_plan",
"/api/get_meal_plans"
]
}
@app.post("/api/process_recipe")
async def process_recipe(request: ProcessRecipeRequest):
"""
Process a recipe and optionally add product matches
"""
try:
# Initialize the recipe processor
recipe_processor = RecipeProcessor()
# Process the recipe
processed_recipe = recipe_processor.process_recipe(
request.recipe_data,
summarize=request.summarize
)
# Add product matches if requested
if request.add_products and (request.woolworths_api_key or request.coles_api_key):
# Initialize product retriever
product_retriever = ProductRetriever(
woolworths_api_key=request.woolworths_api_key,
coles_api_key=request.coles_api_key
)
# Add product matches
processed_recipe = product_retriever.add_product_matches_to_recipe(processed_recipe)
return processed_recipe
except Exception as e:
logging.error(f"Error processing recipe: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/process_meal_plan")
async def process_meal_plan(request: ProcessMealPlanRequest):
"""
Process a meal plan from Supabase and optionally add product matches
"""
try:
# Import Supabase client
try:
from supabase import create_client
except ImportError:
raise HTTPException(status_code=500, detail="Supabase library not installed")
# Initialize Supabase client
supabase = create_client(request.supabase_url, request.supabase_key)
# Get meal plan
if request.session_id:
response = supabase.table("meal_plans").select("*").eq("id", request.session_id).execute()
else:
# Get the latest meal plan
response = supabase.table("meal_plans").select("*").order("created_at", desc=True).limit(1).execute()
if not response.data:
error_msg = "No meal plan found" + (f" for session {request.session_id}" if request.session_id else "")
raise HTTPException(status_code=404, detail=error_msg)
# Get the meal plan data
meal_plan = response.data[0]
session_id = meal_plan.get("id", "")
# Initialize recipe processor
recipe_processor = RecipeProcessor()
# Create result structure
result = {
"session_id": session_id,
"processed": {}
}
# Process each meal type
for meal_type in ["breakfast", "lunch", "dinner"]:
if meal_type not in meal_plan:
continue
meal_data = meal_plan[meal_type]
# Parse JSON if it's a string
if isinstance(meal_data, str):
try:
meal_data = json.loads(meal_data)
except json.JSONDecodeError:
logging.error(f"Invalid JSON in {meal_type}")
continue
# Process this meal
processed_meal = recipe_processor.process_recipe(
meal_data,
summarize=request.summarize
)
# Add to result
result["processed"][meal_type] = processed_meal
# Add product matches if requested
if request.add_products and (request.woolworths_api_key or request.coles_api_key):
# Initialize product retriever
product_retriever = ProductRetriever(
woolworths_api_key=request.woolworths_api_key,
coles_api_key=request.coles_api_key
)
# Add product matches
result = product_retriever.add_product_matches_to_meal_plan(result)
# Generate combined result
combined_result = {}
meal_plans = {}
for meal_type, meal_data in result["processed"].items():
# Extract the data we need
meal_result = {
'recipe': meal_data.get('recipe', ''),
'summarized_recipe': ''
}
# Get summarized recipe (combine description and instructions)
processed = meal_data.get('processed', {})
description = processed.get('description', {}).get('summarized', '')
instructions = processed.get('instructions', {}).get('summarized', '')
if description or instructions:
meal_result['summarized_recipe'] = f"{description}\n\n{instructions}".strip()
# Get ingredients
meal_result['ingredients'] = processed.get('ingredients', [])
# Add to meal plans
meal_plans[meal_type] = meal_result
# Add to result
combined_result['meal_plans'] = meal_plans
combined_result['session_id'] = session_id
return combined_result
except Exception as e:
logging.error(f"Error processing meal plan: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/get_meal_plans")
async def get_meal_plans(settings: SupabaseSettings, limit: int = 10):
"""Get list of available meal plans from Supabase"""
try:
# Import Supabase client
try:
from supabase import create_client
except ImportError:
raise HTTPException(status_code=500, detail="Supabase library not installed")
# Initialize Supabase client
supabase = create_client(settings.supabase_url, settings.supabase_key)
# Get meal plans, most recent first
response = supabase.table("meal_plans").select("id,created_at").order("created_at", desc=True).limit(
limit).execute()
if not response.data:
return {"meal_plans": []}
return {"meal_plans": response.data}
except Exception as e:
logging.error(f"Error getting meal plans: {e}")
raise HTTPException(status_code=500, detail=str(e))
# Main function to run the server directly (for development)
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860)) # Hugging Face Spaces uses port 7860
uvicorn.run("app:app", host="0.0.0.0", port=port)