Soacti's picture
Update app.py
6e5d7fd verified
import gradio as gr
import json
import time
import os
from typing import List, Dict, Any, Optional
import random
import requests
# API key validation
def validate_api_key(api_key: str) -> bool:
"""Validate the API key against the stored secret"""
expected_key = os.environ.get("SOACTI_API_KEY")
if not expected_key:
print("WARNING: SOACTI_API_KEY not set in environment variables")
return False
return api_key == expected_key
# Improved AI Quiz generation
class AIQuizGenerator:
def __init__(self):
self.api_key = os.environ.get("HUGGINGFACE_API_KEY")
self.api_url = "https://api-inference.huggingface.co/models/microsoft/DialoGPT-large"
# Backup models to try
self.models = [
"microsoft/DialoGPT-large",
"google/flan-t5-large",
"facebook/blenderbot-400M-distill",
"microsoft/DialoGPT-medium"
]
print(f"AI Generator initialized. API key available: {bool(self.api_key)}")
def generate_quiz(self, tema: str, antall: int = 3, språk: str = "no") -> List[Dict[str, Any]]:
"""Generate quiz questions using Hugging Face Inference API"""
if not self.api_key:
print("❌ No Hugging Face API key - using enhanced fallback")
return self._generate_enhanced_fallback(tema, antall)
# Try multiple models until one works
for model in self.models:
try:
print(f"🤖 Trying model: {model}")
questions = self._try_model(model, tema, antall, språk)
if questions and len(questions) > 0:
print(f"✅ Success with model: {model}")
return questions
except Exception as e:
print(f"❌ Model {model} failed: {str(e)}")
continue
print("❌ All AI models failed - using enhanced fallback")
return self._generate_enhanced_fallback(tema, antall)
def _try_model(self, model: str, tema: str, antall: int, språk: str) -> List[Dict[str, Any]]:
"""Try a specific model"""
# Create a very specific prompt
prompt = self._create_specific_prompt(tema, antall, språk)
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
payload = {
"inputs": prompt,
"parameters": {
"max_new_tokens": 800,
"temperature": 0.7,
"do_sample": True,
"top_p": 0.9
}
}
api_url = f"https://api-inference.huggingface.co/models/{model}"
start_time = time.time()
response = requests.post(api_url, headers=headers, json=payload, timeout=30)
generation_time = time.time() - start_time
print(f"API Response Status: {response.status_code}")
if response.status_code != 200:
raise Exception(f"API returned {response.status_code}: {response.text}")
result = response.json()
if isinstance(result, list) and len(result) > 0:
generated_text = result[0].get("generated_text", "")
else:
generated_text = str(result)
print(f"Generated text preview: {generated_text[:200]}...")
# Parse the response
questions = self._parse_ai_response(generated_text, tema, antall)
# Add metadata
for q in questions:
q["_metadata"] = {
"model": model,
"generation_time": generation_time,
"ai_generated": True
}
return questions
def _create_specific_prompt(self, tema: str, antall: int, språk: str) -> str:
"""Create a very specific prompt for better results"""
if språk == "no":
return f"""Lag {antall} quiz-spørsmål om {tema} på norsk.
Format:
SPØRSMÅL: [konkret spørsmål om {tema}]
A) [første alternativ]
B) [andre alternativ]
C) [tredje alternativ]
D) [fjerde alternativ]
SVAR: [A, B, C eller D]
FORKLARING: [kort forklaring]
Eksempel om fotball:
SPØRSMÅL: Hvem vant Ballon d'Or i 2023?
A) Lionel Messi
B) Erling Haaland
C) Kylian Mbappé
D) Karim Benzema
SVAR: A
FORKLARING: Lionel Messi vant sin åttende Ballon d'Or i 2023.
Nå lag {antall} spørsmål om {tema}:"""
else:
return f"""Create {antall} quiz questions about {tema} in English.
Format:
QUESTION: [specific question about {tema}]
A) [first option]
B) [second option]
C) [third option]
D) [fourth option]
ANSWER: [A, B, C or D]
EXPLANATION: [brief explanation]
Now create {antall} questions about {tema}:"""
def _parse_ai_response(self, text: str, tema: str, expected_count: int) -> List[Dict[str, Any]]:
"""Parse AI response into structured questions"""
questions = []
# Split into sections
sections = text.split("SPØRSMÅL:") if "SPØRSMÅL:" in text else text.split("QUESTION:")
for section in sections[1:]: # Skip first empty section
try:
question = self._parse_single_question(section, tema)
if question:
questions.append(question)
except Exception as e:
print(f"Error parsing question section: {e}")
continue
return questions[:expected_count]
def _parse_single_question(self, section: str, tema: str) -> Optional[Dict[str, Any]]:
"""Parse a single question from text"""
lines = [line.strip() for line in section.split('\n') if line.strip()]
if not lines:
return None
question_text = lines[0].strip()
options = []
correct_answer = 0
explanation = ""
for line in lines[1:]:
if line.startswith(('A)', 'B)', 'C)', 'D)')):
options.append(line[2:].strip())
elif line.startswith(('SVAR:', 'ANSWER:')):
answer_part = line.split(':', 1)[1].strip()
if answer_part in ['A', 'B', 'C', 'D']:
correct_answer = ['A', 'B', 'C', 'D'].index(answer_part)
elif line.startswith(('FORKLARING:', 'EXPLANATION:')):
explanation = line.split(':', 1)[1].strip()
if len(options) >= 3 and question_text:
# Ensure we have 4 options
while len(options) < 4:
options.append(f"Alternativ {len(options) + 1}")
return {
"spørsmål": question_text,
"alternativer": options[:4],
"korrekt_svar": correct_answer,
"forklaring": explanation or f"Spørsmål om {tema}"
}
return None
def _generate_enhanced_fallback(self, tema: str, antall: int) -> List[Dict[str, Any]]:
"""Generate better fallback questions based on topic analysis"""
# Analyze topic to create better questions
tema_lower = tema.lower()
questions = []
# Football/Soccer specific
if any(word in tema_lower for word in ['fotball', 'football', 'soccer', 'messi', 'ronaldo', 'haaland']):
questions = [
{
"spørsmål": "Hvem regnes som en av verdens beste fotballspillere gjennom tidene?",
"alternativer": ["Lionel Messi", "Michael Jordan", "Tiger Woods", "Usain Bolt"],
"korrekt_svar": 0,
"forklaring": "Lionel Messi regnes som en av de beste fotballspillerne noensinne med 8 Ballon d'Or-priser."
},
{
"spørsmål": "Hvilket land har vunnet flest VM i fotball?",
"alternativer": ["Tyskland", "Argentina", "Brasil", "Frankrike"],
"korrekt_svar": 2,
"forklaring": "Brasil har vunnet VM i fotball 5 ganger (1958, 1962, 1970, 1994, 2002)."
},
{
"spørsmål": "Hva kalles den prestisjetunge individuelle prisen i fotball?",
"alternativer": ["Golden Boot", "Ballon d'Or", "FIFA Award", "Champions Trophy"],
"korrekt_svar": 1,
"forklaring": "Ballon d'Or er den mest prestisjetunge individuelle prisen i fotball."
}
]
# Technology specific
elif any(word in tema_lower for word in ['teknologi', 'technology', 'ai', 'computer', 'programming']):
questions = [
{
"spørsmål": f"Hva er en viktig utvikling innen {tema}?",
"alternativer": ["Kunstig intelligens", "Dampmaskin", "Hjulet", "Ild"],
"korrekt_svar": 0,
"forklaring": f"Kunstig intelligens er en av de viktigste utviklingene innen moderne {tema}."
}
]
# Generic but better questions
if not questions:
questions = [
{
"spørsmål": f"Hva er karakteristisk for {tema}?",
"alternativer": [f"Viktig egenskap ved {tema}", "Irrelevant faktor", "Tilfeldig element", "Ukjent aspekt"],
"korrekt_svar": 0,
"forklaring": f"Dette spørsmålet handler om de karakteristiske egenskapene ved {tema}."
},
{
"spørsmål": f"Hvor er {tema} mest relevant?",
"alternativer": ["I relevant kontekst", "I irrelevant sammenheng", "Ingen steder", "Overalt"],
"korrekt_svar": 0,
"forklaring": f"{tema} er mest relevant i sin naturlige kontekst."
}
]
# Add metadata to show these are fallbacks
for q in questions:
q["_metadata"] = {
"model": "enhanced_fallback",
"generation_time": 0.1,
"ai_generated": False
}
return questions[:antall]
# Initialize the AI generator
quiz_generator = AIQuizGenerator()
# API endpoint for quiz generation
def generate_quiz_api(tema: str, språk: str = "no", antall_spørsmål: int = 3,
type: str = "sted", vanskelighetsgrad: int = 3,
api_key: str = None) -> Dict[str, Any]:
"""API endpoint for quiz generation"""
if not validate_api_key(api_key):
return {
"success": False,
"message": "Ugyldig API-nøkkel",
"questions": []
}
if not tema or len(tema.strip()) < 2:
return {
"success": False,
"message": "Vennligst oppgi et tema (minimum 2 tegn)",
"questions": []
}
try:
start_time = time.time()
questions = quiz_generator.generate_quiz(tema.strip(), antall_spørsmål, språk)
total_time = time.time() - start_time
# Check if we got real AI questions or fallbacks
ai_generated = any(q.get("_metadata", {}).get("ai_generated", False) for q in questions)
model_used = questions[0].get("_metadata", {}).get("model", "unknown") if questions else "none"
return {
"success": True,
"questions": questions,
"metadata": {
"generation_time": round(total_time, 2),
"model_used": model_used,
"topic": tema,
"ai_generated": ai_generated,
"fallback_used": not ai_generated
},
"message": f"Genererte {len(questions)} spørsmål om '{tema}'" +
(" med AI" if ai_generated else " med forbedret fallback")
}
except Exception as e:
print(f"Error in generate_quiz_api: {str(e)}")
return {
"success": False,
"message": f"Feil ved generering av quiz: {str(e)}",
"questions": []
}
# Gradio interface
def generate_quiz_gradio(tema, antall, api_key=None):
"""Gradio wrapper"""
if api_key and not validate_api_key(api_key):
return "❌ **Ugyldig API-nøkkel**"
if not tema or len(tema.strip()) < 2:
return "❌ **Vennligst skriv inn et tema**"
try:
result = generate_quiz_api(tema, "no", antall, "sted", 3, api_key)
if not result["success"]:
return f"❌ **Feil:** {result['message']}"
questions = result["questions"]
metadata = result["metadata"]
# Show different info based on whether AI was used
if metadata.get("ai_generated", False):
status_icon = "🤖"
status_text = "AI-generert"
else:
status_icon = "🔄"
status_text = "Forbedret fallback"
output = f"✅ **Genererte {len(questions)} spørsmål om '{tema}'**\n\n"
output += f"{status_icon} **Type:** {status_text}\n"
output += f"⚙️ **Modell:** {metadata['model_used']}\n"
output += f"⏱️ **Tid:** {metadata['generation_time']}s\n\n"
for i, q in enumerate(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:
return f"❌ **Feil:** {str(e)}"
# Health check
def health_check():
return {
"status": "healthy",
"timestamp": time.time(),
"ai_available": bool(os.environ.get("HUGGINGFACE_API_KEY"))
}
# Gradio interface
with gr.Blocks(title="SoActi AI Quiz API - Forbedret") as demo:
gr.Markdown("# 🧠 SoActi AI Quiz API - Forbedret")
gr.Markdown("**🚀 Ekte AI-generering med forbedret fallback**")
with gr.Row():
with gr.Column():
tema_input = gr.Textbox(
label="Tema",
value="verdens beste fotballspillere",
placeholder="Fotball, teknologi, historie, mat, filmer..."
)
antall_input = gr.Slider(
minimum=1,
maximum=5,
step=1,
label="Antall spørsmål",
value=3
)
api_key_input = gr.Textbox(
label="API-nøkkel",
placeholder="Skriv inn API-nøkkel...",
type="password"
)
generate_btn = gr.Button("🚀 Generer Forbedret Quiz!", variant="primary")
with gr.Column():
output = gr.Textbox(
label="Generert Quiz",
lines=20,
placeholder="Skriv inn et tema og test den forbedrede AI-genereringen!"
)
generate_btn.click(
fn=generate_quiz_gradio,
inputs=[tema_input, antall_input, api_key_input],
outputs=output
)
gr.Markdown("## 🔗 API Endepunkt")
gr.Markdown("`POST https://Soacti-soacti-ai-quiz-api.hf.space/generate-quiz`")
# FastAPI setup
from fastapi import FastAPI, HTTPException, Depends, Header
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
app = FastAPI(title="SoActi Quiz API - Forbedret")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class QuizRequest(BaseModel):
tema: str
språk: str = "no"
antall_spørsmål: int = 3
type: str = "sted"
vanskelighetsgrad: int = 3
async def get_api_key(authorization: str = Header(None)):
if not authorization:
raise HTTPException(status_code=401, detail="API key missing")
parts = authorization.split()
if len(parts) != 2 or parts[0].lower() != "bearer":
raise HTTPException(status_code=401, detail="Invalid authorization header")
return parts[1]
@app.post("/generate-quiz")
async def api_generate_quiz(request: QuizRequest, api_key: str = Depends(get_api_key)):
result = generate_quiz_api(
request.tema,
request.språk,
request.antall_spørsmål,
request.type,
request.vanskelighetsgrad,
api_key
)
if not result["success"]:
raise HTTPException(status_code=400, detail=result["message"])
return result
@app.get("/health")
async def api_health():
return health_check()
# Mount Gradio
app = gr.mount_gradio_app(app, demo, path="/")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)