FlashTriage / backend /batch_triage.py
Chris4K's picture
Upload 19 files
39a5a79 verified
Raw
History Blame Contribute Delete
3.82 kB
"""
Backlog-scale triage via the Cerebras Batch API (Private Preview).
The live console is for interactive, real-time triage. This is the other half of the
enterprise story: point it at a *huge* backlog (up to 50k findings) and let Cerebras
process it asynchronously, guaranteed within 24h. Same Analyst agent + strict schema.
python -m backend.batch_triage huge_scan.json
Notes:
- Batch is Private Preview; your model must be batch-enabled for your org.
- Minimum 10 requests per batch; this script auto-chunks at 50k.
- Results are NOT ordered — we match on custom_id.
"""
from __future__ import annotations
import json
import os
import sys
import time
import httpx
from dotenv import load_dotenv
from .agents import ANALYST_SCHEMA, ANALYST_SYS, _analyst_user
from .models import normalize
load_dotenv()
BASE = os.getenv("CEREBRAS_BASE_URL", "https://api.cerebras.ai/v1").rstrip("/")
MODEL = os.getenv("CEREBRAS_MODEL", "gemma-4-31b")
def _headers() -> dict:
key = os.environ.get("CEREBRAS_API_KEY")
if not key:
sys.exit("Set CEREBRAS_API_KEY to run a batch.")
return {"Authorization": f"Bearer {key}"}
def build_jsonl(findings: list[dict], path: str) -> None:
with open(path, "w", encoding="utf-8", newline="\n") as f:
for i, fd in enumerate(findings):
req = {
"custom_id": fd["id"] or f"f-{i}",
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": MODEL,
"max_completion_tokens": 350,
"messages": [
{"role": "system", "content": ANALYST_SYS},
{"role": "user", "content": _analyst_user(fd)},
],
"response_format": {"type": "json_schema", "json_schema":
{"name": "out", "strict": True, "schema": ANALYST_SCHEMA}},
},
}
f.write(json.dumps(req) + "\n")
def main(src: str) -> None:
findings = normalize(open(src, encoding="utf-8").read())
if len(findings) < 10:
sys.exit("Batch needs >= 10 findings; use the live console for small scans.")
print(f"{len(findings)} findings -> batch")
build_jsonl(findings, "/tmp/batch_in.jsonl")
with httpx.Client(timeout=120) as c:
up = c.post(f"{BASE}/files", headers=_headers(),
files={"purpose": (None, "batch"),
"file": ("batch_in.jsonl", open("/tmp/batch_in.jsonl", "rb"))})
up.raise_for_status()
file_id = up.json()["id"]
print("uploaded:", file_id)
b = c.post(f"{BASE}/batches", headers={**_headers(), "Content-Type": "application/json"},
json={"input_file_id": file_id, "endpoint": "/v1/chat/completions",
"completion_window": "24h",
"metadata": {"app": "flashtriage"}})
b.raise_for_status()
bid = b.json()["id"]
print("batch:", bid)
while True:
st = c.get(f"{BASE}/batches/{bid}", headers=_headers()).json()
rc = st.get("request_counts", {})
print(f" {st['status']} {rc.get('completed',0)}/{rc.get('total',0)}")
if st["status"] in ("completed", "failed", "expired", "cancelled"):
break
time.sleep(10)
out_id = st.get("output_file_id")
if not out_id:
sys.exit(f"no output ({st['status']})")
res = c.get(f"{BASE}/files/{out_id}/content", headers=_headers())
open("batch_results.jsonl", "wb").write(res.content)
print("wrote batch_results.jsonl")
if __name__ == "__main__":
main(sys.argv[1] if len(sys.argv) > 1 else "samples/sample_findings.json")