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 |