"""
ForensicAI 2nd Brain — Hugging Face Space
Interactive morphic simulation UI + GPT Action API endpoints.
"""
from __future__ import annotations
import base64, hashlib, os
from pathlib import Path
from typing import Optional
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse, Response, JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field
import brain as brain_store
from simulation import generate_forensic_image, generate_case_heatmap, CASE_TYPES
SIMS_DIR = Path("/tmp/forensic_sims")
SIMS_DIR.mkdir(parents=True, exist_ok=True)
app = FastAPI(title="ForensicAI 2nd Brain", version="1.0.0")
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
app.mount("/sims", StaticFiles(directory=str(SIMS_DIR)), name="sims")
# ── Interactive UI ─────────────────────────────────────────────────────────────
HTML = """
ForensicAI · 2nd Brain
⬡ FORENSICAI · 2ND BRAIN
GENERATION 001
BRAIN STATE
SESSIONS0
ANALYSES0
EVIDENCE ITEMS0
AVG CONFIDENCE0.70
DOMINANT CASE—
SIM PARAMETERS
FEED RATE0.0370
KILL RATE0.0600
GRID120 × 120
MORPHIC ENGINE ACTIVE
f=0.0370 k=0.0600
"""
# ── Models ────────────────────────────────────────────────────────────────────
class SimulateRequest(BaseModel):
dominant_case: str = Field("default")
evidence_confidence: float = Field(0.70, ge=0.0, le=1.0)
case_complexity: float = Field(0.50, ge=0.0, le=1.0)
out_size: int = Field(512, ge=128, le=1024)
class BrainUpdateRequest(BaseModel):
case_type: Optional[str] = None
confidence: Optional[float] = None
complexity: Optional[float] = None
n_evidence: int = Field(0, ge=0)
insight: Optional[str] = None
# ── Endpoints ─────────────────────────────────────────────────────────────────
@app.get("/", response_class=HTMLResponse)
def index():
return HTML
@app.get("/health")
def health():
return {"status": "ok", "service": "forensicai-2nd-brain"}
@app.get("/brain")
def get_brain():
return JSONResponse(brain_store.load())
@app.post("/brain/update")
def update_brain(body: BrainUpdateRequest):
state = brain_store.update(
case_type=body.case_type,
confidence=body.confidence,
complexity=body.complexity,
n_evidence=body.n_evidence,
insight=body.insight,
)
return JSONResponse(state)
@app.post("/simulate")
def simulate(body: SimulateRequest):
state = brain_store.load()
png = generate_forensic_image(
dominant_case=body.dominant_case if body.dominant_case != "default" else state["dominant_case"],
evidence_confidence=body.evidence_confidence,
session_count=state["session_count"],
pattern_generation=state["pattern_generation"],
case_complexity=body.case_complexity,
out_size=body.out_size,
)
return {
"image_b64": base64.b64encode(png).decode(),
"mime": "image/png",
"pattern_generation": state["pattern_generation"],
"feed_rate": state["feed_rate"],
"kill_rate": state["kill_rate"],
"dominant_case": state["dominant_case"],
"display_hint": "Render image_b64 as: ",
}
@app.get("/brain/visualize/b64")
def brain_visualize_b64():
state = brain_store.load()
png = generate_case_heatmap(
case_history=[],
confidence_history=state["confidence_history"],
case_frequency=state["case_frequency"],
out_size=512,
)
return {
"image_b64": base64.b64encode(png).decode(),
"mime": "image/png",
"display_hint": "Render as: ",
}
@app.post("/brain/reset")
def reset_brain():
return JSONResponse(brain_store.reset())