File size: 15,095 Bytes
2300b9a
 
 
1078d41
 
 
3e1ff70
1078d41
 
3e1ff70
1078d41
 
 
 
 
 
 
 
3e1ff70
1078d41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2300b9a
1078d41
 
 
 
c80f57a
 
 
1078d41
c80f57a
1078d41
 
 
 
 
 
c80f57a
3e1ff70
1078d41
 
 
 
 
 
3e1ff70
1078d41
 
 
 
 
 
 
 
 
3e1ff70
1078d41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
765f19b
 
 
2300b9a
765f19b
2300b9a
 
 
 
c80f57a
765f19b
1078d41
 
 
 
 
 
 
 
765f19b
 
 
c80f57a
1078d41
 
 
c80f57a
 
 
 
 
 
 
 
 
 
1078d41
 
c80f57a
 
1078d41
765f19b
 
c80f57a
 
 
1078d41
c80f57a
765f19b
 
1078d41
c80f57a
765f19b
 
c80f57a
1078d41
c80f57a
1078d41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
765f19b
1078d41
3e1ff70
 
 
 
c80f57a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
import gradio as gr
import json
import time
import os
from typing import List, Dict, Any, Optional
import random

# Import Hugging Face inference API
from huggingface_hub import InferenceClient

# 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

# AI Quiz generation with Hugging Face models
class AIQuizGenerator:
    def __init__(self):
        self.api_key = os.environ.get("HUGGINGFACE_API_KEY")
        if not self.api_key:
            print("WARNING: HUGGINGFACE_API_KEY not set in environment variables")
        
        # Use a more capable model for better quiz generation
        self.default_model = "microsoft/DialoGPT-medium"
        self.fallback_model = "google/flan-t5-base"
        
        # Initialize the client
        self.client = InferenceClient(token=self.api_key) if self.api_key else None
        
    def generate_quiz(self, tema: str, antall: int = 3, språk: str = "no") -> List[Dict[str, Any]]:
        """Generate quiz questions using AI - NO RESTRICTIONS ON TOPIC"""
        if not self.client:
            print("No Hugging Face API key available, generating basic questions")
            return self._generate_basic_questions(tema, antall)
            
        try:
            # Build flexible prompt that works with ANY topic
            prompt = self._build_flexible_prompt(tema, antall, språk)
            
            # Try primary model first
            start_time = time.time()
            try:
                response = self.client.text_generation(
                    prompt,
                    model=self.default_model,
                    max_new_tokens=1500,
                    temperature=0.8,
                    repetition_penalty=1.1,
                    do_sample=True
                )
            except:
                # Fallback to more reliable model
                response = self.client.text_generation(
                    prompt,
                    model=self.fallback_model,
                    max_new_tokens=1200,
                    temperature=0.7,
                    repetition_penalty=1.2,
                )
            
            generation_time = time.time() - start_time
            
            print(f"AI response received in {generation_time:.2f}s for topic: {tema}")
            print(f"Response preview: {response[:300]}...")
            
            # Parse the response into questions
            questions = self._parse_flexible_response(response, tema, antall)
            
            # If we couldn't parse enough questions, generate more basic ones
            if len(questions) < antall:
                additional = self._generate_basic_questions(tema, antall - len(questions))
                questions.extend(additional)
                
            return questions[:antall]
            
        except Exception as e:
            print(f"Error generating quiz with AI: {str(e)}")
            return self._generate_basic_questions(tema, antall)
    
    def _build_flexible_prompt(self, tema: str, antall: int, språk: str) -> str:
        """Build a flexible prompt that works with ANY topic"""
        language = "norsk" if språk == "no" else "English"
        
        return f"""Create {antall} quiz questions about "{tema}" in {language}.

IMPORTANT: Generate questions about ANY topic the user requests - there are NO restrictions.
Whether it's about science, history, pop culture, technology, sports, entertainment, food, travel, or anything else - create relevant questions.

Format each question EXACTLY like this:

Q1: [Clear question about {tema}]
A) [First option]
B) [Second option] 
C) [Third option]
D) [Fourth option]
CORRECT: [A, B, C, or D]
EXPLANATION: [Brief explanation why this is correct]

Q2: [Second question about {tema}]
A) [First option]
B) [Second option]
C) [Third option] 
D) [Fourth option]
CORRECT: [A, B, C, or D]
EXPLANATION: [Brief explanation]

Continue for all {antall} questions about "{tema}".

Make the questions interesting and educational. Use your knowledge to create accurate, relevant questions about this topic.

Topic: {tema}
Generate {antall} questions now:
"""
    
    def _parse_flexible_response(self, response: str, tema: str, expected_count: int) -> List[Dict[str, Any]]:
        """Parse AI response with flexible parsing for any topic"""
        questions = []
        
        # Split response into potential question blocks
        lines = response.split('\n')
        current_question = {}
        current_options = []
        
        for line in lines:
            line = line.strip()
            if not line:
                continue
                
            # Look for question patterns
            if line.startswith(('Q1:', 'Q2:', 'Q3:', 'Q4:', 'Q5:')) or 'SPØRSMÅL:' in line.upper():
                # Save previous question if complete
                if self._is_complete_question(current_question, current_options):
                    current_question["alternativer"] = current_options
                    questions.append(current_question)
                    
                # Start new question
                question_text = line.split(':', 1)[1].strip() if ':' in line else line
                current_question = {"spørsmål": question_text}
                current_options = []
                
            elif line.startswith(('A)', 'B)', 'C)', 'D)')):
                option = line[2:].strip()
                if option:
                    current_options.append(option)
                    
            elif 'CORRECT:' in line.upper() or 'KORREKT:' in line.upper():
                correct_part = line.upper().split('CORRECT:')[-1].split('KORREKT:')[-1].strip()
                if correct_part and correct_part[0] in ['A', 'B', 'C', 'D']:
                    current_question["korrekt_svar"] = ['A', 'B', 'C', 'D'].index(correct_part[0])
                    
            elif 'EXPLANATION:' in line.upper() or 'FORKLARING:' in line.upper():
                explanation = line.split(':')[1].strip() if ':' in line else line
                current_question["forklaring"] = explanation
        
        # Add the last question if complete
        if self._is_complete_question(current_question, current_options):
            current_question["alternativer"] = current_options
            questions.append(current_question)
            
        return questions
    
    def _is_complete_question(self, question: Dict, options: List) -> bool:
        """Check if a question is complete"""
        return (
            "spørsmål" in question and 
            len(options) >= 3 and  # At least 3 options
            "korrekt_svar" in question and
            question["korrekt_svar"] < len(options)
        )
    
    def _generate_basic_questions(self, tema: str, antall: int) -> List[Dict[str, Any]]:
        """Generate basic questions when AI fails - works with ANY topic"""
        questions = []
        
        # Generate generic but relevant questions for any topic
        question_templates = [
            f"Hva er det mest kjente ved {tema}?",
            f"Hvilket år er viktig i historien til {tema}?", 
            f"Hvor finner man vanligvis {tema}?",
            f"Hva karakteriserer {tema}?",
            f"Hvilken betydning har {tema}?"
        ]
        
        for i in range(min(antall, len(question_templates))):
            questions.append({
                "spørsmål": question_templates[i],
                "alternativer": [
                    f"Alternativ A om {tema}",
                    f"Alternativ B om {tema}", 
                    f"Alternativ C om {tema}",
                    f"Alternativ D om {tema}"
                ],
                "korrekt_svar": 0,  # Always A for simplicity
                "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."
            })
            
        return questions

