136 lines
6.1 KiB
Python
136 lines
6.1 KiB
Python
"""Authenticated SUPER authoring endpoints; construction never starts a simulation."""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
|
from pydantic import ValidationError
|
|
from sqlalchemy import func, select
|
|
|
|
from ..models import SuperSelectionSnapshot
|
|
from ..research.assets import Assets
|
|
from ..security import require_auth
|
|
from .contracts import BuildCandidates, ExperimentPreview, PlanSave, SelectionPreview, SelectionReference
|
|
from .evidence import read_selection, snapshot_output
|
|
from .metadata import metadata
|
|
from .service import SuperResearch
|
|
|
|
router = APIRouter(prefix="/api/v1/superalpha", tags=["superalpha"], dependencies=[Depends(require_auth)])
|
|
|
|
|
|
@router.get("/metadata")
|
|
async def get_metadata(request: Request):
|
|
async with request.app.state.sessions() as db:
|
|
return await metadata(db)
|
|
|
|
|
|
@router.get("/plans")
|
|
async def plans(request: Request, q: str = "", limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)):
|
|
async with request.app.state.sessions() as db:
|
|
return await Assets(db).list("superalpha_plan", q, limit, offset)
|
|
|
|
|
|
@router.post("/plans")
|
|
async def save_plan(body: PlanSave, request: Request):
|
|
async with request.app.state.sessions.begin() as db:
|
|
return await SuperResearch(db).save(body)
|
|
|
|
|
|
@router.get("/plans/{plan_id}")
|
|
async def plan(plan_id: str, request: Request, version: int | None = Query(None, ge=1)):
|
|
async with request.app.state.sessions() as db:
|
|
return await Assets(db).get(plan_id, version, "superalpha_plan")
|
|
|
|
|
|
@router.get("/plans/{plan_id}/versions")
|
|
async def versions(plan_id: str, request: Request):
|
|
async with request.app.state.sessions() as db:
|
|
await Assets(db).get(plan_id, expected_kind="superalpha_plan")
|
|
return await Assets(db).versions(plan_id)
|
|
|
|
|
|
@router.delete("/plans/{plan_id}")
|
|
async def archive(plan_id: str, request: Request, version: int = Query(..., ge=1)):
|
|
async with request.app.state.sessions.begin() as db:
|
|
await Assets(db).get(plan_id, expected_kind="superalpha_plan")
|
|
return await Assets(db).archive(plan_id, version)
|
|
|
|
|
|
@router.post("/selections", status_code=202)
|
|
async def preview_selection(body: SelectionPreview, request: Request):
|
|
async with request.app.state.sessions.begin() as db:
|
|
result = await SuperResearch(db).selection_job(body)
|
|
request.app.state.runner.wake.set()
|
|
return result
|
|
|
|
|
|
@router.get("/selections")
|
|
async def selection(request: Request, snapshot_id: str | None = None, job_id: str | None = None,
|
|
q: str = "", limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)):
|
|
try:
|
|
args = SelectionReference(snapshot_id=snapshot_id, job_id=job_id, q=q, limit=limit, offset=offset)
|
|
except ValidationError as exc:
|
|
raise HTTPException(422, str(exc)) from None
|
|
async with request.app.state.sessions() as db:
|
|
return await read_selection(db, args)
|
|
|
|
|
|
@router.post("/candidates", status_code=201)
|
|
async def build(body: BuildCandidates, request: Request):
|
|
async with request.app.state.sessions.begin() as db:
|
|
return await SuperResearch(db).build(body)
|
|
|
|
|
|
@router.get("/experiments")
|
|
async def experiments(request: Request, plan_id: str | None = None,
|
|
limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)):
|
|
async with request.app.state.sessions() as db:
|
|
return await SuperResearch(db).experiments(plan_id, limit, offset)
|
|
|
|
|
|
@router.get("/experiments/{experiment_id}")
|
|
async def experiment(experiment_id: str, request: Request, limit: int = Query(100, ge=1, le=100), offset: int = Query(0, ge=0)):
|
|
async with request.app.state.sessions() as db:
|
|
return await SuperResearch(db).experiment(experiment_id, limit, offset)
|
|
|
|
|
|
@router.post("/experiments/{experiment_id}/preview")
|
|
async def preview(experiment_id: str, body: ExperimentPreview, request: Request):
|
|
async with request.app.state.sessions.begin() as db:
|
|
return await SuperResearch(db).preview(experiment_id, body.candidate_ids)
|
|
|
|
|
|
@router.get("/alphas/{alpha_id}")
|
|
async def alpha(alpha_id: str, request: Request):
|
|
async with request.app.state.sessions() as db:
|
|
return await SuperResearch(db).alpha(alpha_id)
|
|
|
|
|
|
@router.get("/selection-history")
|
|
async def selection_history(request: Request, plan_id: str, limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)):
|
|
async with request.app.state.sessions() as db:
|
|
query = select(SuperSelectionSnapshot).where(SuperSelectionSnapshot.source == "preview", SuperSelectionSnapshot.request["plan_id"].as_string() == plan_id)
|
|
total = await db.scalar(select(func.count()).select_from(query.subquery()))
|
|
rows = await db.scalars(query.order_by(SuperSelectionSnapshot.observed_at.desc(), SuperSelectionSnapshot.id).limit(limit).offset(offset))
|
|
return {"items": [snapshot_output(row, 0) for row in rows], "total": total, "limit": limit, "offset": offset}
|
|
|
|
|
|
@router.get("/experiments/{experiment_id}/results")
|
|
async def experiment_results(experiment_id: str, request: Request, limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)):
|
|
from ..models import Pnl
|
|
from ..research.serialization import encode_snapshot
|
|
from ..research_access.contracts import History
|
|
from ..research_access.queries import EvidenceQueries
|
|
from .evidence import actual_components
|
|
async with request.app.state.sessions() as db:
|
|
await SuperResearch(db).experiment(experiment_id, 1)
|
|
result = await EvidenceQueries(db).history(History(research_id=experiment_id, alpha_type="SUPER", limit=limit, offset=offset))
|
|
for item in result["items"]:
|
|
item["components"] = await actual_components(db, item["id"], 0)
|
|
pnl = await db.get(Pnl, item["alpha_id"]) if item["alpha_id"] else None
|
|
item["pnl_fetched_at"] = pnl.fetched_at if pnl else None
|
|
return encode_snapshot(result)
|
|
|
|
|
|
@router.get("/alphas/{alpha_id}/components")
|
|
async def alpha_components(alpha_id: str, request: Request, limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)):
|
|
async with request.app.state.sessions() as db:
|
|
return (await SuperResearch(db).alpha(alpha_id, limit, offset))["components"]
|