Kirk Brown commited on
Commit
ee9cfab
·
1 Parent(s): 4bfbc4c

🎛️ Reengineer as CAPT Fleet Controller

Browse files

- Complete rewrite from Memory Palace Proxy to Fleet Controller
- Monitor 5 Cloudflare brains + 16 public HF spaces
- Auto-route queries to best brain based on keywords
- Council mode: query all 5 brains and synthesize consensus
- PULSE proxy with tiered model selector
- ECHO memory proxy across all brains
- API tester for any brain endpoint
- MCP hub with connection guides

Files changed (4) hide show
  1. README.md +0 -10
  2. app.py +457 -0
  3. fleet_controller.py +322 -0
  4. requirements.txt +3 -0
README.md DELETED
@@ -1,10 +0,0 @@
1
- ---
2
- title: CAPT Memory Palace Proxy
3
- emoji: 🏆
4
- colorFrom: blue
5
- colorTo: gray
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
app.py ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 🎛️ CAPT Fleet Controller
3
+ Private HF Space — Central orchestrator for all CAPT brains and public HF spaces.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import os
10
+ import sys
11
+ import time
12
+
13
+ import gradio as gr
14
+ import requests
15
+
16
+ from fleet_controller import (
17
+ BRAINS, HF_SPACES, ROUTING_KEYWORDS, MODEL_TIERS, TIER_LABELS,
18
+ route_query, call_brain, call_all_brains,
19
+ check_brain_health, check_all_brains,
20
+ check_space_health, check_all_spaces,
21
+ get_all_models,
22
+ )
23
+
24
+ OPENROUTER_KEY = os.getenv("OPENROUTER_API_KEY", "")
25
+
26
+ # ─── Helper: OpenRouter direct call ───
27
+ def call_openrouter(prompt: str, model: str = "deepseek/deepseek-chat-v3-0324", system: str = "", temperature: float = 0.7, max_tokens: int = 2048) -> dict:
28
+ if not OPENROUTER_KEY:
29
+ return {"error": "OPENROUTER_API_KEY not configured"}
30
+ try:
31
+ r = requests.post(
32
+ "https://openrouter.ai/api/v1/chat/completions",
33
+ headers={
34
+ "Authorization": f"Bearer {OPENROUTER_KEY}",
35
+ "Content-Type": "application/json",
36
+ "HTTP-Referer": "https://capt.dev",
37
+ "X-Title": "CAPT-Fleet-Controller",
38
+ },
39
+ json={
40
+ "model": model,
41
+ "messages": [
42
+ {"role": "system", "content": system or "You are CAPT Fleet Controller."},
43
+ {"role": "user", "content": prompt},
44
+ ],
45
+ "temperature": temperature,
46
+ "max_tokens": max_tokens,
47
+ },
48
+ timeout=60,
49
+ )
50
+ data = r.json()
51
+ if r.status_code != 200:
52
+ return {"error": f"HTTP {r.status_code}: {data.get('error', {}).get('message', r.text[:200])}"}
53
+ return {
54
+ "content": data["choices"][0]["message"]["content"],
55
+ "model": data.get("model", model),
56
+ "input_tokens": data.get("usage", {}).get("prompt_tokens", 0),
57
+ "output_tokens": data.get("usage", {}).get("completion_tokens", 0),
58
+ }
59
+ except Exception as e:
60
+ return {"error": str(e)}
61
+
62
+ # ═══════════════════════════════════════════════════════════════════
63
+ # TAB BUILDERS
64
+ # ═══════════════════════════════════════════════════════════════════
65
+
66
+ def build_fleet_dashboard():
67
+ with gr.Tab("🎛️ Fleet Dashboard"):
68
+ gr.Markdown("## CAPT Fleet Controller\nMonitor all brains and spaces in real-time.")
69
+
70
+ with gr.Row():
71
+ refresh_btn = gr.Button("🔄 Refresh All", variant="primary")
72
+ auto_refresh = gr.Checkbox(label="Auto-refresh (30s)", value=False)
73
+
74
+ gr.Markdown("### 🧠 Brain Health")
75
+ brain_table = gr.Dataframe(
76
+ headers=["Icon", "Brain", "Status", "Latency", "Worker"],
77
+ datatype=["str", "str", "str", "str", "str"],
78
+ interactive=False,
79
+ wrap=True,
80
+ )
81
+
82
+ gr.Markdown("### 🚀 HF Space Health")
83
+ space_table = gr.Dataframe(
84
+ headers=["Space", "Status", "Online", "HTTP", "Latency", "Issue"],
85
+ datatype=["str", "str", "str", "str", "str", "str"],
86
+ interactive=False,
87
+ wrap=True,
88
+ )
89
+
90
+ def refresh_dashboard():
91
+ brains = check_all_brains()
92
+ spaces = check_all_spaces()
93
+
94
+ brain_rows = []
95
+ for b in brains:
96
+ icon = "🟢" if b.get("online") else "🔴"
97
+ brain_rows.append([
98
+ BRAINS.get(b["brain_id"], {}).get("icon", "🧠"),
99
+ b.get("name", b["brain_id"]),
100
+ f"{icon} {b.get('status', 'unknown')}",
101
+ f"{b.get('latency_ms', 0)}ms" if b.get("online") else "—",
102
+ BRAINS.get(b["brain_id"], {}).get("worker", ""),
103
+ ])
104
+
105
+ space_rows = []
106
+ for s in spaces:
107
+ icon = "🟢" if s.get("online") else "🔴"
108
+ space_rows.append([
109
+ s.get("name", s["id"]),
110
+ f"{icon} {s.get('status', 'unknown')}",
111
+ "✅" if s.get("online") else "❌",
112
+ str(s.get("http_code", "—")),
113
+ f"{s.get('latency_ms', 0)}ms" if s.get("online") else "—",
114
+ s.get("error", HF_SPACES.get(s["id"], {}).get("issue", "")),
115
+ ])
116
+
117
+ return brain_rows, space_rows
118
+
119
+ refresh_btn.click(refresh_dashboard, outputs=[brain_table, space_table])
120
+
121
+ # Auto-refresh timer
122
+ def auto_refresh_fn(active):
123
+ if active:
124
+ return refresh_dashboard()
125
+ return [], []
126
+
127
+ # Initial load
128
+ demo.load(refresh_dashboard, outputs=[brain_table, space_table])
129
+
130
+
131
+ def build_brain_router():
132
+ with gr.Tab("🧠 Brain Router"):
133
+ gr.Markdown("## Query Router\nSend queries to individual brains or let the fleet auto-route.")
134
+
135
+ with gr.Row():
136
+ with gr.Column(scale=2):
137
+ router_query = gr.Textbox(label="Query", placeholder="Ask anything...", lines=3)
138
+ router_context = gr.Textbox(label="Context (optional)", placeholder="Additional context", lines=2)
139
+
140
+ with gr.Row():
141
+ router_mode = gr.Radio(
142
+ choices=[("Auto-Route", "auto"), ("Manual Select", "manual"), ("All Brains (Council)", "council")],
143
+ value="auto",
144
+ label="Routing Mode"
145
+ )
146
+ router_brain = gr.Dropdown(
147
+ choices=[(f"{b['icon']} {b['name']}", bid) for bid, b in BRAINS.items()],
148
+ value="capt-core-01",
149
+ label="Target Brain",
150
+ visible=False,
151
+ )
152
+
153
+ with gr.Row():
154
+ router_tier = gr.Dropdown(
155
+ choices=[("All Tiers", "all")] + [(v, k) for k, v in TIER_LABELS.items()],
156
+ value="all",
157
+ label="Model Tier"
158
+ )
159
+ router_model = gr.Dropdown(choices=get_all_models(), value="deepseek/deepseek-chat-v3-0324", label="Model")
160
+
161
+ router_btn = gr.Button("⚡ Execute", variant="primary")
162
+
163
+ with gr.Column(scale=3):
164
+ router_result = gr.Textbox(label="Response", lines=10, interactive=False)
165
+ with gr.Row():
166
+ router_brain_used = gr.Textbox(label="Brain Used", interactive=False)
167
+ router_confidence = gr.Textbox(label="Confidence", interactive=False)
168
+ router_latency = gr.Textbox(label="Latency", interactive=False)
169
+
170
+ def on_mode_change(mode):
171
+ return gr.Dropdown(visible=(mode == "manual"))
172
+
173
+ router_mode.change(on_mode_change, inputs=router_mode, outputs=router_brain)
174
+
175
+ def on_tier_change(tier):
176
+ if tier == "all":
177
+ return gr.Dropdown(choices=get_all_models())
178
+ models = []
179
+ for t, items in MODEL_TIERS.items():
180
+ if t == tier:
181
+ for mid, name in items:
182
+ models.append((f"{TIER_LABELS[t]}: {name}", mid))
183
+ return gr.Dropdown(choices=models)
184
+
185
+ router_tier.change(on_tier_change, inputs=router_tier, outputs=router_model)
186
+
187
+ def do_route(query, context, mode, brain_id, model):
188
+ if not query.strip():
189
+ return "Enter a query.", "", "", ""
190
+
191
+ start = time.time()
192
+ payload = {"query": query, "context": context, "model": model, "modality": "text"}
193
+
194
+ if mode == "council":
195
+ results = call_all_brains("/cogitate", payload)
196
+ # Aggregate responses
197
+ responses = []
198
+ for bid, result in results.items():
199
+ name = BRAINS.get(bid, {}).get("name", bid)
200
+ resp = result.get("response", f"Error: {result.get('error', 'Unknown')}")
201
+ conf = result.get("confidence", 0)
202
+ responses.append(f"### {name} (confidence: {conf:.2f})\n{resp[:500]}")
203
+
204
+ summary_prompt = f"Synthesize these {len(responses)} expert opinions into a single coherent response:\n\n" + "\n\n---\n\n".join(responses)
205
+ summary = call_openrouter(summary_prompt, model, "You are a synthesis engine. Combine multiple expert opinions into one clear answer.", 0.3, 2048)
206
+ full_response = f"## 🏛️ Council Consensus\n\n{summary.get('content', 'Synthesis failed')}\n\n---\n\n## Individual Responses\n\n" + "\n\n".join(responses)
207
+ return full_response, "LLM Council (All 5)", f"{summary.get('confidence', 0):.2f}" if isinstance(summary, dict) else "N/A", f"{int((time.time()-start)*1000)}ms"
208
+
209
+ elif mode == "auto":
210
+ brain_id = route_query(query)
211
+
212
+ result = call_brain(brain_id, "/cogitate", payload)
213
+ if "error" in result:
214
+ return f"Error: {result['error']}", brain_id, "", ""
215
+
216
+ brain_name = BRAINS.get(brain_id, {}).get("name", brain_id)
217
+ return (
218
+ result.get("response", ""),
219
+ f"{brain_name} ({brain_id})",
220
+ f"{result.get('confidence', 0)*100:.1f}%",
221
+ f"{result.get('pipelineTrace', {}).get('totalLatencyMs', 0)}ms",
222
+ )
223
+
224
+ router_btn.click(
225
+ do_route,
226
+ inputs=[router_query, router_context, router_mode, router_brain, router_model],
227
+ outputs=[router_result, router_brain_used, router_confidence, router_latency],
228
+ )
229
+
230
+
231
+ def build_pulse_proxy():
232
+ with gr.Tab("⚡ PULSE Proxy"):
233
+ gr.Markdown("## Direct LLM Access\nBypass cognition pipeline and query any model directly.")
234
+
235
+ with gr.Row():
236
+ with gr.Column(scale=2):
237
+ pulse_prompt = gr.Textbox(label="Prompt", placeholder="Enter prompt...", lines=4)
238
+ pulse_system = gr.Textbox(label="System Prompt", placeholder="You are CAPT...", lines=2)
239
+ pulse_tier = gr.Dropdown(choices=[("All Tiers", "all")] + [(v, k) for k, v in TIER_LABELS.items()], value="all", label="Tier")
240
+ pulse_model = gr.Dropdown(choices=get_all_models(), value="deepseek/deepseek-chat-v3-0324", label="Model")
241
+ with gr.Row():
242
+ pulse_temp = gr.Slider(0.0, 2.0, value=0.7, step=0.1, label="Temperature")
243
+ pulse_max = gr.Slider(64, 4096, value=1024, step=64, label="Max Tokens")
244
+ pulse_btn = gr.Button("🔥 Generate", variant="primary")
245
+ with gr.Column(scale=3):
246
+ pulse_out = gr.Textbox(label="Output", lines=12, interactive=False)
247
+ pulse_meta = gr.JSON(label="Metadata")
248
+
249
+ pulse_tier.change(on_tier_change, inputs=pulse_tier, outputs=pulse_model)
250
+
251
+ def do_pulse(prompt, system, model, temp, maxtok):
252
+ if not prompt.strip():
253
+ return "", {}
254
+ result = call_openrouter(prompt, model, system, temp, maxtok)
255
+ if "error" in result:
256
+ return f"Error: {result['error']}", {}
257
+ meta = {
258
+ "model": result.get("model"),
259
+ "input_tokens": result.get("input_tokens"),
260
+ "output_tokens": result.get("output_tokens"),
261
+ "total_tokens": result.get("input_tokens", 0) + result.get("output_tokens", 0),
262
+ }
263
+ return result.get("content", ""), meta
264
+
265
+ pulse_btn.click(do_pulse, inputs=[pulse_prompt, pulse_system, pulse_model, pulse_temp, pulse_max], outputs=[pulse_out, pulse_meta])
266
+
267
+
268
+ def build_echo_proxy():
269
+ with gr.Tab("💾 ECHO Proxy"):
270
+ gr.Markdown("## Memory Proxy\nStore and recall traces across all brains.")
271
+
272
+ with gr.Row():
273
+ with gr.Column():
274
+ gr.Markdown("### Store")
275
+ echo_content = gr.Textbox(label="Content", lines=3)
276
+ echo_brain = gr.Dropdown(choices=[(f"{b['icon']} {b['name']}", bid) for bid, b in BRAINS.items()], value="capt-core-01", label="Brain")
277
+ echo_tags = gr.Textbox(label="Tags (comma-separated)")
278
+ echo_salience = gr.Slider(0.0, 1.0, value=0.5, step=0.05, label="Salience")
279
+ echo_store_btn = gr.Button("💾 Store", variant="primary")
280
+ echo_store_result = gr.Textbox(label="Result", interactive=False)
281
+ with gr.Column():
282
+ gr.Markdown("### Recall")
283
+ echo_query = gr.Textbox(label="Query")
284
+ echo_recall_brain = gr.Dropdown(choices=[(f"{b['icon']} {b['name']}", bid) for bid, b in BRAINS.items()], value="capt-core-01", label="Brain")
285
+ echo_topk = gr.Slider(1, 20, value=5, step=1, label="Top K")
286
+ echo_recall_btn = gr.Button("🔍 Recall")
287
+ echo_recall_result = gr.JSON(label="Traces")
288
+
289
+ def do_store(content, brain, tags, salience):
290
+ if not content.strip():
291
+ return "Enter content."
292
+ tag_list = [t.strip() for t in tags.split(",") if t.strip()]
293
+ result = call_brain(brain, "/echo/store", {"content": content, "salience": salience, "tags": tag_list})
294
+ if "error" in result:
295
+ return f"Error: {result['error']}"
296
+ return f"✅ Stored: {result.get('data', {}).get('traceId', 'N/A')}"
297
+
298
+ def do_recall(query, brain, topk):
299
+ if not query.strip():
300
+ return []
301
+ result = call_brain(brain, "/echo/recall", {"query": query, "topK": int(topk)})
302
+ if "error" in result:
303
+ return [{"error": result["error"]}]
304
+ traces = result.get("data", {}).get("traces", [])
305
+ return [{"content": t.get("content", "")[:200], "salience": t.get("salience"), "tags": t.get("tags")} for t in traces]
306
+
307
+ echo_store_btn.click(do_store, inputs=[echo_content, echo_brain, echo_tags, echo_salience], outputs=echo_store_result)
308
+ echo_recall_btn.click(do_recall, inputs=[echo_query, echo_recall_brain, echo_topk], outputs=echo_recall_result)
309
+
310
+
311
+ def build_api_tester():
312
+ with gr.Tab("🔌 API Tester"):
313
+ gr.Markdown("## Test Any Brain API\nDirect API proxy to all brain endpoints.")
314
+
315
+ with gr.Row():
316
+ with gr.Column(scale=2):
317
+ api_brain = gr.Dropdown(choices=[(f"{b['icon']} {b['name']}", bid) for bid, b in BRAINS.items()], value="capt-core-01", label="Brain")
318
+ api_endpoint = gr.Dropdown(
319
+ choices=["/health", "/cogitate", "/pulse", "/echo/store", "/echo/recall", "/immu/scan", "/constitution/enforce", "/status"],
320
+ value="/health",
321
+ label="Endpoint"
322
+ )
323
+ api_method = gr.Radio(choices=["GET", "POST"], value="GET", label="Method")
324
+ api_payload = gr.Code(label="Payload (JSON)", language="json", value='{}')
325
+ api_btn = gr.Button("📡 Send", variant="primary")
326
+ with gr.Column(scale=3):
327
+ api_response = gr.JSON(label="Response")
328
+ api_raw = gr.Textbox(label="Raw Response", lines=6, interactive=False)
329
+
330
+ def do_api(brain, endpoint, method, payload):
331
+ try:
332
+ body = json.loads(payload) if payload.strip() else {}
333
+ except json.JSONDecodeError as e:
334
+ return {"error": f"Invalid JSON: {e}"}, ""
335
+
336
+ worker = BRAINS.get(brain, {}).get("worker", "")
337
+ url = f"{worker}{endpoint}"
338
+ try:
339
+ if method == "GET":
340
+ r = requests.get(url, timeout=15)
341
+ else:
342
+ r = requests.post(url, json=body, timeout=60, headers={"Content-Type": "application/json"})
343
+ try:
344
+ data = r.json()
345
+ except:
346
+ data = {"status_code": r.status_code, "text": r.text[:500]}
347
+ return data, r.text[:1000]
348
+ except Exception as e:
349
+ return {"error": str(e)}, ""
350
+
351
+ api_btn.click(do_api, inputs=[api_brain, api_endpoint, api_method, api_payload], outputs=[api_response, api_raw])
352
+
353
+
354
+ def build_mcp_hub():
355
+ with gr.Tab("🔗 MCP Hub"):
356
+ gr.Markdown("## Model Context Protocol Hub\nConnect MCP clients to the fleet.")
357
+
358
+ gr.Markdown("""
359
+ ### MCP Endpoints
360
+
361
+ Each brain exposes a full MCP server over SSE:
362
+
363
+ | Brain | MCP SSE | MCP RPC |
364
+ |-------|---------|---------|
365
+ """)
366
+ for bid, b in BRAINS.items():
367
+ gr.Markdown(f"| {b['icon']} {b['name']} | `{b['worker']}/mcp` | `{b['worker']}/mcp/messages` |")
368
+
369
+ gr.Markdown("""
370
+ ### MCP Tool Registry (22 tools per brain)
371
+
372
+ - `capt_cogitate` — Full 46-module pipeline
373
+ - `pulse_generate` — LLM generation
374
+ - `sens_perceive` — Tokenization & entities
375
+ - `echo_store` / `echo_recall` — Memory
376
+ - `constitution_enforce` — Seven Laws
377
+ - `immu_scan` — Threat detection
378
+ - `qipc_consensus` — Module consensus
379
+ - `brain_status` — Health check
380
+ - `echo_prune` — Memory maintenance
381
+ - ... and 13 more
382
+
383
+ ### Connecting Claude Desktop
384
+
385
+ ```json
386
+ {
387
+ "mcpServers": {
388
+ "capt-core": {
389
+ "type": "sse",
390
+ "url": "https://capt-brain-01.knowurknottty.workers.dev/mcp"
391
+ }
392
+ }
393
+ }
394
+ ```
395
+ """)
396
+
397
+ mcp_test_btn = gr.Button("🧪 Test MCP Connection to CAPT Core")
398
+ mcp_test_result = gr.JSON(label="MCP Test Result")
399
+
400
+ def test_mcp():
401
+ try:
402
+ r = requests.post(
403
+ "https://capt-brain-01.knowurknottty.workers.dev/mcp/messages",
404
+ json={"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
405
+ timeout=15,
406
+ headers={"Content-Type": "application/json"},
407
+ )
408
+ return r.json()
409
+ except Exception as e:
410
+ return {"error": str(e)}
411
+
412
+ mcp_test_btn.click(test_mcp, outputs=mcp_test_result)
413
+
414
+
415
+ # ═══════════════════════════════════════════════════════════════════
416
+ # MAIN APP
417
+ # ═══════════════════════════════════════════════════════════════════
418
+
419
+ with gr.Blocks(
420
+ title="CAPT Fleet Controller",
421
+ theme=gr.themes.Base(
422
+ primary_hue="cyan",
423
+ secondary_hue="violet",
424
+ neutral_hue="zinc",
425
+ font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
426
+ ).set(
427
+ body_background_fill="*neutral_950",
428
+ body_text_color="*neutral_100",
429
+ block_background_fill="*neutral_900",
430
+ block_border_color="*neutral_800",
431
+ input_background_fill="*neutral_950",
432
+ button_primary_background_fill="*primary_600",
433
+ button_primary_text_color="white",
434
+ ),
435
+ ) as demo:
436
+ gr.Markdown(
437
+ "# 🎛️ CAPT Fleet Controller\n"
438
+ "<p style='opacity:0.7;font-size:0.9rem'>"
439
+ "Central orchestrator for <b>5 Cloudflare brains</b> + <b>16 public HF spaces</b> | "
440
+ f"API Key: {'✅' if OPENROUTER_KEY else '❌ Not Set'}"
441
+ "</p>"
442
+ )
443
+
444
+ build_fleet_dashboard()
445
+ build_brain_router()
446
+ build_pulse_proxy()
447
+ build_echo_proxy()
448
+ build_api_tester()
449
+ build_mcp_hub()
450
+
451
+ if __name__ == "__main__":
452
+ demo.launch(
453
+ server_name="0.0.0.0",
454
+ server_port=int(os.getenv("PORT", "7860")),
455
+ share=False,
456
+ show_error=True,
457
+ )
fleet_controller.py ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 🎛️ CAPT Fleet Controller
3
+ Central orchestrator for all CAPT brains and HF spaces.
4
+ """
5
+
6
+ import json
7
+ import os
8
+ import time
9
+ from dataclasses import dataclass, field
10
+ from typing import Any
11
+
12
+ import requests
13
+
14
+ # ─── Configuration ───
15
+ OPENROUTER_KEY = os.getenv("OPENROUTER_API_KEY", "")
16
+ HF_TOKEN = os.getenv("HF_TOKEN", "")
17
+
18
+ # ─── Brain Registry ───
19
+ BRAINS = {
20
+ "capt-core-01": {
21
+ "id": "capt-core-01",
22
+ "name": "CAPT Core Alpha",
23
+ "icon": "🧠",
24
+ "type": "capt",
25
+ "worker": "https://capt-brain-01.knowurknottty.workers.dev",
26
+ "pages": "https://edea3345.capt-brain-01-capt-ui.pages.dev",
27
+ "description": "Primary 46-module cognitive architecture with constitutional governance",
28
+ "specialties": ["general", "analysis", "reasoning", "constitution"],
29
+ },
30
+ "biocapt-core-01": {
31
+ "id": "biocapt-core-01",
32
+ "name": "bioCAPT Genesis",
33
+ "icon": "🧬",
34
+ "type": "biocapt",
35
+ "worker": "https://capt-brain-02-biocapt.knowurknottty.workers.dev",
36
+ "pages": "https://64c3188c.capt-brain-02-biocapt-ui.pages.dev",
37
+ "description": "Bio-inspired variant with enhanced episodic memory",
38
+ "specialties": ["memory", "learning", "biomimicry", "adaptation"],
39
+ },
40
+ "frankencapt-core-01": {
41
+ "id": "frankencapt-core-01",
42
+ "name": "FrankenCAPT Chimera",
43
+ "icon": "⚗️",
44
+ "type": "frankencapt",
45
+ "worker": "https://capt-brain-03-frankencapt.knowurknottty.workers.dev",
46
+ "pages": "https://2867a361.capt-brain-03-frankencapt-ui.pages.dev",
47
+ "description": "Modular assembly with variant mixing and skill fusion",
48
+ "specialties": ["modular", "fusion", "skills", "variants"],
49
+ },
50
+ "synthesis-core-01": {
51
+ "id": "synthesis-core-01",
52
+ "name": "Synthesis Nexus",
53
+ "icon": "🔮",
54
+ "type": "synthesis",
55
+ "worker": "https://capt-brain-04-synthesis.knowurknottty.workers.dev",
56
+ "pages": "https://99766389.capt-brain-04-synthesis-ui.pages.dev",
57
+ "description": "Cross-brain consensus and meta-cognitive orchestration",
58
+ "specialties": ["consensus", "meta-cognition", "synthesis", "cross-brain"],
59
+ },
60
+ "council-core-01": {
61
+ "id": "council-core-01",
62
+ "name": "LLM Council",
63
+ "icon": "🏛️",
64
+ "type": "council",
65
+ "worker": "https://capt-brain-05-council.knowurknottty.workers.dev",
66
+ "pages": "https://551b6882.capt-brain-05-council-ui.pages.dev",
67
+ "description": "Multi-model deliberation and voting architecture",
68
+ "specialties": ["voting", "deliberation", "multi-model", "consensus"],
69
+ },
70
+ }
71
+
72
+ # ─── HF Space Registry ───
73
+ HF_SPACES = {
74
+ "knowurknot-capt-wiki": {
75
+ "name": "CAPT Wiki",
76
+ "url": "https://knowurknot-capt-wiki.hf.space",
77
+ "type": "gradio",
78
+ "status": "degraded",
79
+ "issue": "Hardcoded local path, empty corpus",
80
+ },
81
+ "knowurknot-inversion-labs-biocapt-demo": {
82
+ "name": "Inversion Labs bioCAPT",
83
+ "url": "https://knowurknot-inversion-labs-biocapt-demo.hf.space",
84
+ "type": "gradio",
85
+ "status": "degraded",
86
+ "issue": "100% backend failure - engines not shipped",
87
+ },
88
+ "knowurknot-biocapt-universal": {
89
+ "name": "bioCAPT Universal",
90
+ "url": "https://knowurknot-biocapt-universal.hf.space",
91
+ "type": "gradio",
92
+ "status": "degraded",
93
+ "issue": "Mock-only backend, no real LLM",
94
+ },
95
+ "knowurknot-git-hologram": {
96
+ "name": "Git Hologram",
97
+ "url": "https://knowurknot-git-hologram.hf.space",
98
+ "type": "gradio",
99
+ "status": "working",
100
+ "issue": "No caching, no bioCAPT integration",
101
+ },
102
+ "knowurknot-capt-cognitive-dashboard": {
103
+ "name": "CAPT Cognitive Dashboard",
104
+ "url": "https://knowurknot-capt-cognitive-dashboard.hf.space",
105
+ "type": "gradio",
106
+ "status": "degraded",
107
+ "issue": "Mock data only, no live backend",
108
+ },
109
+ "knowurknot-inversion-labs-demo": {
110
+ "name": "Inversion Labs Demo",
111
+ "url": "https://knowurknot-inversion-labs-demo.hf.space",
112
+ "type": "gradio",
113
+ "status": "degraded",
114
+ "issue": "Exact duplicate of bioCAPT demo",
115
+ },
116
+ "knowurknot-capt-cogitate-demo": {
117
+ "name": "CAPT Cogitate Demo",
118
+ "url": "https://knowurknot-capt-cogitate-demo.hf.space",
119
+ "type": "gradio",
120
+ "status": "degraded",
121
+ "issue": "CAPT core unreachable, demo mode only",
122
+ },
123
+ "knowurknot-space-frankencapt": {
124
+ "name": "FrankenCAPT",
125
+ "url": "https://knowurknot-space-frankencapt.hf.space",
126
+ "type": "gradio",
127
+ "status": "partial",
128
+ "issue": "Chatroom backend missing",
129
+ },
130
+ "knowurknot-capt-novel-studio": {
131
+ "name": "CAPT Novel Studio",
132
+ "url": "https://knowurknot-capt-novel-studio.hf.space",
133
+ "type": "gradio",
134
+ "status": "sleeping",
135
+ "issue": "Asleep, wakes on request",
136
+ },
137
+ "knowurknot-karpathy-llm-council": {
138
+ "name": "Karpathy LLM Council",
139
+ "url": "https://knowurknot-karpathy-llm-council.hf.space",
140
+ "type": "gradio",
141
+ "status": "sleeping",
142
+ "issue": "Asleep, returns 404 after wake",
143
+ },
144
+ "knowurknot-rl-environments-guide": {
145
+ "name": "RL Environments Guide",
146
+ "url": "https://knowurknot-rl-environments-guide.hf.space",
147
+ "type": "gradio",
148
+ "status": "sleeping",
149
+ "issue": "Asleep, healthy when awake",
150
+ },
151
+ "knowurknot-biocapt-runtime-api": {
152
+ "name": "bioCAPT Runtime API",
153
+ "url": "https://knowurknot-biocapt-runtime-api.hf.space",
154
+ "type": "docker",
155
+ "status": "private",
156
+ "issue": "Private Docker space",
157
+ },
158
+ "knowurknot-biocapt-vessel": {
159
+ "name": "bioCAPT Vessel",
160
+ "url": "",
161
+ "type": "private",
162
+ "status": "private",
163
+ "issue": "No active domain",
164
+ },
165
+ "knowurknot-biocapt-vessel-atlas": {
166
+ "name": "bioCAPT Vessel Atlas",
167
+ "url": "",
168
+ "type": "private",
169
+ "status": "private",
170
+ "issue": "Template README",
171
+ },
172
+ "knowurknot-biocapt-cognitive-world": {
173
+ "name": "bioCAPT Cognitive World",
174
+ "url": "",
175
+ "type": "private",
176
+ "status": "private",
177
+ "issue": "Default template",
178
+ },
179
+ "knowurknot-tools": {
180
+ "name": "CAPT Tools",
181
+ "url": "https://knowurknot-tools.hf.space",
182
+ "type": "gradio",
183
+ "status": "running",
184
+ "issue": "Fixed & reengineered",
185
+ },
186
+ }
187
+
188
+ # ─── Brain Router ───
189
+ ROUTING_KEYWORDS = {
190
+ "capt-core-01": ["general", "analyze", "explain", "what is", "how to", "why", "compare", "constitution", "ethics", "laws"],
191
+ "biocapt-core-01": ["memory", "remember", "recall", "learn", "adapt", "evolve", "bio", "organic", "neural", "synapse"],
192
+ "frankencapt-core-01": ["modular", "combine", "fuse", "mix", "variant", "skill", "build", "assemble", "component"],
193
+ "synthesis-core-01": ["synthesize", "consensus", "vote", "aggregate", "combine brains", "meta", "orchestrate", "coordinate"],
194
+ "council-core-01": ["council", "vote", "deliberate", "debate", "panel", "jury", "multi-model", "ensemble"],
195
+ }
196
+
197
+ def route_query(query: str) -> str:
198
+ """Route a query to the best brain based on keywords."""
199
+ q = query.lower()
200
+ scores = {brain_id: 0 for brain_id in BRAINS}
201
+ for brain_id, keywords in ROUTING_KEYWORDS.items():
202
+ for kw in keywords:
203
+ if kw in q:
204
+ scores[brain_id] += 1
205
+ best = max(scores, key=scores.get)
206
+ return best if scores[best] > 0 else "capt-core-01"
207
+
208
+ # ─── API Callers ───
209
+ def call_brain(brain_id: str, endpoint: str, payload: dict, timeout: int = 60) -> dict:
210
+ """Call a Cloudflare brain Worker."""
211
+ brain = BRAINS.get(brain_id)
212
+ if not brain:
213
+ return {"error": f"Unknown brain: {brain_id}"}
214
+ url = f"{brain['worker']}{endpoint}"
215
+ try:
216
+ r = requests.post(url, json=payload, timeout=timeout, headers={"Content-Type": "application/json"})
217
+ return r.json() if r.status_code == 200 else {"error": f"HTTP {r.status_code}", "detail": r.text[:200]}
218
+ except Exception as e:
219
+ return {"error": str(e)}
220
+
221
+ def call_all_brains(endpoint: str, payload: dict) -> dict:
222
+ """Call all brains in parallel and return consensus."""
223
+ results = {}
224
+ for brain_id in BRAINS:
225
+ results[brain_id] = call_brain(brain_id, endpoint, payload, timeout=30)
226
+ return results
227
+
228
+ def check_brain_health(brain_id: str) -> dict:
229
+ """Check health of a single brain."""
230
+ brain = BRAINS.get(brain_id)
231
+ if not brain:
232
+ return {"status": "unknown", "brain_id": brain_id}
233
+ try:
234
+ r = requests.get(f"{brain['worker']}/health", timeout=10)
235
+ data = r.json() if r.status_code == 200 else {}
236
+ return {
237
+ "status": data.get("status", "unknown"),
238
+ "brain_id": data.get("brain", {}).get("id", brain_id),
239
+ "name": data.get("brain", {}).get("name", brain["name"]),
240
+ "latency_ms": int(r.elapsed.total_seconds() * 1000),
241
+ "online": r.status_code == 200,
242
+ }
243
+ except Exception as e:
244
+ return {"status": "offline", "brain_id": brain_id, "name": brain["name"], "error": str(e), "online": False}
245
+
246
+ def check_all_brains() -> list[dict]:
247
+ """Check health of all brains."""
248
+ return [check_brain_health(bid) for bid in BRAINS]
249
+
250
+ def check_space_health(space_id: str, space_info: dict) -> dict:
251
+ """Check health of an HF Space."""
252
+ url = space_info.get("url", "")
253
+ if not url:
254
+ return {"id": space_id, "name": space_info["name"], "status": "no-url", "online": False}
255
+ try:
256
+ r = requests.get(url, timeout=15, headers={"User-Agent": "CAPT-Fleet-Controller/1.0"})
257
+ online = r.status_code in (200, 307, 308)
258
+ return {
259
+ "id": space_id,
260
+ "name": space_info["name"],
261
+ "status": space_info.get("status", "unknown"),
262
+ "online": online,
263
+ "http_code": r.status_code,
264
+ "latency_ms": int(r.elapsed.total_seconds() * 1000),
265
+ }
266
+ except Exception as e:
267
+ return {
268
+ "id": space_id,
269
+ "name": space_info["name"],
270
+ "status": space_info.get("status", "unknown"),
271
+ "online": False,
272
+ "error": str(e),
273
+ }
274
+
275
+ def check_all_spaces() -> list[dict]:
276
+ """Check health of all HF spaces."""
277
+ return [check_space_health(sid, sinfo) for sid, sinfo in HF_SPACES.items()]
278
+
279
+ # ─── Model Tiers (for PULSE proxy) ───
280
+ MODEL_TIERS = {
281
+ "free": [
282
+ ("deepseek/deepseek-chat-v3-0324", "DeepSeek V3"),
283
+ ("google/gemini-2.0-flash-exp:free", "Gemini 2.0 Flash (Free)"),
284
+ ("meta-llama/llama-3.1-8b-instruct:free", "Llama 3.1 8B"),
285
+ ],
286
+ "super-cheap": [
287
+ ("openai/gpt-4o-mini", "GPT-4o Mini"),
288
+ ("google/gemini-flash-1.5", "Gemini 1.5 Flash"),
289
+ ("anthropic/claude-3-haiku", "Claude 3 Haiku"),
290
+ ("mistralai/mistral-small", "Mistral Small"),
291
+ ],
292
+ "cheap": [
293
+ ("anthropic/claude-3.5-sonnet", "Claude 3.5 Sonnet"),
294
+ ("openai/gpt-4o", "GPT-4o"),
295
+ ("google/gemini-1.5-pro", "Gemini 1.5 Pro"),
296
+ ("meta-llama/llama-3.1-405b-instruct", "Llama 3.1 405B"),
297
+ ],
298
+ "medium": [
299
+ ("anthropic/claude-3-opus", "Claude 3 Opus"),
300
+ ("openai/gpt-4-turbo", "GPT-4 Turbo"),
301
+ ("x-ai/grok-2", "Grok 2"),
302
+ ],
303
+ "too-damn-high": [
304
+ ("openai/o1-preview", "o1 Preview"),
305
+ ("openai/o1-mini", "o1 Mini"),
306
+ ],
307
+ }
308
+
309
+ TIER_LABELS = {
310
+ "free": "🆓 Free",
311
+ "super-cheap": "💚 Super Cheap",
312
+ "cheap": "💛 Cheap",
313
+ "medium": "🧡 Medium",
314
+ "too-damn-high": "💎 Too Damn High",
315
+ }
316
+
317
+ def get_all_models():
318
+ models = []
319
+ for tier, items in MODEL_TIERS.items():
320
+ for model_id, name in items:
321
+ models.append((f"{TIER_LABELS[tier]}: {name}", model_id))
322
+ return models
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio[mcp]==6.2.0
2
+ requests
3
+ pandas