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) @router.post("/generate", response_model=CardGenerateResponse) 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)