Introcuced a Free LLM for easier food input

This commit is contained in:
2026-07-30 09:06:25 +02:00
parent 5196ff0194
commit c40a142da2
8 changed files with 272 additions and 7 deletions
+45 -1
View File
@@ -2,17 +2,36 @@ from uuid import uuid4
import httpx
from fastapi import APIRouter, HTTPException, Query, status
from pydantic import ValidationError
from sqlmodel import select
from app import llm
from app.access import product_barcode_accessible
from app.deps import CurrentUserDep, SessionDep
from app.models import FoodLog, Product
from app.schemas import ProductCreate, ProductRead, ProductSearchResult
from app.schemas import (
NutritionEstimateRequest,
NutritionEstimateResponse,
ProductCreate,
ProductRead,
ProductSearchResult,
)
router = APIRouter(prefix="/api/products", tags=["products"])
OFF_API_URL = "https://world.openfoodfacts.org/api/v2/product/{barcode}.json"
ESTIMATE_SYSTEM_PROMPT = """Du bist ein Ernährungsexperte. Schätze die Nährwerte des vom Nutzer beschriebenen Lebensmittels oder Gerichts.
Antworte AUSSCHLIESSLICH mit einem JSON-Objekt in genau diesem Format:
{"name": "kurzer Produktname", "calories": 250.0, "carbs": 30.0, "protein": 12.0, "fat": 9.0, "sugar": 3.0, "fiber": 2.0, "saturated_fat": 3.5, "salt": 1.2, "portion_g": 350.0}
Regeln:
- Alle Nährwerte beziehen sich auf 100 g (calories in kcal, alles andere in Gramm).
- portion_g ist die geschätzte übliche Portionsgröße der Beschreibung in Gramm.
- name ist ein kurzer, gut lesbarer deutscher Produktname ohne Mengenangabe.
- Nutze realistische Durchschnittswerte. Keine Erklärungen, kein Text außerhalb des JSON."""
def _extract_macros(nutriments: dict) -> dict:
calories = nutriments.get("energy-kcal_100g")
@@ -71,6 +90,31 @@ def create_product(product_in: ProductCreate, current_user: CurrentUserDep, sess
return product
@router.post("/estimate", response_model=NutritionEstimateResponse)
def estimate_nutrition(estimate_in: NutritionEstimateRequest, current_user: CurrentUserDep):
if not llm.is_configured():
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="KI-Schätzung ist auf diesem Server nicht konfiguriert",
)
try:
data = llm.complete_json(ESTIMATE_SYSTEM_PROMPT, estimate_in.description)
except llm.LLMUnavailableError:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="KI-Schätzung gerade nicht erreichbar. Bitte später erneut versuchen.",
)
try:
return NutritionEstimateResponse(**data)
except (ValidationError, TypeError):
raise HTTPException(
status_code=status.HTTP_502_BAD_GATEWAY,
detail="KI-Antwort war unbrauchbar. Bitte erneut versuchen oder Werte manuell eintragen.",
)
@router.get("/search", response_model=list[ProductSearchResult])
def search_products(
current_user: CurrentUserDep,