flex-analysis / api.py
baxin's picture
Create api.py
9b48f64 verified
Raw
History Blame Contribute Delete
2.85 kB
import os
import tempfile
from typing import Optional
import requests
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
ROBOT_IP = os.getenv("ROBOT_IP", "127.0.0.1")
ROBOT_PORT = int(os.getenv("ROBOT_PORT", "31950"))
OT_API_VERSION = os.getenv("OT_API_VERSION", "3")
TIMEOUT_SEC = float(os.getenv("HTTP_TIMEOUT", "30"))
app = FastAPI(title="Opentrons Protocol Analyzer API")
class AnalyzeRequest(BaseModel):
protocol_text: str = Field(..., description="Python protocol source code")
filename: str = Field("protocol.py", description="Filename to send to robot-server")
class AnalyzeResponse(BaseModel):
ok: bool
status_code: int
data: Optional[dict] = None
error: Optional[dict] = None
def _post_protocol_file(filepath: str, filename: str) -> requests.Response:
endpoint = f"http://{ROBOT_IP}:{ROBOT_PORT}/protocols"
headers = {"Opentrons-Version": OT_API_VERSION}
with open(filepath, "rb") as f:
files = {"files": (filename, f, "text/x-python")}
return requests.post(endpoint, headers=headers, files=files, timeout=TIMEOUT_SEC)
@app.get("/health")
def health():
# FastAPIの生存確認(robot-serverの生存確認は別endpointに分ける)
return {"ok": True}
@app.get("/robot/health")
def robot_health():
# robot-serverが上がってるか軽く確認
try:
r = requests.get(f"http://{ROBOT_IP}:{ROBOT_PORT}/health", timeout=5)
return {"ok": r.ok, "status_code": r.status_code, "text": r.text[:2000]}
except requests.RequestException as e:
raise HTTPException(status_code=503, detail=f"robot-server unreachable: {e}")
@app.post("/analyze", response_model=AnalyzeResponse)
def analyze(req: AnalyzeRequest):
text = (req.protocol_text or "").strip()
if not text:
raise HTTPException(status_code=400, detail="protocol_text is empty")
fd, path = tempfile.mkstemp(prefix="protocol_", suffix=".py")
try:
with os.fdopen(fd, "w", encoding="utf-8") as f:
f.write(text)
try:
resp = _post_protocol_file(path, req.filename)
except requests.RequestException as e:
raise HTTPException(status_code=503, detail=f"robot-server request failed: {e}")
# robot-serverはJSONを返す想定
try:
payload = resp.json()
except ValueError:
# JSONじゃなければそのまま返す
raise HTTPException(status_code=502, detail=f"non-json from robot-server: {resp.text[:2000]}")
if resp.status_code >= 400:
return AnalyzeResponse(ok=False, status_code=resp.status_code, error=payload)
return AnalyzeResponse(ok=True, status_code=resp.status_code, data=payload)
finally:
try:
os.remove(path)
except OSError:
pass