Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, HTTPException, Depends, Request | |
| from dotenv import load_dotenv | |
| from supabase import Client | |
| import uuid | |
| import logging | |
| from typing import Optional, Dict, Any | |
| from ..schemas.card_schemas import CardGenerateRequest, CardGenerateResponse | |
| from ....core.generator import build_prompt # get_constellation wird hier nicht direkt verwendet | |
| from ....core.card_renderer import generate_card as render_card_sync # Umbenennen für Klarheit | |
| from ....utils.qr_utils import generate_qr_code_sync # Umbenennen für Klarheit | |
| from ....services.database import get_supabase_client, save_card | |
| from ....core.config import settings | |
| from ....core.model_loader import get_generator | |
| from ....core.constraints import generate_with_retry, check_constraints | |
| from ....core.auth import authenticate_request, get_current_user | |
| from ....services.request_logger import RequestEventLogger | |
| from ....services.auth_logger import get_client_info | |
| from fastapi.concurrency import run_in_threadpool # Importieren | |
| # Configure logger for this module | |
| logger = logging.getLogger(__name__) | |
| logger.setLevel(logging.INFO) | |
| # Create console handler if not already configured | |
| if not logger.handlers: | |
| console_handler = logging.StreamHandler() | |
| console_handler.setLevel(logging.INFO) | |
| formatter = logging.Formatter( | |
| '%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
| datefmt='%Y-%m-%d %H:%M:%S' | |
| ) | |
| console_handler.setFormatter(formatter) | |
| logger.addHandler(console_handler) | |
| load_dotenv() | |
| router = APIRouter() | |
| # Asynchrone Wrapper für blockierende Funktionen | |
| async def render_card_async(*args, **kwargs): | |
| return await run_in_threadpool(render_card_sync, *args, **kwargs) | |
| async def generate_qr_code_async(*args, **kwargs): | |
| return await run_in_threadpool(generate_qr_code_sync, *args, **kwargs) | |
| async def generate_endpoint( | |
| request: CardGenerateRequest, | |
| fastapi_request: Request, | |
| supabase: Client = Depends(get_supabase_client), | |
| authenticated: bool = Depends(authenticate_request), | |
| current_user: Optional[Dict[str, Any]] = Depends(get_current_user) | |
| ): | |
| # IMMEDIATE DEBUG LOG - Should appear as soon as endpoint is hit | |
| logger.info(f"🎯 GENERATE ENDPOINT HIT! Terms: {request.terms}") | |
| # Generate unique request ID for tracking | |
| request_id = str(uuid.uuid4()) | |
| # Log to console for HuggingFace visibility | |
| logger.info(f"🚀 [REQUEST START] ID: {request_id}") | |
| logger.info(f"📝 Terms: {request.terms}") | |
| logger.info(f"📅 Date: {request.card_date}") | |
| logger.info(f"🌐 Language: {request.lang}") | |
| logger.info(f"👤 User: {current_user.get('username', 'anonymous') if current_user else 'anonymous'}") | |
| try: | |
| # Extract client information | |
| client_ip, user_agent = get_client_info(fastapi_request) | |
| lang = request.lang or "de" | |
| input_date_str = request.card_date.isoformat() | |
| logger.info(f"🔄 [PROCESSING] Generating card for date: {input_date_str}, lang: {lang}") | |
| # Log user context if available | |
| user_id = current_user["id"] if current_user else None | |
| username = current_user["username"] if current_user else "anonymous" | |
| # Log incoming request | |
| RequestEventLogger.log_card_generation_request( | |
| user_id=user_id, | |
| username=username, | |
| client_ip=client_ip, | |
| user_agent=user_agent, | |
| terms=request.terms, | |
| card_date=input_date_str, | |
| lang=lang, | |
| card_design_id=request.card_design_id_override, | |
| symbol_ids=request.symbol_ids_override, | |
| request_id=request_id | |
| ) | |
| logger.info(f"📝 [PROMPT] Building prompt for LLM...") | |
| card_prompt = build_prompt( | |
| lang=lang, | |
| card_date=input_date_str, | |
| terms=request.terms, | |
| request_id=request_id | |
| ) | |
| logger.info(f"✅ [PROMPT] Built prompt with {len(card_prompt)} characters") | |
| logger.info(f"🤖 [LLM] Loading model and preparing generation...") | |
| llm_pipeline = get_generator() | |
| generation_params = { | |
| "max_new_tokens": settings.GENERATION_MAX_NEW_TOKENS, | |
| "temperature": settings.GENERATION_TEMPERATURE, | |
| "do_sample": settings.GENERATION_DO_SAMPLE, | |
| "top_k": settings.GENERATION_TOP_K, | |
| "top_p": settings.GENERATION_TOP_P, | |
| "return_full_text": False | |
| } | |
| logger.info(f"🎯 [LLM] Starting text generation with {settings.GENERATION_MAX_RETRIES} max retries...") | |
| card_text = await generate_with_retry( | |
| prompt=card_prompt, | |
| generator=llm_pipeline, | |
| terms=request.terms, | |
| max_retries=settings.GENERATION_MAX_RETRIES, | |
| generation_params=generation_params, | |
| request_id=request_id | |
| ) | |
| logger.info(f"📝 [LLM] Generated text: {card_text[:100]}{'...' if len(card_text) > 100 else ''}") | |
| if card_text == "Leider konnte kein gültiger Text erzeugt werden." or not check_constraints(card_text, request.terms): | |
| error_msg = "Kartentext konnte nicht generiert werden oder erfüllt nicht die Bedingungen." | |
| logger.error(f"❌ [ERROR] {error_msg}") | |
| # Log failed card generation | |
| RequestEventLogger.log_card_generation_result( | |
| request_id=request_id, | |
| success=False, | |
| error_message=error_msg | |
| ) | |
| raise HTTPException( | |
| status_code=500, | |
| detail=error_msg | |
| ) | |
| logger.info(f"🎨 [QR] Generating QR code...") | |
| card_id_for_url = str(uuid.uuid4()) | |
| qr_content_url = f"{settings.FRONTEND_BASE_URL}/card/{card_id_for_url}" | |
| logger.info(f"🔗 [QR] QR Code URL: {qr_content_url}") | |
| qr_code_file_id = await generate_qr_code_async( | |
| data=qr_content_url, | |
| output_path=settings.resolved_qr_code_path, | |
| size=settings.QR_CODE_SIZE | |
| ) | |
| qr_code_url = f"{settings.API_PREFIX}/static/images/qr/{qr_code_file_id}.png" | |
| logger.info(f"✅ [QR] QR code generated: {qr_code_file_id}") | |
| logger.info(f"🖼️ [CARD] Rendering card image...") | |
| card_design_id_to_render = request.card_design_id_override or 1 | |
| symbol_ids_to_render = request.symbol_ids_override or [1, 2] | |
| card_file_id = await render_card_async( | |
| card_design_id=card_design_id_to_render, | |
| symbol_ids=symbol_ids_to_render, | |
| text=card_text, | |
| base_images_path=settings.resolved_base_path, | |
| symbols_images_path=settings.resolved_symbols_path, | |
| font_path=settings.resolved_default_font_path, | |
| output_path=settings.resolved_generated_path, | |
| request_id=request_id | |
| ) | |
| logger.info(f"✅ [CARD] Card image generated: {card_file_id}") | |
| logger.info(f"💾 [DB] Saving card to database...") | |
| card_data_for_db = { | |
| "terms": request.terms, | |
| "card_date": input_date_str, | |
| "card_text": card_text, | |
| "image_filename": f"{card_file_id}.png", | |
| "qr_code_filename": f"{qr_code_file_id}.png", | |
| "qr_code_link": qr_content_url, | |
| "session_id": uuid.UUID(card_id_for_url), | |
| "lang": lang, | |
| "prompt_text": card_prompt, | |
| "ml_model_info": llm_pipeline.model.config.to_dict() if hasattr(llm_pipeline, 'model') and hasattr(llm_pipeline.model, 'config') else {"name": str(type(llm_pipeline.model).__name__)}, | |
| "generation_params": generation_params | |
| } | |
| db_response = await save_card(supabase, card_data_for_db) | |
| db_id = None | |
| if db_response and hasattr(db_response, 'data') and db_response.data and len(db_response.data) > 0: | |
| db_id = str(db_response.data[0].get('id')) | |
| elif isinstance(db_response, list) and db_response and isinstance(db_response[0], dict): | |
| db_id = str(db_response[0].get('id')) | |
| # Log successful card generation | |
| RequestEventLogger.log_card_generation_result( | |
| request_id=request_id, | |
| success=True, | |
| card_id=db_id if db_id else card_id_for_url, | |
| card_file_id=card_file_id, | |
| qr_code_file_id=qr_code_file_id, | |
| qr_content_url=qr_content_url | |
| ) | |
| logger.info(f"🎉 [SUCCESS] Card generation completed!") | |
| logger.info(f"📋 Card ID: {db_id if db_id else card_id_for_url}") | |
| logger.info(f"🖼️ Image: {card_file_id}.png") | |
| logger.info(f"📱 QR Code: {qr_code_file_id}.png") | |
| return CardGenerateResponse( | |
| message="Karte erfolgreich generiert.", | |
| card_id=db_id if db_id else card_id_for_url, | |
| qr_code_image_url=qr_code_url | |
| ) | |
| except FileNotFoundError as e: | |
| error_msg = f"Ein benötigtes Template oder eine Datei wurde nicht gefunden: {e.filename}" | |
| logger.error(f"FileNotFoundError in generate_endpoint: {e}") | |
| # Log failed card generation | |
| RequestEventLogger.log_card_generation_result( | |
| request_id=request_id, | |
| success=False, | |
| error_message=error_msg | |
| ) | |
| raise HTTPException(status_code=500, detail=error_msg) | |
| except HTTPException as e: | |
| # Log failed card generation for HTTP exceptions | |
| RequestEventLogger.log_card_generation_result( | |
| request_id=request_id if 'request_id' in locals() else None, | |
| success=False, | |
| error_message=str(e.detail) | |
| ) | |
| raise e | |
| except Exception as e: | |
| error_msg = f"Ein interner Fehler ist aufgetreten: {str(e)}" | |
| import traceback | |
| logger.error(f"Unerwarteter Fehler in generate_endpoint: {type(e).__name__} - {str(e)}") | |
| logger.error(traceback.format_exc()) | |
| # Log failed card generation | |
| RequestEventLogger.log_card_generation_result( | |
| request_id=request_id if 'request_id' in locals() else None, | |
| success=False, | |
| error_message=error_msg | |
| ) | |
| raise HTTPException(status_code=500, detail=error_msg) | |