File size: 3,965 Bytes
46354f0
 
 
41f8b3d
46354f0
 
 
41f8b3d
 
 
46354f0
 
41f8b3d
 
64ca6f8
46354f0
 
 
64ca6f8
46354f0
 
2679305
46354f0
 
 
2679305
46354f0
 
 
 
2679305
46354f0
 
 
 
2679305
46354f0
 
 
 
 
 
 
 
 
 
 
64ca6f8
46354f0
 
 
cda5c64
46354f0
 
 
3283333
46354f0
 
 
 
 
 
 
 
 
 
 
 
2472fa3
357210d
46354f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41f8b3d
 
46354f0
 
 
 
 
 
 
 
 
 
 
64ca6f8
 
46354f0
 
86932ae
 
46354f0
86932ae
46354f0
86932ae
 
 
46354f0
86932ae
46354f0
 
 
 
53a321f
41f8b3d
46354f0
 
 
 
 
 
 
 
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
import os
import json
from typing import Literal, Dict, Any

import httpx
from fastapi import FastAPI, Query, HTTPException
from fastapi.responses import JSONResponse

app = FastAPI()

CEREBRAS_API_KEY = os.getenv("CEREBRAS_API_KEY")
CEREBRAS_API_URL = "https://api.cerebras.ai/v1/chat/completions"


def build_prompt(difficulty: str, language: str) -> str:
    return f"""
あなたはクイズ作成者です。
難易度: {difficulty}
言語: {language}

次のフォーマットの JSON だけを返してください(前後に一切の文章を付けないこと):
**出力には{language}を使ってください。**
{{
  "questions": [
    {{
      "question": "問題文({language})",
      "choices": ["選択肢1", "選択肢2", "選択肢3", "選択肢4"],
      "correctIndex": 0
    }},
    {{
      "question": "問題文({language})",
      "choices": ["選択肢1", "選択肢2", "選択肢3", "選択肢4"],
      "correctIndex": 0
    }},
    {{
      "question": "問題文({language})",
      "choices": ["選択肢1", "選択肢2", "選択肢3", "選択肢4"],
      "correctIndex": 0
    }}
  ]
}}

制約:
- 質問は必ず3問
- 各問題は4択
- テーマは一般教養レベル(雑学など)で OK
- 難易度は easy, medium, hard に応じて調整
- 最終的なアウトプットは {language}に合わせて出力
"""


async def call_cerebras_quiz_api(difficulty: str, language: str) -> Dict[str, Any]:
    if not CEREBRAS_API_KEY:
        raise RuntimeError("CEREBRAS_API_KEY is not set")

    prompt = build_prompt(difficulty, language)

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {CEREBRAS_API_KEY}",
    }

    payload = {
        "model": "gpt-oss-120b",
        "messages": [
            {"role": "system", "content": "You are a helpful quiz generator."},
            {"role": "user", "content": prompt},
        ],
        "temperature": 0.8,
        "max_tokens": 1024,
    }

    async with httpx.AsyncClient(timeout=30.0) as client:
        resp = await client.post(CEREBRAS_API_URL, headers=headers, json=payload)
        if resp.status_code != 200:
            raise RuntimeError(f"Cerebras API error: {resp.status_code}, {resp.text}")

        data = resp.json()

    content = data.get("choices", [{}])[0].get("message", {}).get("content", "")

    # たまにモデルが ```json ... ``` で囲んでくる可能性があるので軽く掃除
    cleaned = content.strip()
    if cleaned.startswith("```"):
        # ```json or ``` で始まるケースに対応
        cleaned = cleaned.strip("`")
        # 先頭行に "json" とか残っていたら落とす
        if cleaned.startswith("json"):
            cleaned = cleaned[len("json") :].lstrip()

    try:
        quiz = json.loads(cleaned)
    except json.JSONDecodeError as e:
        # デバッグ用に content を見たい場合は log に出す
        print("Failed to parse JSON from model:", content)
        raise RuntimeError(f"JSON parse error: {e}")

    return quiz


@app.get("/quiz")
async def get_quiz(
    difficulty: Literal["easy", "medium", "hard"] = Query("medium"),
    language: str = Query("en")
):
    """
    クイズを3問返すAPI。
    レスポンス形式:
    {
      "questions": [
        {
          "question": "....",
          "choices": ["..", "..", "..", ".."],
          "correctIndex": 1
        },
        ...
      ]
    }
    """
    try:
        quiz = await call_cerebras_quiz_api(difficulty, language)
    except Exception as e:
        # 拡張側が扱いやすいように 500 + メッセージだけ返す
        raise HTTPException(status_code=500, detail=str(e))

    # 最低限のバリデーション(壊れたJSONでないかチェック)
    if not isinstance(quiz, dict) or "questions" not in quiz:
        raise HTTPException(status_code=500, detail="Invalid quiz format")

    return JSONResponse(content=quiz)