baxin commited on
Commit
46354f0
·
verified ·
1 Parent(s): a715309

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +134 -32
app/main.py CHANGED
@@ -1,41 +1,143 @@
1
- import io
2
- import traceback
 
3
 
4
- from fastapi import FastAPI, HTTPException, status
5
- from fastapi.concurrency import run_in_threadpool
6
- from pydantic import BaseModel
7
- from opentrons.simulate import simulate
8
 
9
  app = FastAPI()
10
 
11
- class Protocol(BaseModel):
12
- name: str
13
- content: str
14
 
15
 
16
- @app.get("/")
17
- async def root():
18
- return {"message": "Opentrons simulation API is running."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- @app.post("/simulate")
21
- async def simulate_protocol(protocol: Protocol):
22
  try:
23
- await run_in_threadpool(
24
- simulate,
25
- protocol_file=io.StringIO(protocol.content),
26
- file_name=protocol.name
27
- )
28
- return {
29
- "protocol_name": protocol.name,
30
- "status": "success"
31
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  except Exception as e:
33
- raise HTTPException(
34
- status_code=status.HTTP_400_BAD_REQUEST,
35
- detail={
36
- "status": "error",
37
- "error_type": type(e).__name__,
38
- "message": str(e),
39
- "traceback": traceback.format_exc()
40
- }
41
- )
 
1
+ import os
2
+ import json
3
+ from typing import Literal, Dict, Any
4
 
5
+ import httpx
6
+ from fastapi import FastAPI, Query, HTTPException
7
+ from fastapi.responses import JSONResponse
 
8
 
9
  app = FastAPI()
10
 
11
+ CEREBRAS_API_KEY = os.getenv("CEREBRAS_API_KEY")
12
+ CEREBRAS_API_URL = "https://api.cerebras.ai/v1/chat/completions"
 
13
 
14
 
15
+ def build_prompt(difficulty: str) -> str:
16
+ return f"""
17
+ あなたはクイズ作成者です。
18
+ 難易度: {difficulty}
19
+
20
+ 次のフォーマットの JSON だけを返してください(前後に一切の文章を付けないこと):
21
+
22
+ {{
23
+ "questions": [
24
+ {{
25
+ "question": "問題文(日本語)",
26
+ "choices": ["選択肢1", "選択肢2", "選択肢3", "選択肢4"],
27
+ "correctIndex": 0
28
+ }},
29
+ {{
30
+ "question": "問題文(日本語)",
31
+ "choices": ["選択肢1", "選択肢2", "選択肢3", "選択肢4"],
32
+ "correctIndex": 0
33
+ }},
34
+ {{
35
+ "question": "問題文(日本語)",
36
+ "choices": ["選択肢1", "選択肢2", "選択肢3", "選択肢4"],
37
+ "correctIndex": 0
38
+ }}
39
+ ]
40
+ }}
41
+
42
+ 制約:
43
+ - 質問は必ず3問
44
+ - 各問題は4択
45
+ - テーマは一般教養レベル(雑学など)で OK
46
+ - 難易度は easy, medium, hard に応じて調整
47
+ """
48
+
49
+
50
+ async def call_cerebras_quiz_api(difficulty: str) -> Dict[str, Any]:
51
+ if not CEREBRAS_API_KEY:
52
+ raise RuntimeError("CEREBRAS_API_KEY is not set")
53
+
54
+ prompt = build_prompt(difficulty)
55
+
56
+ headers = {
57
+ "Content-Type": "application/json",
58
+ "Authorization": f"Bearer {CEREBRAS_API_KEY}",
59
+ }
60
+
61
+ payload = {
62
+ "model": "gpt-oss-120b",
63
+ "messages": [
64
+ {"role": "system", "content": "You are a helpful quiz generator."},
65
+ {"role": "user", "content": prompt},
66
+ ],
67
+ "temperature": 0.7,
68
+ "max_tokens": 512,
69
+ }
70
+
71
+ async with httpx.AsyncClient(timeout=30.0) as client:
72
+ resp = await client.post(CEREBRAS_API_URL, headers=headers, json=payload)
73
+ if resp.status_code != 200:
74
+ raise RuntimeError(f"Cerebras API error: {resp.status_code}, {resp.text}")
75
+
76
+ data = resp.json()
77
+
78
+ content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
79
+
80
+ # たまにモデルが ```json ... ``` で囲んでくる可能性があるので軽く掃除
81
+ cleaned = content.strip()
82
+ if cleaned.startswith("```"):
83
+ # ```json or ``` で始まるケースに対応
84
+ cleaned = cleaned.strip("`")
85
+ # 先頭行に "json" とか残っていたら落とす
86
+ if cleaned.startswith("json"):
87
+ cleaned = cleaned[len("json") :].lstrip()
88
 
 
 
89
  try:
90
+ quiz = json.loads(cleaned)
91
+ except json.JSONDecodeError as e:
92
+ # デバッグ用に content を見たい場合は log に出す
93
+ print("Failed to parse JSON from model:", content)
94
+ raise RuntimeError(f"JSON parse error: {e}")
95
+
96
+ return quiz
97
+
98
+
99
+ @app.get("/quiz")
100
+ async def get_quiz(
101
+ difficulty: Literal["easy", "medium", "hard"] = Query("medium")
102
+ ):
103
+ """
104
+ You are the master of quiz. You offer 3 quizzes to a user by the difficulty that user posts (easy, medium, hard).
105
+ You will return 3 quizzes with the following JSON format. The following is just sample format.
106
+
107
+ format
108
+ ```
109
+ {
110
+ "quizzes": [
111
+ {
112
+ "id": 1,
113
+ "question": "question 1",
114
+ "choices": ["chice1", "chice2", "chice3", "chice4"],
115
+ "answer": "choice2"
116
+ },
117
+ {
118
+ "id": 2,
119
+ "question": "question 2",
120
+ "choices": ["chice1", "chice2", "chice3", "chice4"],
121
+ "answer": "choice4"
122
+ },
123
+ {
124
+ "id": 3,
125
+ "question": "question 3",
126
+ "choices": ["chice1", "chice2", "chice3", "chice4"],
127
+ "answer": "choice3"
128
+ },
129
+ ]
130
+ }
131
+ ```
132
+ """
133
+ try:
134
+ quiz = await call_cerebras_quiz_api(difficulty)
135
  except Exception as e:
136
+ # 拡張側が扱いやすいように 500 + メ���セージだけ返す
137
+ raise HTTPException(status_code=500, detail=str(e))
138
+
139
+ # 最低限のバリデーション(壊れたJSONでないかチェック)
140
+ if not isinstance(quiz, dict) or "questions" not in quiz:
141
+ raise HTTPException(status_code=500, detail="Invalid quiz format")
142
+
143
+ return JSONResponse(content=quiz)