feat(pipeline): add Pass 0a endpoint to core control-pipeline
Registers /generate/run-pass0a and /generate/pass0a-status/{job_id}
on the core control-pipeline (port 8098). Previously Pass 0a was only
available on the compliance backend which connects to Production DB,
causing a split-brain when controls are generated locally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2293,6 +2293,67 @@ async def get_batch_process_status(job_id: str):
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
class RunPass0aRequest(BaseModel):
|
||||||
|
limit: int = 0 # 0 = no limit
|
||||||
|
batch_size: int = 5
|
||||||
|
use_anthropic: bool = True
|
||||||
|
category_filter: Optional[str] = None
|
||||||
|
source_filter: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
_pass0a_status: dict = {}
|
||||||
|
|
||||||
|
|
||||||
|
async def _run_pass0a_background(req: RunPass0aRequest, job_id: str):
|
||||||
|
"""Run Pass 0a in background with own DB session."""
|
||||||
|
from services.decomposition_pass import DecompositionPass
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
_pass0a_status[job_id] = {"status": "running"}
|
||||||
|
dp = DecompositionPass(db)
|
||||||
|
result = await dp.run_pass0a(
|
||||||
|
limit=req.limit,
|
||||||
|
batch_size=req.batch_size,
|
||||||
|
use_anthropic=req.use_anthropic,
|
||||||
|
category_filter=req.category_filter,
|
||||||
|
source_filter=req.source_filter,
|
||||||
|
)
|
||||||
|
_pass0a_status[job_id] = {"status": "completed", **result}
|
||||||
|
logger.info("Pass 0a job %s completed: %s", job_id, result)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Pass 0a job %s failed: %s", job_id, e)
|
||||||
|
_pass0a_status[job_id] = {"status": "failed", "error": str(e)}
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/generate/run-pass0a")
|
||||||
|
async def run_pass0a(req: RunPass0aRequest):
|
||||||
|
"""Run Pass 0a (Obligation Extraction) on undecomposed controls.
|
||||||
|
|
||||||
|
Extracts individual normative obligations from rich controls using LLM.
|
||||||
|
Runs in background — poll status via GET /generate/pass0a-status/{job_id}.
|
||||||
|
"""
|
||||||
|
import uuid
|
||||||
|
job_id = str(uuid.uuid4())[:8]
|
||||||
|
_pass0a_status[job_id] = {"status": "starting"}
|
||||||
|
asyncio.create_task(_run_pass0a_background(req, job_id))
|
||||||
|
return {
|
||||||
|
"status": "running",
|
||||||
|
"job_id": job_id,
|
||||||
|
"message": f"Pass 0a started. Poll /generate/pass0a-status/{job_id}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/generate/pass0a-status/{job_id}")
|
||||||
|
async def get_pass0a_status(job_id: str):
|
||||||
|
"""Get status of a Pass 0a job."""
|
||||||
|
status = _pass0a_status.get(job_id)
|
||||||
|
if not status:
|
||||||
|
raise HTTPException(status_code=404, detail="Pass 0a job not found")
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
class SubmitPass0bRequest(BaseModel):
|
class SubmitPass0bRequest(BaseModel):
|
||||||
limit: int = 10
|
limit: int = 10
|
||||||
batch_size: int = 5
|
batch_size: int = 5
|
||||||
|
|||||||
Reference in New Issue
Block a user