whassan's picture
Deploy Sensorflow Studio (part 2)
5d10875 verified
Raw History Blame Contribute Delete
11.9 kB
"""FastAPI router for the agentic launch-readiness subsystem (/api/agentic/*).
Endpoint map:
POST /api/agentic/failures/detect layer-1 scan
GET /api/agentic/failures failure queue
GET /api/agentic/failures/{id} one failure
GET /api/agentic/failures/{id}/state pipeline state
POST /api/agentic/failures/{id}/analyze[?stage=] advance the pipeline
GET /api/agentic/failures/{id}/evidence evidence graph
GET /api/agentic/failures/{id}/snippets failure snippets
POST /api/agentic/failures/{id}/cluster scenario mining on demand
POST /api/agentic/failures/{id}/safety-assessment safety impact on demand
POST /api/agentic/failures/{id}/launch-assessment launch decision stage
POST /api/agentic/failures/{id}/human-review record a human decision
GET /api/agentic/policy[?version=] active/specific policy
POST /api/agentic/policy save a new version
GET /api/agentic/policy/versions version list
POST /api/agentic/evaluation-suites flywheel stage for a failure
GET /api/agentic/evaluation-suites list suites
GET /api/agentic/evaluation-suites/{id} one suite
POST /api/agentic/evaluation-suites/{id}/promote-member guarded promotion
POST /api/agentic/regression/evaluate suite regression hook
GET /api/agentic/scorecards/{id} scorecard
GET /api/agentic/audit/{failure_id} immutable audit trail
GET /api/agentic/worked-example deterministic walkthrough
"""
from __future__ import annotations
from typing import Dict, List, Optional
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field
from sensorflow.agentic import data as data_mod
from sensorflow.agentic import flywheel as flywheel_mod
from sensorflow.agentic import pipeline as pipeline_mod
from sensorflow.agentic import policy as policy_mod
from sensorflow.agentic import review as review_mod
from sensorflow.agentic import scorecard as scorecard_mod
from sensorflow.agentic import store as store_mod
from sensorflow.agentic import worked_example as we_mod
from sensorflow.agentic.agents import SafetyImpactAgent, ScenarioMiningAgent
from sensorflow.agentic.models import STAGES
router = APIRouter(prefix="/api/agentic", tags=["agentic"])
def _failure_or_404(failure_id: str):
failure = pipeline_mod.get_failure(failure_id)
if failure is None:
raise HTTPException(404, f"Unknown failure {failure_id}")
return failure
# ------------------------------------------------------------------ detection
class DetectRequest(BaseModel):
seed: int = data_mod.DEFAULT_SEED
use_llm: bool = False
@router.post("/failures/detect")
def detect(req: Optional[DetectRequest] = None):
req = req or DetectRequest()
events = pipeline_mod.detect_failures(seed=req.seed, use_llm=req.use_llm)
return {"status": "ok", "detected": len(events),
"failures": [{"failure_id": e.failure_id, "kind": e.kind,
"title": e.title,
"candidate_events": e.detection_basis.candidate_events,
"denominator": e.detection_basis.denominator}
for e in events]}
@router.get("/failures")
def failure_queue():
return {"status": "ok", "failures": pipeline_mod.list_failures(),
"stages": STAGES}
@router.get("/failures/{failure_id}")
def failure_detail(failure_id: str):
return {"status": "ok", "failure": _failure_or_404(failure_id).model_dump()}
@router.get("/failures/{failure_id}/state")
def failure_state(failure_id: str):
state = pipeline_mod.get_state(failure_id)
if state is None:
raise HTTPException(404, f"Unknown failure {failure_id}")
return {"status": "ok", "state": state.model_dump()}
# ------------------------------------------------------------------ pipeline
class AnalyzeRequest(BaseModel):
seed: int = data_mod.DEFAULT_SEED
use_llm: bool = False
policy_input_overrides: Dict = Field(default_factory=dict)
@router.post("/failures/{failure_id}/analyze")
def analyze(failure_id: str, req: Optional[AnalyzeRequest] = None,
stage: Optional[str] = Query(default=None)):
_failure_or_404(failure_id)
req = req or AnalyzeRequest()
try:
state = pipeline_mod.run_stage(
failure_id, stage, seed=req.seed, use_llm=req.use_llm,
policy_input_overrides=req.policy_input_overrides or None)
except ValueError as e:
raise HTTPException(409, str(e))
return {"status": "ok", "state": state.model_dump()}
@router.get("/failures/{failure_id}/evidence")
def evidence(failure_id: str):
graph = pipeline_mod.get_evidence_graph(failure_id)
if graph is None:
raise HTTPException(404, "Evidence graph not built yet; run the "
"EVIDENCE_AGGREGATION stage first")
return {"status": "ok", "graph": graph.model_dump()}
@router.get("/failures/{failure_id}/snippets")
def snippets(failure_id: str):
_failure_or_404(failure_id)
return {"status": "ok", "snippets": pipeline_mod.get_snippets(failure_id)}
@router.post("/failures/{failure_id}/cluster")
def cluster(failure_id: str, req: Optional[AnalyzeRequest] = None):
failure = _failure_or_404(failure_id)
req = req or AnalyzeRequest()
result = ScenarioMiningAgent(use_llm=req.use_llm).run(
failure_id, failure=failure)
return {"status": "ok", "result": result.model_dump()}
@router.post("/failures/{failure_id}/safety-assessment")
def safety_assessment(failure_id: str, req: Optional[AnalyzeRequest] = None):
failure = _failure_or_404(failure_id)
req = req or AnalyzeRequest()
result = SafetyImpactAgent(use_llm=req.use_llm).run(
failure_id, failure=failure)
return {"status": "ok", "result": result.model_dump()}
@router.post("/failures/{failure_id}/launch-assessment")
def launch_assessment(failure_id: str, req: Optional[AnalyzeRequest] = None):
_failure_or_404(failure_id)
req = req or AnalyzeRequest()
try:
state = pipeline_mod.run_stage(
failure_id, "LAUNCH_DECISION", seed=req.seed, use_llm=req.use_llm,
policy_input_overrides=req.policy_input_overrides or None)
except ValueError as e:
raise HTTPException(409, str(e))
return {"status": "ok", "policy_evaluation": state.policy_evaluation,
"scorecard_id": state.scorecard_id}
# ------------------------------------------------------------------ human review
class HumanReviewRequest(BaseModel):
reviewer: str
decision: str
rationale: str
approved_option: Optional[str] = None
evidence_reviewed: List[str] = Field(default_factory=list)
override_reason: Optional[str] = None
@router.post("/failures/{failure_id}/human-review")
def human_review(failure_id: str, req: HumanReviewRequest):
failure = _failure_or_404(failure_id)
state = pipeline_mod.get_state(failure_id)
policy_version = (state.policy_evaluation or {}).get("policy_version", "") \
if state else ""
try:
rec = review_mod.record_decision(
failure, reviewer=req.reviewer, decision=req.decision,
rationale=req.rationale, evidence_reviewed=req.evidence_reviewed,
policy_version=policy_version,
approved_option=req.approved_option,
override_reason=req.override_reason)
except Exception as e:
raise HTTPException(400, str(e))
if req.decision == "confirm_failure":
failure.validated = True
elif req.decision == "reject_failure":
failure.validated = False
failure.status = "closed"
pipeline_mod.save_failure(failure)
return {"status": "ok", "review": rec.model_dump(),
"decisions": review_mod.decisions_for(failure_id)}
@router.get("/failures/{failure_id}/human-review")
def human_review_list(failure_id: str):
_failure_or_404(failure_id)
return {"status": "ok", "decisions": review_mod.decisions_for(failure_id)}
# ------------------------------------------------------------------ policy
@router.get("/policy")
def get_policy(version: Optional[str] = None):
try:
return {"status": "ok", "policy": policy_mod.get_policy(version)}
except KeyError as e:
raise HTTPException(404, str(e))
class PolicyRequest(BaseModel):
policy: Dict
actor: str = "api"
@router.post("/policy")
def save_policy(req: PolicyRequest):
doc = policy_mod.save_policy(req.policy, actor=req.actor)
return {"status": "ok", "policy_version": doc["policy_version"],
"policy": doc}
@router.get("/policy/versions")
def policy_versions():
return {"status": "ok", "versions": policy_mod.list_policies()}
# ------------------------------------------------------------------ flywheel
class SuiteRequest(BaseModel):
failure_id: str
use_llm: bool = False
@router.post("/evaluation-suites")
def create_suite(req: SuiteRequest):
_failure_or_404(req.failure_id)
try:
state = pipeline_mod.run_stage(req.failure_id, "LEARNING_FLYWHEEL",
use_llm=req.use_llm)
except ValueError as e:
raise HTTPException(409, str(e))
rec = state.stage_record("LEARNING_FLYWHEEL")
return {"status": "ok", "stage_status": rec.status, "detail": rec.detail,
"suite_ids": state.suite_ids}
@router.get("/evaluation-suites")
def list_suites():
return {"status": "ok", "suites": flywheel_mod.list_suites()}
@router.get("/evaluation-suites/{suite_id}")
def suite_detail(suite_id: str):
suite = flywheel_mod.get_suite(suite_id)
if suite is None:
raise HTTPException(404, f"Unknown suite {suite_id}")
return {"status": "ok", "suite": suite.model_dump()}
class PromoteRequest(BaseModel):
member_id: str
actor: Optional[str] = None
override_reason: Optional[str] = None
@router.post("/evaluation-suites/{suite_id}/promote-member")
def promote_member(suite_id: str, req: PromoteRequest):
try:
result = flywheel_mod.promote_member_to_training(
suite_id, req.member_id, actor=req.actor,
override_reason=req.override_reason)
except flywheel_mod.LeakageError as e:
raise HTTPException(403, str(e))
except KeyError as e:
raise HTTPException(404, str(e))
return {"status": "ok", **result}
class RegressionRequest(BaseModel):
seed: int = data_mod.DEFAULT_SEED
@router.post("/regression/evaluate")
def regression_evaluate(req: Optional[RegressionRequest] = None):
req = req or RegressionRequest()
return {"status": "ok", **flywheel_mod.regression_evaluate(seed=req.seed)}
# ------------------------------------------------------------------ scorecards / audit
@router.get("/scorecards/{scorecard_id}")
def scorecard(scorecard_id: str):
card = scorecard_mod.load_scorecard(scorecard_id)
if card is None:
raise HTTPException(404, f"Unknown scorecard {scorecard_id}")
return {"status": "ok", "scorecard": card}
@router.get("/audit/{failure_id}")
def audit(failure_id: str):
trail = store_mod.audit_trail(failure_id)
return {"status": "ok", "records": trail,
"chain": store_mod.verify_audit_chain(failure_id)}
# ------------------------------------------------------------------ worked example
@router.get("/worked-example")
def worked_example(refresh: bool = False, use_llm: bool = False):
if not refresh:
cached = store_mod.read_json("worked_example", "latest.json")
if cached:
return {"status": "ok", "walkthrough": cached, "cached": True}
walkthrough = we_mod.run_worked_example(use_llm=use_llm)
return {"status": "ok", "walkthrough": walkthrough, "cached": False}