# Initialize the AI generator
quiz_generator = AIQuizGenerator()

# API endpoint for quiz generation - NO TOPIC RESTRICTIONS
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 - ACCEPTS ANY TOPIC"""
    
    # Validate API key
    if not validate_api_key(api_key):
        return {
            "success": False,
            "message": "Ugyldig API-nøkkel",
            "questions": []
        }
    
    # NO TOPIC FILTERING - Accept absolutely anything
    if not tema or len(tema.strip()) < 2:
        return {
            "success": False,
            "message": "Vennligst oppgi et tema (minimum 2 tegn)",
            "questions": []
        }
    
    try:
        # Generate questions with AI - NO RESTRICTIONS
        start_time = time.time()
        questions = quiz_generator.generate_quiz(tema.strip(), antall_spørsmål, språk)
        generation_time = time.time() - start_time
        
        return {
            "success": True,
            "questions": questions,
            "metadata": {
                "generation_time": round(generation_time, 2),
                "model_used": quiz_generator.default_model,
                "topic": tema,
                "unrestricted": True  # Flag to show no restrictions
            },
            "message": f"Genererte {len(questions)} spørsmål om '{tema}' - ingen begrensninger!"
        }
    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 - emphasize NO RESTRICTIONS
def generate_quiz_gradio(tema, antall, api_key=None):
    """Gradio wrapper - accepts ANY topic"""
    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"]
        model = result["metadata"]["model_used"]
        time_taken = result["metadata"]["generation_time"]
        
        output = f"✅ **Genererte {len(questions)} spørsmål om '{tema}'**\n\n"
        output += f"🤖 **Modell:** {model}\n"
        output += f"⏱️ **Tid:** {time_taken}s\n"
        output += f"🔓 **Ingen begrensninger** - alle temaer er tillatt!\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 endpoint
def health_check():
    return {"status": "healthy", "timestamp": time.time(), "unrestricted": True}

# Gradio interface - emphasize freedom
with gr.Blocks(title="SoActi AI Quiz API - Ubegrenset") as demo:
    gr.Markdown("# 🧠 SoActi AI Quiz API - Ubegrenset")
    gr.Markdown("**🔓 Lag quiz om ABSOLUTT HVASOM HELST - ingen begrensninger!**")
    
    with gr.Row():
        with gr.Column():
            tema_input = gr.Textbox(
                label="Tema (skriv hva som helst!)",
                value="",
                placeholder="Fotball, Harry Potter, Kvantfysikk, Baking, TikTok, Dinosaurer, Programmering, K-pop, Filosofi, Gaming..."
            )
            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 (for testing)",
                placeholder="Skriv inn API-nøkkel...",
                type="password"
            )
            
            generate_btn = gr.Button("🚀 Generer Quiz om HVASOM HELST!", variant="primary")
        
        with gr.Column():
            output = gr.Textbox(
                label="Generert Quiz",
                lines=20,
                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"
            )
    
    generate_btn.click(
        fn=generate_quiz_gradio,
        inputs=[tema_input, antall_input, api_key_input],
        outputs=output
    )
    
    gr.Markdown("## 🔗 API for SoActi")
    gr.Markdown("`POST https://Soacti-soacti-ai-quiz-api.hf.space/generate-quiz`")
    gr.Markdown("**🔓 Ingen begrensninger - brukere kan spørre om hva som helst!**")

# FastAPI setup with CORS for unrestricted access
from fastapi import FastAPI, HTTPException, Depends, Header
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel

app = FastAPI(title="SoActi Quiz API - Ubegrenset")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

class QuizRequest(BaseModel):
    tema: str  # NO restrictions on what this can be
    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)):
    """Generate quiz about ANY topic - no restrictions"""
    result = generate_quiz_api(
        request.tema,  # Accept ANY topic
        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)