Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| import json | |
| import os | |
| import asyncio | |
| import aiohttp | |
| from typing import List, Optional, Literal | |
| import time | |
| import logging | |
| import re | |
| # Setup logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| app = FastAPI( | |
| title="SoActi AI Quiz API", | |
| description="Separat AI-tjeneste for quiz-generering som SoActi kan bruke", | |
| version="1.0.0" | |
| ) | |
| # Request/Response models | |
| class QuizRequest(BaseModel): | |
| tema: str | |
| språk: Literal["no", "en"] = "no" | |
| antall_spørsmål: int = 5 | |
| type: Literal["sted", "rute"] = "sted" | |
| vanskelighetsgrad: int = 3 | |
| class QuizQuestion(BaseModel): | |
| spørsmål: str | |
| alternativer: List[str] | |
| korrekt_svar: int | |
| forklaring: str | |
| class QuizResponse(BaseModel): | |
| success: bool | |
| questions: List[QuizQuestion] | |
| metadata: dict | |
| message: str | |
| class HealthResponse(BaseModel): | |
| status: str | |
| models_loaded: bool | |
| api_key_configured: bool | |
| uptime_seconds: float | |
| # Global state | |
| start_time = time.time() | |
| api_key = os.getenv("HUGGINGFACE_API_KEY") | |
| async def root(): | |
| """Root endpoint med API-informasjon""" | |
| return { | |
| "service": "SoActi AI Quiz API", | |
| "version": "1.0.0", | |
| "status": "running", | |
| "endpoints": { | |
| "generate": "/generate-quiz", | |
| "health": "/health", | |
| "models": "/models", | |
| "docs": "/docs" | |
| }, | |
| "description": "Separat AI-tjeneste for quiz-generering" | |
| } | |
| async def health_check(): | |
| """Helsesjekk for tjenesten""" | |
| uptime = time.time() - start_time | |
| return HealthResponse( | |
| status="healthy", | |
| models_loaded=True, | |
| api_key_configured=bool(api_key), | |
| uptime_seconds=uptime | |
| ) | |
| async def get_available_models(): | |
| """Liste over tilgjengelige AI-modeller""" | |
| return { | |
| "norwegian_models": [ | |
| { | |
| "name": "NbAiLab/nb-gpt-j-6B", | |
| "description": "Beste for norsk - Nasjonalbiblioteket", | |
| "cost": "Gratis", | |
| "quality": "Høy (norsk)" | |
| } | |
| ], | |
| "english_models": [ | |
| { | |
| "name": "meta-llama/Llama-2-70b-chat-hf", | |
| "description": "Beste kvalitet - Premium", | |
| "cost": "Premium ($$$)", | |
| "quality": "Exceptional" | |
| }, | |
| { | |
| "name": "mistralai/Mistral-7B-Instruct-v0.1", | |
| "description": "Balansert kvalitet/kostnad", | |
| "cost": "Premium ($$)", | |
| "quality": "Meget høy" | |
| } | |
| ], | |
| "fallback_models": [ | |
| { | |
| "name": "google/flan-t5-small", | |
| "description": "Gratis fallback", | |
| "cost": "Gratis", | |
| "quality": "Middels" | |
| } | |
| ] | |
| } | |
| async def generate_quiz(request: QuizRequest): | |
| """Hovedendepunkt for quiz-generering""" | |
| try: | |
| logger.info(f"Genererer quiz for tema: {request.tema} ({request.språk})") | |
| # Valider request | |
| if not request.tema.strip(): | |
| raise HTTPException(status_code=400, detail="Tema kan ikke være tomt") | |
| if request.antall_spørsmål < 1 or request.antall_spørsmål > 20: | |
| raise HTTPException(status_code=400, detail="Antall spørsmål må være mellom 1 og 20") | |
| # Generer quiz | |
| from quiz_generator import QuizGenerator | |
| generator = QuizGenerator(api_key) | |
| start_time = time.time() | |
| questions = await generator.generate_quiz(request) | |
| generation_time = time.time() - start_time | |
| if not questions: | |
| raise HTTPException(status_code=500, detail="Kunne ikke generere spørsmål") | |
| logger.info(f"Genererte {len(questions)} spørsmål på {generation_time:.2f}s") | |
| return QuizResponse( | |
| success=True, | |
| questions=questions, | |
| metadata={ | |
| "generation_time": round(generation_time, 2), | |
| "model_used": generator.get_model_used(), | |
| "method": generator.get_generation_method(), | |
| "tema": request.tema, | |
| "språk": request.språk | |
| }, | |
| message=f"Genererte {len(questions)} spørsmål om '{request.tema}'" | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Feil ved quiz-generering: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Intern feil: {str(e)}") | |
| async def validate_quiz(questions: List[QuizQuestion]): | |
| """Valider genererte quiz-spørsmål""" | |
| try: | |
| from quiz_validator import QuizValidator | |
| validator = QuizValidator() | |
| results = [] | |
| for i, question in enumerate(questions): | |
| validation = validator.validate_question(question.dict()) | |
| results.append({ | |
| "question_index": i, | |
| "valid": validation["valid"], | |
| "score": validation["score"], | |
| "issues": validation["issues"] | |
| }) | |
| overall_score = sum(r["score"] for r in results) / len(results) | |
| return { | |
| "overall_valid": all(r["valid"] for r in results), | |
| "overall_score": round(overall_score, 2), | |
| "question_results": results | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Valideringsfeil: {str(e)}") | |
| # Gradio interface for testing | |
| def gradio_generate_quiz(tema, språk, antall, type_val, vanskelighet): | |
| """Gradio wrapper for quiz generation""" | |
| try: | |
| import asyncio | |
| request = QuizRequest( | |
| tema=tema, | |
| språk=språk, | |
| antall_spørsmål=antall, | |
| type=type_val, | |
| vanskelighetsgrad=vanskelighet | |
| ) | |
| # Run async function in sync context | |
| loop = asyncio.new_event_loop() | |
| asyncio.set_event_loop(loop) | |
| response = loop.run_until_complete(generate_quiz(request)) | |
| loop.close() | |
| # Format for display | |
| output = f"✅ {response.message}\n\n" | |
| output += f"🤖 Modell: {response.metadata.get('model_used', 'Ukjent')}\n" | |
| output += f"⏱️ Tid: {response.metadata.get('generation_time', 0)}s\n" | |
| output += f"🔧 Metode: {response.metadata.get('method', 'Ukjent')}\n\n" | |
| for i, q in enumerate(response.questions, 1): | |
| output += f"📝 **Spørsmål {i}:** {q.spørsmål}\n" | |
| for j, alt in enumerate(q.alternativer): | |
| marker = "✅" if j == q.korrekt_svar else "❌" | |
| output += f" {chr(65+j)}) {alt} {marker}\n" | |
| output += f"💡 **Forklaring:** {q.forklaring}\n\n" | |
| return output | |
| except Exception as e: | |
| retur |