baxin's picture
Update app/main.py
2679305 verified
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)