Spaces:
Sleeping
Sleeping
File size: 11,922 Bytes
4944128 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | """Mock CCAI backend for UI rendering/screenshots ONLY.
Serves the exact JSON shapes the frontend expects (derived from the real
backend's api/personas.py, api/models.py, extra_personas.py, demo_questions.json)
plus a canned SSE panel for /api/chat/start so the multi-persona discussion UI
can be screenshotted without any real LLM/HANA access. Not used in production.
"""
import json, time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
PROVIDERS = [
{"id": "openai", "name": "OpenAI", "models": [
{"id": "gpt-5.4", "name": "GPT-5.4", "params": "Undisclosed"},
{"id": "gpt-4.1", "name": "GPT-4.1", "params": "Undisclosed"},
{"id": "gpt-4o", "name": "GPT-4o", "params": "~200B"},
{"id": "gpt-4o-mini", "name": "GPT-4o Mini", "params": "~8B"},
{"id": "o4-mini", "name": "o4-Mini", "params": "Undisclosed"},
]},
{"id": "gemini", "name": "Google Gemini", "models": [
{"id": "gemini-2.5-flash", "name": "Gemini 2.5 Flash", "params": "Undisclosed"},
{"id": "gemini-2.5-pro", "name": "Gemini 2.5 Pro", "params": "Undisclosed"},
]},
{"id": "mistral", "name": "Mistral", "models": [
{"id": "devstral-2512", "name": "Devstral2", "params": "123B"},
{"id": "mistral-small-2603", "name": "Mistral Small 4", "params": "119B"},
]},
{"id": "meta", "name": "Meta Llama", "models": [
{"id": "meta-llama/Llama-3.3-70B-Instruct-Turbo", "name": "Llama 3.3 70B Turbo", "params": "70B"},
]},
{"id": "deepseek", "name": "DeepSeek", "models": [
{"id": "accounts/fireworks/models/deepseek-v3p1", "name": "DeepSeek V3.1", "params": "671B"},
]},
]
NEON_MODELS = [
{"model_id": "BrainForge/Security@2026.03.18", "name": "BrainForge/Security",
"personas": [
{"persona_name": "Athena", "enabled": True, "system_prompt": "You are Athena, a strategic advisor."},
{"persona_name": "Vanilla", "enabled": True, "system_prompt": "Plain assistant."},
]},
]
NEON_PERSONAS = [
{"participant_id": "neon:BrainForge/Security@2026.03.18:Athena", "kind": "neon",
"name": "Athena (Strategic Advisor)", "model_display": "Security",
"default_model_id": "neon:BrainForge/Security@2026.03.18:Athena",
"description": "Strategic advisor persona", "role_prompt": "You are Athena."},
]
EXTRA = [
{"participant_id": "extra_pragmatic_generalist", "name": "Pragmatic Finance Expert",
"default_model_id": "gpt-5.4", "model_display": "gpt-5.4", "kind": "extra", "role_prompt": "..."},
{"participant_id": "extra_skeptical_critic", "name": "Skeptical Philosopher",
"default_model_id": "gemini-2.5-flash", "model_display": "gemini-2.5-flash", "kind": "extra", "role_prompt": "..."},
{"participant_id": "extra_empathetic_humanist", "name": "Empathetic Historian",
"default_model_id": "devstral-2512", "model_display": "devstral-2512", "kind": "extra", "role_prompt": "..."},
{"participant_id": "extra_data_driven_analyst", "name": "Data-Driven Geologist",
"default_model_id": "meta-llama/Llama-3.3-70B-Instruct-Turbo",
"model_display": "Llama-3.3-70B-Instruct-Turbo", "kind": "extra", "role_prompt": "..."},
{"participant_id": "extra_elena_financial_strategist", "name": "Elena — Financial Strategist",
"default_model_id": "gpt-4.1", "model_display": "gpt-4.1", "kind": "extra", "role_prompt": "..."},
{"participant_id": "extra_marcus_technology_strategist", "name": "Marcus — Technology Strategist",
"default_model_id": "mistral-small-2603",
"model_display": "mistral-small-2603", "kind": "extra", "role_prompt": "..."},
{"participant_id": "extra_amira_security_advisor", "name": "Dr. Amira — Security & Privacy Advisor",
"default_model_id": "neon:BrainForge/Security@2026.05.13:CybersecurityExpert",
"model_display": "CybersecurityExpert", "kind": "extra", "role_prompt": "..."},
]
DEMO_QUESTIONS = json.load(open("backend/app/data/demo_questions.json"))["questions"]
FORMATS = {
"structures": [
{"id": "collaborative", "name": "Collaborative Discussion", "description": "Structured group reasoning toward consensus."},
{"id": "roberts_rules", "name": "Robert's Rules", "description": "Formal motion/second/vote procedure."},
],
"decisions": [
{"id": "consensus", "name": "Consensus", "description": "Seek agreement; majority report with dissent."},
{"id": "majority", "name": "Majority Vote", "description": "Simple majority."},
{"id": "ranked_choice", "name": "Ranked Choice", "description": "Ranked-choice tally."},
],
"default_structure_id": "collaborative",
"default_decision_id": "consensus",
}
def sse(event, data):
return f"event: {event}\ndata: {json.dumps(data)}\n\n".encode()
class H(BaseHTTPRequestHandler):
def log_message(self, *a): pass
def _json(self, obj, code=200):
body = json.dumps(obj).encode()
self.send_response(code)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(body)
def do_OPTIONS(self):
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.end_headers()
def do_GET(self):
p = self.path.split("?")[0]
if p == "/api/models": return self._json({"neon_models": NEON_MODELS, "providers": PROVIDERS})
if p == "/api/personas": return self._json({"neon": NEON_PERSONAS, "extra": EXTRA})
if p == "/api/demo-questions": return self._json({"questions": DEMO_QUESTIONS})
if p == "/api/chat/orchestrator": return self._json({"model_id": "gpt-4o-mini"})
if p == "/api/chat/speed-priority": return self._json({"enabled": False})
if p == "/api/chat/conversation-formats": return self._json(FORMATS)
if p == "/api/auth/status": return self._json({"logged_in": False, "is_org_member": False, "remaining_conversations": 30})
if p == "/api/rate-limit/status": return self._json({"remaining": 30, "daily_limit": 30})
return self._json({}, 404)
def do_PUT(self):
self._read()
if self.path.startswith("/api/chat/orchestrator"): return self._json({"model_id": "gpt-4o-mini"})
if self.path.startswith("/api/chat/speed-priority"): return self._json({"enabled": False})
return self._json({})
def do_PATCH(self):
self._read(); return self._json({})
def _read(self):
n = int(self.headers.get("Content-Length", 0) or 0)
return self.rfile.read(n) if n else b""
def do_POST(self):
raw = self._read()
try: body = json.loads(raw or b"{}")
except Exception: body = {}
if self.path.startswith("/api/chat/suggest-model"):
return self._json({"recommended_model_id": "gemini-2.5-flash",
"rationale": "A philosophy-leaning critic benefits from a model with strong reasoning and concise argumentation; Gemini 2.5 Flash also diversifies the panel away from the GPT family already present."})
if self.path.startswith("/api/chat/generate-role-freeform") or self.path.startswith("/api/chat/generate-role"):
return self._json({"role_prompt": "You are " + (body.get("name") or "an expert") + ". (mock-generated role prompt for UI rendering)"})
if self.path.startswith("/api/chat/auto-select-participants"):
cands = body.get("candidates", [])
return self._json({"selected": [c["participant_id"] for c in cands[:body.get("count",3)]],
"rationale": "Picked the most topically relevant participants (mock)."})
if self.path.startswith("/api/chat/start"):
return self._sse_panel(body)
return self._json({})
def _sse_panel(self, body):
self.send_response(200)
self.send_header("Content-Type", "text/event-stream")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Cache-Control", "no-cache")
self.end_headers()
parts = body.get("participants", [])
if len(parts) < 2:
parts = [{"participant_id": "extra_pragmatic_generalist", "name": "Pragmatic Finance Expert"},
{"participant_id": "extra_skeptical_critic", "name": "Skeptical Philosopher"},
{"participant_id": "extra_empathetic_humanist", "name": "Empathetic Historian"}]
q = body.get("question", "the question")
sid = "mock-" + str(int(time.time()))
roster = [{"participant_id": p["participant_id"], "name": p["name"],
"model_display": p.get("name")} for p in parts]
self.wfile.write(sse("session", {"session_id": sid, "participants": roster})); self.wfile.flush()
self.wfile.write(sse("status", {"message": "Phase 1: Initial Opinions"})); self.wfile.flush()
op = {
parts[0]["name"]: f"On '{q[:50]}...', my first take is to weigh cost against benefit. The numbers have to clear a return-on-investment bar before anything else.",
parts[1]["name"]: "I'd challenge the framing. Before we optimize, what assumption are we all making that nobody has examined? Let's pressure-test the premise.",
}
if len(parts) > 2:
op[parts[2]["name"]] = "I want to center the human stakes. Whoever is on the receiving end of this decision matters as much as the spreadsheet."
for i, p in enumerate(parts):
mid = f"m{i}"
txt = op.get(p["name"], "Here is my initial opinion, grounded in my area of expertise.")
self.wfile.write(sse("message", {"message_id": mid, "role": "participant",
"speaker_id": p["participant_id"], "speaker_name": p["name"],
"text": txt, "phase": "initial_opinions", "elapsed_seconds": 2.1 + i,
"timestamp": time.time()})); self.wfile.flush()
self.wfile.write(sse("orchestrator", {"message_id": "o1", "role": "orchestrator",
"kind": "status", "text": "All participants have given initial opinions. Moving to the critique phase, where each will respond to the others.",
"timestamp": time.time()})); self.wfile.flush()
self.wfile.write(sse("message", {"message_id": "m10", "role": "participant",
"speaker_id": parts[1]["participant_id"], "speaker_name": parts[1]["name"],
"text": "Responding directly to the finance framing: ROI is necessary but not sufficient. A positive ROI on paper can still be the wrong call if it erodes trust.",
"addressed_to": parts[0]["participant_id"], "replying_to": [parts[0]["participant_id"]],
"phase": "critique", "elapsed_seconds": 3.4, "timestamp": time.time()})); self.wfile.flush()
self.wfile.write(sse("orchestrator", {"message_id": "o2", "role": "orchestrator",
"kind": "majority_report",
"text": "**Majority Report.** The panel converges on a staged approach: validate ROI on a small pilot first, while explicitly protecting the human/trust factors the Historian raised. One participant dissents, preferring a faster full commitment.",
"timestamp": time.time()})); self.wfile.flush()
self.wfile.write(sse("system", {"text": "End of Chat"})); self.wfile.flush()
self.wfile.write(sse("done", {})); self.wfile.flush()
if __name__ == "__main__":
print("Mock backend on :8000")
ThreadingHTTPServer(("127.0.0.1", 8000), H).serve_forever()
|