"""Authenticated research workspace; previewing never starts a platform simulation.""" from fastapi import APIRouter, Depends, HTTPException, Query, Request from ..security import require_auth from .assets import Assets from .comparisons import compare from .experiments import Experiments from .model import request_model from .workspace_contracts import ( AssetWrite, CompareInput, Expansion, ExperimentPreview, Generation, ImportCommit, ImportPreview, SettingVariants, ) router = APIRouter(prefix="/api/v1/research", tags=["research"], dependencies=[Depends(require_auth)]) @router.get("/inputs") async def inputs(request: Request, limit: int = Query(100, ge=1, le=100)): async with request.app.state.sessions() as db: return await Experiments(db).available_inputs(limit) @router.get("/assets") async def assets( request: Request, kind: str = "template", q: str = "", limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0), ): if kind != "template": raise HTTPException(422, "当前素材类型尚未开放") async with request.app.state.sessions() as db: return await Assets(db).list(kind, q, limit, offset) @router.post("/assets", status_code=201) async def save_asset(body: AssetWrite, request: Request): if body.kind != "template": raise HTTPException(422, "当前素材类型尚未开放") async with request.app.state.sessions.begin() as db: return await Assets(db).save(body) @router.get("/assets/{asset_id}") async def asset(asset_id: str, request: Request, version: int | None = Query(None, ge=1)): async with request.app.state.sessions() as db: return await Assets(db).get(asset_id, version) @router.put("/assets/{asset_id}") async def update_asset(asset_id: str, body: AssetWrite, request: Request): if body.kind != "template": raise HTTPException(422, "当前素材类型尚未开放") async with request.app.state.sessions.begin() as db: return await Assets(db).save(body, asset_id) @router.delete("/assets/{asset_id}") async def archive_asset(asset_id: str, request: Request, version: int = Query(ge=1)): async with request.app.state.sessions.begin() as db: return await Assets(db).archive(asset_id, version) @router.get("/assets/{asset_id}/versions") async def versions(asset_id: str, request: Request): async with request.app.state.sessions() as db: return await Assets(db).versions(asset_id) @router.post("/templates/import-preview") async def import_preview(body: ImportPreview, request: Request): async with request.app.state.sessions() as db: return await Assets(db).import_preview(body.templates) @router.post("/templates/import", status_code=201) async def import_commit(body: ImportCommit, request: Request): async with request.app.state.sessions.begin() as db: return await Assets(db).import_commit(body) @router.post("/generate", status_code=201) async def generate(body: Generation, request: Request): if body.method == "feature": raise HTTPException(422, "特征方案生成将在特征工程阶段开放") async with request.app.state.sessions() as db: context = await Experiments(db).generation_context(body) result, evidence = await request_model(request.app.state.ai, context) async with request.app.state.sessions.begin() as db: asset = await Assets(db).save( AssetWrite(kind="template", content=result.model_dump(mode="json")), provenance={"generation": evidence, "context": context}, ) return { **asset, "generation": evidence, "parent_alpha_ids": body.parent_alpha_ids, "parent_experiment_ids": body.parent_experiment_ids, } @router.post("/experiments", status_code=201) async def expand(body: Expansion, request: Request): async with request.app.state.sessions.begin() as db: kind = "variant" if body.parent_alpha_ids or body.parent_experiment_ids else "template" return await Experiments(db).create( body, kind, {"method": "structure" if kind == "variant" else "template"} ) @router.get("/experiments") async def experiments( request: Request, kind: 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 Experiments(db).list(kind, limit, offset) @router.get("/experiments/{experiment_id}") async def experiment(experiment_id: str, request: Request): async with request.app.state.sessions() as db: return await Experiments(db).get(experiment_id) @router.post("/experiments/{experiment_id}/preview", status_code=201) async def preview(experiment_id: str, body: ExperimentPreview, request: Request): async with request.app.state.sessions.begin() as db: return await Experiments(db).preview(experiment_id, body.candidate_ids) @router.post("/variants/settings", status_code=201) async def settings_variants(body: SettingVariants, request: Request): async with request.app.state.sessions.begin() as db: return await Experiments(db).setting_variants(body) @router.post("/compare") async def comparison(body: CompareInput, request: Request): async with request.app.state.sessions() as db: return await compare(db, body.alpha_ids)