uzzam2121
Add /api/alter OpenAI increase decrease
29ad923
Raw
History Blame Contribute Delete
5.29 kB
"""FastAPI backend for Railway — word complexity inference."""
from __future__ import annotations
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from app.config import CORS_ORIGINS, MODEL_PATH
from app.schemas import (
AlterComplexityRequest,
AlterComplexityResponse,
EfficiencyRequest,
EfficiencyResponse,
HealthResponse,
InterveneAllRequest,
InterveneAllResponse,
InterveneRequest,
InterveneResponse,
PredictRequest,
PredictResponse,
)
from app.services.efficiency import compare_efficiency
from app.services.interventions import run_intervention
from app.services.model import get_predictor, model_status, predict
@asynccontextmanager
async def lifespan(_app: FastAPI):
if os.environ.get("EAGER_LOAD_MODEL", "1") == "1":
try:
get_predictor()
except RuntimeError as exc:
print(f"WARN: model not loaded at startup: {exc}")
yield
app = FastAPI(title="Word Complexity API", version="1.0.0", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"] if os.environ.get("CORS_ALLOW_ALL") == "1" else CORS_ORIGINS,
allow_credentials=os.environ.get("CORS_ALLOW_ALL") != "1",
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def root():
ready, err = model_status()
return {
"service": "Word Complexity API",
"health": "/health",
"docs": "/docs",
"model_ready": ready,
"model_error": err,
}
@app.get("/health", response_model=HealthResponse)
def health():
ready, err = model_status()
return HealthResponse(
status="ok" if ready else "degraded",
model_path=str(MODEL_PATH),
model_ready=ready,
model_error=err,
)
@app.post("/api/predict", response_model=PredictResponse)
def api_predict(body: PredictRequest):
try:
data, latency_ms, target_in = predict(body.sentence.strip(), body.target_word.strip())
except RuntimeError as exc:
raise HTTPException(status_code=503, detail=str(exc)) from exc
return PredictResponse(**data, latency_ms=latency_ms, target_in_sentence=target_in)
@app.post("/api/intervene", response_model=InterveneResponse)
def api_intervene(body: InterveneRequest):
try:
predictor = get_predictor()
detail = run_intervention(
predictor,
body.sentence.strip(),
body.target_word.strip(),
body.reason,
body.use_llm,
)
except RuntimeError as exc:
raise HTTPException(status_code=503, detail=str(exc)) from exc
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
return InterveneResponse(intervention=detail)
@app.post("/api/intervene/all", response_model=InterveneAllResponse)
def api_intervene_all(body: InterveneAllRequest):
try:
predictor = get_predictor()
base = predictor.predict(body.sentence.strip(), body.target_word.strip())
from utils import REASON_ORDER
results = [
run_intervention(
predictor,
body.sentence.strip(),
body.target_word.strip(),
reason,
body.use_llm,
predicted_reason=body.predicted_reason,
)
for reason in REASON_ORDER
]
except RuntimeError as exc:
raise HTTPException(status_code=503, detail=str(exc)) from exc
best = max(results, key=lambda r: r.hardness_drop)
faithful = None
if body.predicted_reason:
faithful = best.reason == body.predicted_reason
return InterveneAllResponse(
base_level=base.complexity_level,
results=results,
best_reason=best.reason,
faithful=faithful,
)
@app.post("/api/efficiency", response_model=EfficiencyResponse)
def api_efficiency(body: EfficiencyRequest):
return compare_efficiency(body.sentence.strip(), body.target_word.strip(), body.local_latency_ms)
@app.post("/api/alter", response_model=AlterComplexityResponse)
def api_alter(body: AlterComplexityRequest):
from reason_interventions import alter_complexity
try:
result = alter_complexity(
body.reason,
body.sentence.strip(),
body.target_word.strip(),
body.direction,
)
except RuntimeError as exc:
raise HTTPException(status_code=503, detail=str(exc)) from exc
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
if result.reason == "Lexical Rarity":
updates = "Updated sentence and target word above — click Predict again."
else:
updates = "Updated sentence above (target word unchanged) — click Predict again."
return AlterComplexityResponse(
reason=result.reason,
direction=result.direction,
original_sentence=result.original_sentence,
original_target_word=result.original_target,
new_sentence=result.new_sentence,
new_target_word=result.new_target,
edit_method=result.edit_method,
explanation=result.explanation,
updates=updates,
)