Spaces:
Sleeping
Sleeping
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
- README.md +0 -10
- app.py +457 -0
- fleet_controller.py +322 -0
- 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
|