-
Notifications
You must be signed in to change notification settings - Fork 0
Add OfficeAI-500 Sovereign Mesh demo: backend API, memory/capsule system, governance, audit and minimal frontend #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # OfficeAI-500-Sovereign-Mesh | ||
|
|
||
| OfficeAI-500 Sovereign Mesh is an enterprise task-orchestration MVP that simulates a 500-agent virtual office for operations and finops workflows. It is a governed execution kernel with durable storage, deterministic memory reuse, review gates, tamper checks, and auditable traces. | ||
|
|
||
| ## Not a chatbot / not plain RAG | ||
| This system routes structured tasks through governance, memory integrity checks, and review queues. It processes operational work units, not free-form assistant dialog. | ||
|
|
||
| ## Run backend | ||
| ```bash | ||
| cd backend | ||
| pip install -r requirements.txt | ||
| python run.py | ||
| ``` | ||
|
|
||
| ## Run frontend | ||
| ```bash | ||
| cd frontend | ||
| npm install | ||
| npm run dev | ||
| ``` | ||
|
|
||
| ## Run tests | ||
| ```bash | ||
| cd backend | ||
| pytest | ||
| ``` | ||
|
|
||
| ## Run 500 demo | ||
| ```bash | ||
| curl -X POST http://localhost:8000/demo/load | ||
| curl -X POST http://localhost:8000/demo/run_500 | ||
| curl -X POST http://localhost:8000/demo/run_500 | ||
| ``` | ||
| Second run should show increased `memory_hits` and reduced `memory_misses`. | ||
|
|
||
| ## Tamper detection | ||
| 1. Find a capsule via `/memory/search?q=`. | ||
| 2. Tamper with `POST /memory/{capsule_id}/tamper`. | ||
| 3. Verify with `GET /memory/{capsule_id}/verify`. | ||
| 4. Failed verification marks capsule `verified=false` and prevents reuse. | ||
|
|
||
| ## Enterprise office / finops mapping | ||
| - **Agents** = virtual back-office workers. | ||
| - **Tasks** = invoices, support tickets, reports, exceptions. | ||
| - **Governance** = policy and risk controls. | ||
| - **Review queue** = human checkpoint on risky/regulated work. | ||
| - **Memory capsules** = deterministic reuse for repeat workflows and spend reduction. | ||
| - **Audit traces** = compliance and incident visibility. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| from datetime import datetime | ||
| from hashlib import sha384 | ||
| import uuid | ||
| from .models import AuditTrace | ||
|
|
||
| def write_audit(db, task, memory_hit, governance_allowed, governance_status, risk_level, reason, output_text, latency_ms): | ||
| trace_id = str(uuid.uuid4()) | ||
| tr = AuditTrace( | ||
| trace_id=trace_id, | ||
| task_id=task.id, | ||
| agent_id=task.agent_id, | ||
| task_type=task.type, | ||
| memory_hit=memory_hit, | ||
| governance_allowed=governance_allowed, | ||
| governance_status=governance_status, | ||
| risk_level=risk_level, | ||
| reason=reason, | ||
| input_hash=sha384(str(task.payload).encode()).hexdigest(), | ||
| output_hash=sha384(str(output_text).encode()).hexdigest(), | ||
| latency_ms=latency_ms, | ||
| timestamp=datetime.utcnow(), | ||
| ) | ||
| db.add(tr) | ||
| db.commit() | ||
| return trace_id |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import os | ||
|
|
||
| class Settings: | ||
| DATABASE_URL = os.getenv("OFFICEAI_DB_URL", "sqlite:///./officeai.db") | ||
| SECRET_SALT = os.getenv("OFFICEAI_SECRET_SALT", "officeai-local-safe-salt") | ||
| BUDGET_LIMIT = 50000.0 | ||
| COST_PER_MISS = 0.12 | ||
|
|
||
| settings = Settings() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| from sqlalchemy import create_engine | ||
| from sqlalchemy.orm import declarative_base, sessionmaker | ||
| from .config import settings | ||
|
|
||
| engine = create_engine(settings.DATABASE_URL, connect_args={"check_same_thread": False}) | ||
| SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False) | ||
| Base = declarative_base() | ||
|
|
||
| def get_db(): | ||
| db = SessionLocal() | ||
| try: | ||
| yield db | ||
| finally: | ||
| db.close() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| from .models import Agent | ||
| from .schemas import TaskInput | ||
|
|
||
| def ensure_agents(db, total=500): | ||
| count = db.query(Agent).count() | ||
| for i in range(count + 1, total + 1): | ||
| db.add(Agent(id=i, role=f"office_agent_{i}")) | ||
| db.commit() | ||
|
|
||
| def stable_tasks(): | ||
| types = ["GENERAL","EMAIL","SUPPORT","REPORT","RESEARCH","COMPLIANCE","FINANCIAL","INVOICE","PAYMENT_EXCEPTION","GENERAL"] | ||
| tasks = [] | ||
| for i in range(500): | ||
| t = types[i % len(types)] | ||
| payload = {"subject": f"item-{i%50}", "recipient": f"u{i}@ex.com", "vendor": f"v{i%25}"} | ||
| amount = 100.0 + (i % 30) * 250 | ||
| if t == "INVOICE" and i % 33 == 0: | ||
| payload.pop("vendor", None) | ||
| tasks.append(TaskInput(type=t, payload=payload, amount=amount, priority=(i % 5) + 1)) | ||
| return tasks |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| from hashlib import sha256 | ||
|
|
||
| VALID_TYPES = {"GENERAL","EMAIL","SUPPORT","REPORT","RESEARCH","COMPLIANCE","FINANCIAL","INVOICE","PAYMENT_EXCEPTION"} | ||
|
|
||
| def deterministic_conviction(task_type: str, payload: str) -> float: | ||
| h = sha256(f"{task_type}|{payload}".encode()).hexdigest() | ||
| return int(h[:8], 16) / 0xFFFFFFFF | ||
|
|
||
| def apply_governance(task_type: str, payload: dict, amount: float): | ||
| if task_type not in VALID_TYPES: | ||
| return False, "failed", "high", "unknown task type" | ||
| if not payload: | ||
| return False, "failed", "high", "empty payload" | ||
| if task_type == "INVOICE" and not payload.get("vendor"): | ||
| return False, "failed", "high", "missing vendor" | ||
| if task_type == "EMAIL" and not payload.get("recipient"): | ||
| return False, "failed", "high", "missing recipient" | ||
| if task_type == "COMPLIANCE": | ||
| return False, "needs_review", "medium", "compliance review required" | ||
| if task_type == "PAYMENT_EXCEPTION" and amount > 5000: | ||
| return False, "needs_review", "high", "high amount payment exception" | ||
| if task_type == "FINANCIAL": | ||
| c = deterministic_conviction(task_type, str(payload)) | ||
| if c < 0.95: | ||
| return False, "needs_review", "medium", f"low conviction {c:.4f}" | ||
| return True, "completed", "low", "policy pass" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| from fastapi import FastAPI, Depends, HTTPException | ||
| from sqlalchemy.orm import Session | ||
| from .database import Base, engine, get_db | ||
| from .models import Agent, Task, MemoryCapsule, AuditTrace | ||
| from .schemas import TaskInput, BatchInput | ||
| from .mesh import process_task | ||
| from .demo import ensure_agents, stable_tasks | ||
| from .memory import write_memory, lookup_memory, capsule_id_for, integrity_for | ||
| from .governance import deterministic_conviction | ||
| import uuid | ||
|
|
||
| Base.metadata.create_all(bind=engine) | ||
| app = FastAPI(title="OfficeAI-500 Sovereign Mesh") | ||
|
|
||
| @app.get('/health') | ||
| def health(): return {"status": "ok"} | ||
|
|
||
| @app.get('/agents') | ||
| def agents(db: Session = Depends(get_db)): return [a.__dict__ for a in db.query(Agent).all()] | ||
|
|
||
| @app.get('/tasks') | ||
| def tasks(limit: int = 50, db: Session = Depends(get_db)): return [t.__dict__ for t in db.query(Task).order_by(Task.id.desc()).limit(limit).all()] | ||
|
|
||
| @app.get('/tasks/{task_id}') | ||
| def task(task_id: int, db: Session = Depends(get_db)): | ||
| t = db.query(Task).filter(Task.id == task_id).first() | ||
| if not t: raise HTTPException(404, "not found") | ||
| return t.__dict__ | ||
|
|
||
| @app.post('/mesh/dispatch') | ||
| def dispatch(task_in: TaskInput, db: Session = Depends(get_db)): | ||
| task = Task(type=task_in.type, payload=str(task_in.payload), amount=task_in.amount, priority=task_in.priority) | ||
| db.add(task); db.commit(); db.refresh(task) | ||
| return process_task(db, task) | ||
|
|
||
| @app.post('/mesh/batch') | ||
| def batch(body: BatchInput, db: Session = Depends(get_db)): | ||
| out=[] | ||
| for ti in body.tasks: | ||
| task = Task(type=ti.type, payload=str(ti.payload), amount=ti.amount, priority=ti.priority) | ||
| db.add(task); db.commit(); db.refresh(task) | ||
| out.append(process_task(db, task)) | ||
| return {"results": out} | ||
|
|
||
| @app.post('/demo/load') | ||
| def load_demo(db: Session = Depends(get_db)): | ||
| ensure_agents(db, 500) | ||
| return {"agents_loaded": db.query(Agent).count()} | ||
|
|
||
| @app.post('/demo/run_500') | ||
| def run_500(db: Session = Depends(get_db)): | ||
| ensure_agents(db, 500) | ||
| tasks = stable_tasks(); results=[] | ||
| for ti in tasks: | ||
| task = Task(type=ti.type, payload=str(ti.payload), amount=ti.amount, priority=ti.priority) | ||
| db.add(task); db.commit(); db.refresh(task) | ||
| results.append(process_task(db, task)) | ||
| completed = sum(1 for r in results if r['status']=='completed') | ||
| needs_review = sum(1 for r in results if r['status']=='needs_review') | ||
| failed = sum(1 for r in results if r['status']=='failed') | ||
| hits = sum(1 for r in results if r['memory_hit']) | ||
| misses = len(results)-hits | ||
| total_latency = sum(r['latency_ms'] for r in results) | ||
| savings = hits * 0.12 | ||
| return {"batch_id": str(uuid.uuid4()), "submitted": len(results), "completed": completed, "needs_review": needs_review, "failed": failed, "memory_hits": hits, "memory_misses": misses, "total_latency_ms": total_latency, "average_latency_ms": total_latency/len(results), "estimated_api_savings": round(savings,2)} | ||
|
|
||
| @app.get('/audit/{task_id}') | ||
| def audit(task_id:int, db: Session=Depends(get_db)): | ||
| traces = db.query(AuditTrace).filter(AuditTrace.task_id==task_id).all() | ||
| return [t.__dict__ for t in traces] | ||
|
|
||
| @app.get('/memory/search') | ||
| def mem_search(q:str="", db: Session=Depends(get_db)): | ||
| rows = db.query(MemoryCapsule).filter(MemoryCapsule.task_type.contains(q) | MemoryCapsule.result.contains(q)).limit(100).all() | ||
| return [r.__dict__ for r in rows] | ||
|
|
||
| @app.post('/memory/{capsule_id}/tamper') | ||
| def tamper(capsule_id:str, db:Session=Depends(get_db)): | ||
| c = db.query(MemoryCapsule).filter(MemoryCapsule.capsule_id==capsule_id).first() | ||
| if not c: raise HTTPException(404, "not found") | ||
| c.result = c.result + "::tampered" | ||
| db.commit() | ||
| return {"tampered": True, "capsule_id": capsule_id} | ||
|
|
||
| @app.get('/memory/{capsule_id}/verify') | ||
| def verify(capsule_id:str, db:Session=Depends(get_db)): | ||
| c = db.query(MemoryCapsule).filter(MemoryCapsule.capsule_id==capsule_id).first() | ||
| if not c: raise HTTPException(404, "not found") | ||
| candidate = eval(db.query(Task).filter(Task.type==c.task_type).first().payload) | ||
| ok = integrity_for(c.task_type, candidate, c.result) == c.integrity_hash | ||
| if not ok: | ||
| c.verified = False | ||
| db.commit() | ||
| return {"capsule_id": capsule_id, "verified": ok} | ||
|
|
||
| @app.post('/review/{task_id}/approve') | ||
| def approve(task_id:int, db: Session=Depends(get_db)): | ||
| t = db.query(Task).filter(Task.id==task_id).first() | ||
| if not t: raise HTTPException(404, "not found") | ||
| if t.status != "needs_review": raise HTTPException(400, "task not in review") | ||
| payload = eval(t.payload) | ||
| t.status = "completed" | ||
| t.result = t.result or f"approved:{t.type}" | ||
| write_memory(db, t.type, payload, t.result, deterministic_conviction(t.type, str(payload))) | ||
| db.commit() | ||
| return {"task_id": task_id, "status": t.status} | ||
|
|
||
| @app.post('/review/{task_id}/reject') | ||
| def reject(task_id:int, db: Session=Depends(get_db)): | ||
| t = db.query(Task).filter(Task.id==task_id).first() | ||
| if not t: raise HTTPException(404, "not found") | ||
| if t.status != "needs_review": raise HTTPException(400, "task not in review") | ||
| t.status = "rejected"; db.commit(); return {"task_id": task_id, "status": t.status} | ||
|
|
||
| @app.get('/metrics') | ||
| def metrics(db: Session=Depends(get_db)): | ||
| tasks = db.query(Task).all() | ||
| agents = db.query(Agent).all() | ||
| hits = db.query(AuditTrace).filter(AuditTrace.memory_hit==True).count() | ||
| misses = db.query(AuditTrace).filter(AuditTrace.memory_hit==False).count() | ||
| current_spend = misses * 0.12 | ||
| counts = {s: sum(1 for t in tasks if t.status==s) for s in ["queued","in_progress","completed","needs_review","failed","rejected"]} | ||
| return {"agents_total": len(agents), "agents_idle": sum(1 for a in agents if a.status=="idle"), "agents_busy": sum(1 for a in agents if a.status=="busy"), "tasks_total": len(tasks), **counts, "memory_capsules": db.query(MemoryCapsule).count(), "memory_hits": hits, "memory_misses": misses, "estimated_api_savings": round(hits*0.12,2), "budget_limit": 50000.0, "current_spend": round(current_spend,2)} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import json | ||
| from hashlib import sha384 | ||
| from datetime import datetime | ||
| from .config import settings | ||
| from .models import MemoryCapsule | ||
|
|
||
| def normalize_payload(payload): | ||
| return json.dumps(payload, sort_keys=True, separators=(",",":")) | ||
|
|
||
| def capsule_id_for(task_type, payload): | ||
| normalized = normalize_payload(payload) | ||
| return sha384(f"{task_type}|{normalized}".encode()).hexdigest()[:32] | ||
|
|
||
| def integrity_for(task_type, payload, result): | ||
| normalized = normalize_payload(payload) | ||
| return sha384(f"{settings.SECRET_SALT}|{task_type}|{normalized}|{result}".encode()).hexdigest() | ||
|
|
||
| def lookup_memory(db, task_type, payload): | ||
| cid = capsule_id_for(task_type, payload) | ||
| capsule = db.query(MemoryCapsule).filter(MemoryCapsule.capsule_id == cid).first() | ||
| if not capsule or not capsule.verified: | ||
| return None, cid | ||
| expected = integrity_for(task_type, payload, capsule.result) | ||
| if expected != capsule.integrity_hash: | ||
| capsule.verified = False | ||
| db.commit() | ||
| return None, cid | ||
| capsule.use_count += 1 | ||
| capsule.last_used_at = datetime.utcnow() | ||
| db.commit() | ||
| db.refresh(capsule) | ||
| return capsule, cid | ||
|
|
||
| def write_memory(db, task_type, payload, result, confidence): | ||
| normalized = normalize_payload(payload) | ||
| cid = capsule_id_for(task_type, payload) | ||
| existing = db.query(MemoryCapsule).filter(MemoryCapsule.capsule_id == cid).first() | ||
| if existing and existing.verified: | ||
| return existing | ||
| capsule = MemoryCapsule( | ||
| capsule_id=cid, | ||
| task_type=task_type, | ||
| input_hash=sha384(f"{task_type}|{normalized}".encode()).hexdigest(), | ||
| payload_hash=sha384(normalized.encode()).hexdigest(), | ||
| result=result, | ||
| confidence=confidence, | ||
| integrity_hash=integrity_for(task_type, payload, result), | ||
| verified=True, | ||
| ) | ||
| db.merge(capsule) | ||
| db.commit() | ||
| return capsule |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| from datetime import datetime | ||
| import time | ||
| from .models import Agent, Task | ||
| from .memory import lookup_memory, write_memory | ||
| from .governance import apply_governance, deterministic_conviction | ||
| from .audit import write_audit | ||
|
|
||
|
|
||
| def assign_agent(db): | ||
| agent = db.query(Agent).filter(Agent.status == "idle").first() | ||
| if not agent: | ||
| return None | ||
| agent.status = "busy" | ||
| agent.last_seen = datetime.utcnow() | ||
| db.commit() | ||
| db.refresh(agent) | ||
| return agent | ||
|
|
||
| def release_agent(db, agent, success): | ||
| agent.status = "idle" | ||
| agent.current_task_id = None | ||
| if success: | ||
| agent.completed_count += 1 | ||
| else: | ||
| agent.failed_count += 1 | ||
| agent.last_seen = datetime.utcnow() | ||
| db.commit() | ||
|
|
||
| def process_task(db, task): | ||
| t0 = time.time() | ||
| agent = assign_agent(db) | ||
| if not agent: | ||
| task.status = "failed" | ||
| task.error = "no idle agents" | ||
| db.commit() | ||
| return {"task_id": task.id, "status": task.status, "memory_hit": False, "latency_ms": 0} | ||
| task.agent_id = agent.id | ||
| task.status = "in_progress" | ||
| agent.current_task_id = task.id | ||
| task.updated_at = datetime.utcnow() | ||
| db.commit() | ||
|
|
||
| payload = eval(task.payload) if isinstance(task.payload, str) else task.payload | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The dispatcher executes Useful? React with 👍 / 👎. |
||
| capsule, _ = lookup_memory(db, task.type, payload) | ||
| memory_hit = capsule is not None | ||
|
|
||
| if memory_hit: | ||
| task.result = capsule.result | ||
| task.status = "completed" | ||
| task.error = None | ||
| allowed, gov_status, risk, reason = True, "completed", "low", "memory reuse" | ||
| else: | ||
| allowed, gov_status, risk, reason = apply_governance(task.type, payload, task.amount) | ||
| if gov_status == "failed": | ||
| task.status = "failed" | ||
| task.error = reason | ||
| task.result = None | ||
| elif gov_status == "needs_review": | ||
| task.status = "needs_review" | ||
| task.result = f"pending_review:{task.type}" | ||
| else: | ||
| task.status = "completed" | ||
| task.result = f"processed:{task.type}:{payload}" | ||
| write_memory(db, task.type, payload, task.result, deterministic_conviction(task.type, str(payload))) | ||
| task.updated_at = datetime.utcnow() | ||
|
|
||
| latency = int((time.time() - t0) * 1000) | ||
| task.trace_id = write_audit(db, task, memory_hit, allowed, gov_status, risk, reason, task.result or task.error, latency) | ||
| db.commit() | ||
| release_agent(db, agent, task.status == "completed") | ||
| return {"task_id": task.id, "status": task.status, "memory_hit": memory_hit, "latency_ms": latency} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integrity verification is recomputed using the payload from the first task with the same
task_typeinstead of the payload that produced this capsule. When multiple tasks share a type but differ in payload,/memory/{capsule_id}/verifycan incorrectly mark an untampered capsule as invalid (verified=False), which corrupts memory reuse behavior and auditability.Useful? React with 👍 / 👎.