Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,168 +1,280 @@
|
|
1 |
-
import os
|
2 |
import gradio as gr
|
3 |
import json
|
4 |
import time
|
5 |
-
|
6 |
-
from
|
7 |
-
|
8 |
-
from typing import Optional
|
9 |
-
|
10 |
-
# Hent API-nøkkel fra miljøvariabler
|
11 |
-
API_KEY = os.getenv("SOACTI_API_KEY")
|
12 |
-
if not API_KEY:
|
13 |
-
raise ValueError("SOACTI_API_KEY miljøvariabel er ikke satt!")
|
14 |
-
|
15 |
-
# FastAPI app
|
16 |
-
app = FastAPI(title="SoActi AI Quiz API - Secure")
|
17 |
-
security = HTTPBearer()
|
18 |
-
|
19 |
-
# Hardkodede spørsmål (samme som før)
|
20 |
-
QUIZ_DATABASE = {
|
21 |
-
"oslo": [
|
22 |
-
{
|
23 |
-
"spørsmål": "Hva heter Oslos hovedflyplass?",
|
24 |
-
"alternativer": ["Fornebu", "Gardermoen", "Torp", "Rygge"],
|
25 |
-
"korrekt_svar": 1,
|
26 |
-
"forklaring": "Oslo lufthavn Gardermoen er Oslos hovedflyplass, åpnet i 1998."
|
27 |
-
},
|
28 |
-
{
|
29 |
-
"spørsmål": "Hvilken fjord ligger Oslo ved?",
|
30 |
-
"alternativer": ["Trondheimsfjorden", "Oslofjorden", "Hardangerfjorden", "Sognefjorden"],
|
31 |
-
"korrekt_svar": 1,
|
32 |
-
"forklaring": "Oslo ligger innerst i Oslofjorden."
|
33 |
-
},
|
34 |
-
{
|
35 |
-
"spørsmål": "Hva heter Oslos berømte operahus?",
|
36 |
-
"alternativer": ["Operaen", "Den Norske Opera", "Oslo Opera House", "Operahuset"],
|
37 |
-
"korrekt_svar": 3,
|
38 |
-
"forklaring": "Operahuset i Oslo åpnet i 2008."
|
39 |
-
}
|
40 |
-
],
|
41 |
-
"bergen": [
|
42 |
-
{
|
43 |
-
"spørsmål": "Hva kalles det fargerike kaiområdet i Bergen?",
|
44 |
-
"alternativer": ["Bryggen", "Fisketorget", "Torgallmenningen", "Nordnes"],
|
45 |
-
"korrekt_svar": 0,
|
46 |
-
"forklaring": "Bryggen er UNESCOs verdensarvsted."
|
47 |
-
},
|
48 |
-
{
|
49 |
-
"spørsmål": "Hvilket fjell kan man ta Fløibanen opp til?",
|
50 |
-
"alternativer": ["Ulriken", "Fløyen", "Løvstakken", "Sandviksfjellet"],
|
51 |
-
"korrekt_svar": 1,
|
52 |
-
"forklaring": "Fløibanen går opp til Fløyen, 320 meter over havet."
|
53 |
-
}
|
54 |
-
],
|
55 |
-
"norsk historie": [
|
56 |
-
{
|
57 |
-
"spørsmål": "Hvilket år ble Norge selvstendig fra Danmark?",
|
58 |
-
"alternativer": ["1814", "1905", "1821", "1830"],
|
59 |
-
"korrekt_svar": 0,
|
60 |
-
"forklaring": "Norge ble selvstendig fra Danmark i 1814."
|
61 |
-
}
|
62 |
-
]
|
63 |
-
}
|
64 |
-
|
65 |
-
# API-nøkkel validering
|
66 |
-
async def verify_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
67 |
-
if credentials.credentials != API_KEY:
|
68 |
-
raise HTTPException(
|
69 |
-
status_code=401,
|
70 |
-
detail="Ugyldig API-nøkkel"
|
71 |
-
)
|
72 |
-
return credentials.credentials
|
73 |
|
74 |
-
#
|
75 |
-
|
76 |
-
tema: str
|
77 |
-
språk: str = "no"
|
78 |
-
antall_spørsmål: int = 5
|
79 |
-
type: str = "multiple_choice"
|
80 |
-
vanskelighetsgrad: int = 3
|
81 |
|
82 |
-
# API
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
"
|
88 |
-
|
89 |
-
|
90 |
-
}
|
91 |
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
try:
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
questions = QUIZ_DATABASE[tema_lower]
|
104 |
-
elif "oslo" in tema_lower:
|
105 |
-
questions = QUIZ_DATABASE["oslo"]
|
106 |
-
elif "bergen" in tema_lower:
|
107 |
-
questions = QUIZ_DATABASE["bergen"]
|
108 |
-
elif "historie" in tema_lower:
|
109 |
-
questions = QUIZ_DATABASE["norsk historie"]
|
110 |
-
else:
|
111 |
-
questions = QUIZ_DATABASE["oslo"]
|
112 |
-
|
113 |
-
# Begrens antall spørsmål
|
114 |
-
selected_questions = questions[:request.antall_spørsmål]
|
115 |
|
116 |
return {
|
117 |
"success": True,
|
118 |
-
"questions":
|
119 |
"metadata": {
|
120 |
-
"
|
121 |
-
"
|
122 |
-
"
|
123 |
-
"
|
124 |
-
|
125 |
-
|
126 |
-
}
|
127 |
}
|
128 |
-
|
129 |
except Exception as e:
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
|
|
|
|
134 |
|
135 |
-
# Gradio interface
|
136 |
-
def
|
137 |
-
"""Gradio wrapper
|
138 |
-
if not
|
139 |
-
return "❌ API-nøkkel
|
140 |
-
|
141 |
-
if
|
142 |
-
return "❌
|
143 |
-
|
144 |
try:
|
145 |
-
|
146 |
-
|
147 |
-
if
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
output = f"✅ **Genererte {len(selected_questions)} spørsmål om '{tema}'**\n\n"
|
161 |
-
output += f"🔐 **Autentisert:** Ja\n"
|
162 |
-
output += f"🤖 **Modell:** Fallback Database\n"
|
163 |
-
output += f"⏱️ **Tid:** 0.1s\n\n"
|
164 |
-
|
165 |
-
for i, q in enumerate(selected_questions, 1):
|
166 |
output += f"📝 **Spørsmål {i}:** {q['spørsmål']}\n"
|
167 |
for j, alt in enumerate(q['alternativer']):
|
168 |
marker = "✅" if j == q['korrekt_svar'] else "❌"
|
@@ -174,17 +286,21 @@ def generate_quiz_ui(tema, antall, api_key_input):
|
|
174 |
except Exception as e:
|
175 |
return f"❌ **Feil:** {str(e)}"
|
176 |
|
177 |
-
#
|
178 |
-
|
179 |
-
|
180 |
-
|
|
|
|
|
|
|
|
|
181 |
|
182 |
with gr.Row():
|
183 |
with gr.Column():
|
184 |
tema_input = gr.Textbox(
|
185 |
-
label="Tema",
|
186 |
-
value="
|
187 |
-
placeholder="
|
188 |
)
|
189 |
antall_input = gr.Slider(
|
190 |
minimum=1,
|
@@ -195,30 +311,83 @@ with gr.Blocks(title="SoActi AI Quiz API - Secure") as demo:
|
|
195 |
)
|
196 |
api_key_input = gr.Textbox(
|
197 |
label="API-nøkkel (for testing)",
|
198 |
-
|
199 |
-
|
200 |
)
|
201 |
|
202 |
-
generate_btn = gr.Button("🚀 Generer Quiz", variant="primary")
|
203 |
|
204 |
with gr.Column():
|
205 |
output = gr.Textbox(
|
206 |
label="Generert Quiz",
|
207 |
lines=20,
|
208 |
-
placeholder="Skriv inn
|
209 |
)
|
210 |
|
211 |
generate_btn.click(
|
212 |
-
fn=
|
213 |
inputs=[tema_input, antall_input, api_key_input],
|
214 |
outputs=output
|
215 |
)
|
216 |
|
217 |
-
gr.Markdown("## 🔗 API
|
218 |
gr.Markdown("`POST https://Soacti-soacti-ai-quiz-api.hf.space/generate-quiz`")
|
219 |
-
gr.Markdown("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
|
221 |
-
# Mount Gradio
|
222 |
app = gr.mount_gradio_app(app, demo, path="/")
|
223 |
|
224 |
if __name__ == "__main__":
|
|
|
|
|
1 |
import gradio as gr
|
2 |
import json
|
3 |
import time
|
4 |
+
import os
|
5 |
+
from typing import List, Dict, Any, Optional
|
6 |
+
import random
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
+
# Import Hugging Face inference API
|
9 |
+
from huggingface_hub import InferenceClient
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
# API key validation
|
12 |
+
def validate_api_key(api_key: str) -> bool:
|
13 |
+
"""Validate the API key against the stored secret"""
|
14 |
+
expected_key = os.environ.get("SOACTI_API_KEY")
|
15 |
+
if not expected_key:
|
16 |
+
print("WARNING: SOACTI_API_KEY not set in environment variables")
|
17 |
+
return False
|
18 |
+
return api_key == expected_key
|
|
|
19 |
|
20 |
+
# AI Quiz generation with Hugging Face models
|
21 |
+
class AIQuizGenerator:
|
22 |
+
def __init__(self):
|
23 |
+
self.api_key = os.environ.get("HUGGINGFACE_API_KEY")
|
24 |
+
if not self.api_key:
|
25 |
+
print("WARNING: HUGGINGFACE_API_KEY not set in environment variables")
|
26 |
+
|
27 |
+
# Use a more capable model for better quiz generation
|
28 |
+
self.default_model = "microsoft/DialoGPT-medium"
|
29 |
+
self.fallback_model = "google/flan-t5-base"
|
30 |
+
|
31 |
+
# Initialize the client
|
32 |
+
self.client = InferenceClient(token=self.api_key) if self.api_key else None
|
33 |
+
|
34 |
+
def generate_quiz(self, tema: str, antall: int = 3, språk: str = "no") -> List[Dict[str, Any]]:
|
35 |
+
"""Generate quiz questions using AI - NO RESTRICTIONS ON TOPIC"""
|
36 |
+
if not self.client:
|
37 |
+
print("No Hugging Face API key available, generating basic questions")
|
38 |
+
return self._generate_basic_questions(tema, antall)
|
39 |
+
|
40 |
+
try:
|
41 |
+
# Build flexible prompt that works with ANY topic
|
42 |
+
prompt = self._build_flexible_prompt(tema, antall, språk)
|
43 |
+
|
44 |
+
# Try primary model first
|
45 |
+
start_time = time.time()
|
46 |
+
try:
|
47 |
+
response = self.client.text_generation(
|
48 |
+
prompt,
|
49 |
+
model=self.default_model,
|
50 |
+
max_new_tokens=1500,
|
51 |
+
temperature=0.8,
|
52 |
+
repetition_penalty=1.1,
|
53 |
+
do_sample=True
|
54 |
+
)
|
55 |
+
except:
|
56 |
+
# Fallback to more reliable model
|
57 |
+
response = self.client.text_generation(
|
58 |
+
prompt,
|
59 |
+
model=self.fallback_model,
|
60 |
+
max_new_tokens=1200,
|
61 |
+
temperature=0.7,
|
62 |
+
repetition_penalty=1.2,
|
63 |
+
)
|
64 |
+
|
65 |
+
generation_time = time.time() - start_time
|
66 |
+
|
67 |
+
print(f"AI response received in {generation_time:.2f}s for topic: {tema}")
|
68 |
+
print(f"Response preview: {response[:300]}...")
|
69 |
+
|
70 |
+
# Parse the response into questions
|
71 |
+
questions = self._parse_flexible_response(response, tema, antall)
|
72 |
+
|
73 |
+
# If we couldn't parse enough questions, generate more basic ones
|
74 |
+
if len(questions) < antall:
|
75 |
+
additional = self._generate_basic_questions(tema, antall - len(questions))
|
76 |
+
questions.extend(additional)
|
77 |
+
|
78 |
+
return questions[:antall]
|
79 |
+
|
80 |
+
except Exception as e:
|
81 |
+
print(f"Error generating quiz with AI: {str(e)}")
|
82 |
+
return self._generate_basic_questions(tema, antall)
|
83 |
+
|
84 |
+
def _build_flexible_prompt(self, tema: str, antall: int, språk: str) -> str:
|
85 |
+
"""Build a flexible prompt that works with ANY topic"""
|
86 |
+
language = "norsk" if språk == "no" else "English"
|
87 |
+
|
88 |
+
return f"""Create {antall} quiz questions about "{tema}" in {language}.
|
89 |
+
|
90 |
+
IMPORTANT: Generate questions about ANY topic the user requests - there are NO restrictions.
|
91 |
+
Whether it's about science, history, pop culture, technology, sports, entertainment, food, travel, or anything else - create relevant questions.
|
92 |
+
|
93 |
+
Format each question EXACTLY like this:
|
94 |
+
|
95 |
+
Q1: [Clear question about {tema}]
|
96 |
+
A) [First option]
|
97 |
+
B) [Second option]
|
98 |
+
C) [Third option]
|
99 |
+
D) [Fourth option]
|
100 |
+
CORRECT: [A, B, C, or D]
|
101 |
+
EXPLANATION: [Brief explanation why this is correct]
|
102 |
+
|
103 |
+
Q2: [Second question about {tema}]
|
104 |
+
A) [First option]
|
105 |
+
B) [Second option]
|
106 |
+
C) [Third option]
|
107 |
+
D) [Fourth option]
|
108 |
+
CORRECT: [A, B, C, or D]
|
109 |
+
EXPLANATION: [Brief explanation]
|
110 |
+
|
111 |
+
Continue for all {antall} questions about "{tema}".
|
112 |
+
|
113 |
+
Make the questions interesting and educational. Use your knowledge to create accurate, relevant questions about this topic.
|
114 |
+
|
115 |
+
Topic: {tema}
|
116 |
+
Generate {antall} questions now:
|
117 |
+
"""
|
118 |
+
|
119 |
+
def _parse_flexible_response(self, response: str, tema: str, expected_count: int) -> List[Dict[str, Any]]:
|
120 |
+
"""Parse AI response with flexible parsing for any topic"""
|
121 |
+
questions = []
|
122 |
+
|
123 |
+
# Split response into potential question blocks
|
124 |
+
lines = response.split('\n')
|
125 |
+
current_question = {}
|
126 |
+
current_options = []
|
127 |
+
|
128 |
+
for line in lines:
|
129 |
+
line = line.strip()
|
130 |
+
if not line:
|
131 |
+
continue
|
132 |
+
|
133 |
+
# Look for question patterns
|
134 |
+
if line.startswith(('Q1:', 'Q2:', 'Q3:', 'Q4:', 'Q5:')) or 'SPØRSMÅL:' in line.upper():
|
135 |
+
# Save previous question if complete
|
136 |
+
if self._is_complete_question(current_question, current_options):
|
137 |
+
current_question["alternativer"] = current_options
|
138 |
+
questions.append(current_question)
|
139 |
+
|
140 |
+
# Start new question
|
141 |
+
question_text = line.split(':', 1)[1].strip() if ':' in line else line
|
142 |
+
current_question = {"spørsmål": question_text}
|
143 |
+
current_options = []
|
144 |
+
|
145 |
+
elif line.startswith(('A)', 'B)', 'C)', 'D)')):
|
146 |
+
option = line[2:].strip()
|
147 |
+
if option:
|
148 |
+
current_options.append(option)
|
149 |
+
|
150 |
+
elif 'CORRECT:' in line.upper() or 'KORREKT:' in line.upper():
|
151 |
+
correct_part = line.upper().split('CORRECT:')[-1].split('KORREKT:')[-1].strip()
|
152 |
+
if correct_part and correct_part[0] in ['A', 'B', 'C', 'D']:
|
153 |
+
current_question["korrekt_svar"] = ['A', 'B', 'C', 'D'].index(correct_part[0])
|
154 |
+
|
155 |
+
elif 'EXPLANATION:' in line.upper() or 'FORKLARING:' in line.upper():
|
156 |
+
explanation = line.split(':')[1].strip() if ':' in line else line
|
157 |
+
current_question["forklaring"] = explanation
|
158 |
+
|
159 |
+
# Add the last question if complete
|
160 |
+
if self._is_complete_question(current_question, current_options):
|
161 |
+
current_question["alternativer"] = current_options
|
162 |
+
questions.append(current_question)
|
163 |
+
|
164 |
+
return questions
|
165 |
+
|
166 |
+
def _is_complete_question(self, question: Dict, options: List) -> bool:
|
167 |
+
"""Check if a question is complete"""
|
168 |
+
return (
|
169 |
+
"spørsmål" in question and
|
170 |
+
len(options) >= 3 and # At least 3 options
|
171 |
+
"korrekt_svar" in question and
|
172 |
+
question["korrekt_svar"] < len(options)
|
173 |
+
)
|
174 |
+
|
175 |
+
def _generate_basic_questions(self, tema: str, antall: int) -> List[Dict[str, Any]]:
|
176 |
+
"""Generate basic questions when AI fails - works with ANY topic"""
|
177 |
+
questions = []
|
178 |
+
|
179 |
+
# Generate generic but relevant questions for any topic
|
180 |
+
question_templates = [
|
181 |
+
f"Hva er det mest kjente ved {tema}?",
|
182 |
+
f"Hvilket år er viktig i historien til {tema}?",
|
183 |
+
f"Hvor finner man vanligvis {tema}?",
|
184 |
+
f"Hva karakteriserer {tema}?",
|
185 |
+
f"Hvilken betydning har {tema}?"
|
186 |
+
]
|
187 |
+
|
188 |
+
for i in range(min(antall, len(question_templates))):
|
189 |
+
questions.append({
|
190 |
+
"spørsmål": question_templates[i],
|
191 |
+
"alternativer": [
|
192 |
+
f"Alternativ A om {tema}",
|
193 |
+
f"Alternativ B om {tema}",
|
194 |
+
f"Alternativ C om {tema}",
|
195 |
+
f"Alternativ D om {tema}"
|
196 |
+
],
|
197 |
+
"korrekt_svar": 0, # Always A for simplicity
|
198 |
+
"forklaring": f"Dette er et generert spørsmål om {tema}. For mer nøyaktige spørsmål, prøv igjen - AI-systemet lærer kontinuerlig."
|
199 |
+
})
|
200 |
+
|
201 |
+
return questions
|
202 |
+
|
203 |
+
# Initialize the AI generator
|
204 |
+
quiz_generator = AIQuizGenerator()
|
205 |
+
|
206 |
+
# API endpoint for quiz generation - NO TOPIC RESTRICTIONS
|
207 |
+
def generate_quiz_api(tema: str, språk: str = "no", antall_spørsmål: int = 3,
|
208 |
+
type: str = "sted", vanskelighetsgrad: int = 3,
|
209 |
+
api_key: str = None) -> Dict[str, Any]:
|
210 |
+
"""API endpoint for quiz generation - ACCEPTS ANY TOPIC"""
|
211 |
+
|
212 |
+
# Validate API key
|
213 |
+
if not validate_api_key(api_key):
|
214 |
+
return {
|
215 |
+
"success": False,
|
216 |
+
"message": "Ugyldig API-nøkkel",
|
217 |
+
"questions": []
|
218 |
+
}
|
219 |
+
|
220 |
+
# NO TOPIC FILTERING - Accept absolutely anything
|
221 |
+
if not tema or len(tema.strip()) < 2:
|
222 |
+
return {
|
223 |
+
"success": False,
|
224 |
+
"message": "Vennligst oppgi et tema (minimum 2 tegn)",
|
225 |
+
"questions": []
|
226 |
+
}
|
227 |
+
|
228 |
try:
|
229 |
+
# Generate questions with AI - NO RESTRICTIONS
|
230 |
+
start_time = time.time()
|
231 |
+
questions = quiz_generator.generate_quiz(tema.strip(), antall_spørsmål, språk)
|
232 |
+
generation_time = time.time() - start_time
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
233 |
|
234 |
return {
|
235 |
"success": True,
|
236 |
+
"questions": questions,
|
237 |
"metadata": {
|
238 |
+
"generation_time": round(generation_time, 2),
|
239 |
+
"model_used": quiz_generator.default_model,
|
240 |
+
"topic": tema,
|
241 |
+
"unrestricted": True # Flag to show no restrictions
|
242 |
+
},
|
243 |
+
"message": f"Genererte {len(questions)} spørsmål om '{tema}' - ingen begrensninger!"
|
|
|
244 |
}
|
|
|
245 |
except Exception as e:
|
246 |
+
print(f"Error in generate_quiz_api: {str(e)}")
|
247 |
+
return {
|
248 |
+
"success": False,
|
249 |
+
"message": f"Feil ved generering av quiz: {str(e)}",
|
250 |
+
"questions": []
|
251 |
+
}
|
252 |
|
253 |
+
# Gradio interface - emphasize NO RESTRICTIONS
|
254 |
+
def generate_quiz_gradio(tema, antall, api_key=None):
|
255 |
+
"""Gradio wrapper - accepts ANY topic"""
|
256 |
+
if api_key and not validate_api_key(api_key):
|
257 |
+
return "❌ **Ugyldig API-nøkkel**"
|
258 |
+
|
259 |
+
if not tema or len(tema.strip()) < 2:
|
260 |
+
return "❌ **Vennligst skriv inn et tema**"
|
261 |
+
|
262 |
try:
|
263 |
+
result = generate_quiz_api(tema, "no", antall, "sted", 3, api_key)
|
264 |
+
|
265 |
+
if not result["success"]:
|
266 |
+
return f"❌ **Feil:** {result['message']}"
|
267 |
+
|
268 |
+
questions = result["questions"]
|
269 |
+
model = result["metadata"]["model_used"]
|
270 |
+
time_taken = result["metadata"]["generation_time"]
|
271 |
+
|
272 |
+
output = f"✅ **Genererte {len(questions)} spørsmål om '{tema}'**\n\n"
|
273 |
+
output += f"🤖 **Modell:** {model}\n"
|
274 |
+
output += f"⏱️ **Tid:** {time_taken}s\n"
|
275 |
+
output += f"🔓 **Ingen begrensninger** - alle temaer er tillatt!\n\n"
|
276 |
+
|
277 |
+
for i, q in enumerate(questions, 1):
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
output += f"📝 **Spørsmål {i}:** {q['spørsmål']}\n"
|
279 |
for j, alt in enumerate(q['alternativer']):
|
280 |
marker = "✅" if j == q['korrekt_svar'] else "❌"
|
|
|
286 |
except Exception as e:
|
287 |
return f"❌ **Feil:** {str(e)}"
|
288 |
|
289 |
+
# Health check endpoint
|
290 |
+
def health_check():
|
291 |
+
return {"status": "healthy", "timestamp": time.time(), "unrestricted": True}
|
292 |
+
|
293 |
+
# Gradio interface - emphasize freedom
|
294 |
+
with gr.Blocks(title="SoActi AI Quiz API - Ubegrenset") as demo:
|
295 |
+
gr.Markdown("# 🧠 SoActi AI Quiz API - Ubegrenset")
|
296 |
+
gr.Markdown("**🔓 Lag quiz om ABSOLUTT HVASOM HELST - ingen begrensninger!**")
|
297 |
|
298 |
with gr.Row():
|
299 |
with gr.Column():
|
300 |
tema_input = gr.Textbox(
|
301 |
+
label="Tema (skriv hva som helst!)",
|
302 |
+
value="",
|
303 |
+
placeholder="Fotball, Harry Potter, Kvantfysikk, Baking, TikTok, Dinosaurer, Programmering, K-pop, Filosofi, Gaming..."
|
304 |
)
|
305 |
antall_input = gr.Slider(
|
306 |
minimum=1,
|
|
|
311 |
)
|
312 |
api_key_input = gr.Textbox(
|
313 |
label="API-nøkkel (for testing)",
|
314 |
+
placeholder="Skriv inn API-nøkkel...",
|
315 |
+
type="password"
|
316 |
)
|
317 |
|
318 |
+
generate_btn = gr.Button("🚀 Generer Quiz om HVASOM HELST!", variant="primary")
|
319 |
|
320 |
with gr.Column():
|
321 |
output = gr.Textbox(
|
322 |
label="Generert Quiz",
|
323 |
lines=20,
|
324 |
+
placeholder="Skriv inn HVILKET SOM HELST tema og klikk 'Generer Quiz'!\n\nEksempler:\n- Marvel filmer\n- Norsk rap\n- Kryptovaluta\n- Yoga\n- Sushi\n- Elon Musk\n- Klimaendringer\n- Netflix serier\n- Fotografi\n- Skateboard"
|
325 |
)
|
326 |
|
327 |
generate_btn.click(
|
328 |
+
fn=generate_quiz_gradio,
|
329 |
inputs=[tema_input, antall_input, api_key_input],
|
330 |
outputs=output
|
331 |
)
|
332 |
|
333 |
+
gr.Markdown("## 🔗 API for SoActi")
|
334 |
gr.Markdown("`POST https://Soacti-soacti-ai-quiz-api.hf.space/generate-quiz`")
|
335 |
+
gr.Markdown("**🔓 Ingen begrensninger - brukere kan spørre om hva som helst!**")
|
336 |
+
|
337 |
+
# FastAPI setup with CORS for unrestricted access
|
338 |
+
from fastapi import FastAPI, HTTPException, Depends, Header
|
339 |
+
from fastapi.middleware.cors import CORSMiddleware
|
340 |
+
from pydantic import BaseModel
|
341 |
+
|
342 |
+
app = FastAPI(title="SoActi Quiz API - Ubegrenset")
|
343 |
+
|
344 |
+
app.add_middleware(
|
345 |
+
CORSMiddleware,
|
346 |
+
allow_origins=["*"],
|
347 |
+
allow_credentials=True,
|
348 |
+
allow_methods=["*"],
|
349 |
+
allow_headers=["*"],
|
350 |
+
)
|
351 |
+
|
352 |
+
class QuizRequest(BaseModel):
|
353 |
+
tema: str # NO restrictions on what this can be
|
354 |
+
språk: str = "no"
|
355 |
+
antall_spørsmål: int = 3
|
356 |
+
type: str = "sted"
|
357 |
+
vanskelighetsgrad: int = 3
|
358 |
+
|
359 |
+
async def get_api_key(authorization: str = Header(None)):
|
360 |
+
if not authorization:
|
361 |
+
raise HTTPException(status_code=401, detail="API key missing")
|
362 |
+
|
363 |
+
parts = authorization.split()
|
364 |
+
if len(parts) != 2 or parts[0].lower() != "bearer":
|
365 |
+
raise HTTPException(status_code=401, detail="Invalid authorization header")
|
366 |
+
|
367 |
+
return parts[1]
|
368 |
+
|
369 |
+
@app.post("/generate-quiz")
|
370 |
+
async def api_generate_quiz(request: QuizRequest, api_key: str = Depends(get_api_key)):
|
371 |
+
"""Generate quiz about ANY topic - no restrictions"""
|
372 |
+
result = generate_quiz_api(
|
373 |
+
request.tema, # Accept ANY topic
|
374 |
+
request.språk,
|
375 |
+
request.antall_spørsmål,
|
376 |
+
request.type,
|
377 |
+
request.vanskelighetsgrad,
|
378 |
+
api_key
|
379 |
+
)
|
380 |
+
|
381 |
+
if not result["success"]:
|
382 |
+
raise HTTPException(status_code=400, detail=result["message"])
|
383 |
+
|
384 |
+
return result
|
385 |
+
|
386 |
+
@app.get("/health")
|
387 |
+
async def api_health():
|
388 |
+
return health_check()
|
389 |
|
390 |
+
# Mount Gradio
|
391 |
app = gr.mount_gradio_app(app, demo, path="/")
|
392 |
|
393 |
if __name__ == "__main__":
|