Spaces:
Sleeping
Sleeping
feat: real router + live 98-model registry
Browse files- app.py +153 -281
- requirements.txt +2 -2
app.py
CHANGED
|
@@ -1,301 +1,173 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
| 3 |
-
|
|
|
|
| 4 |
import json
|
|
|
|
| 5 |
from dataclasses import dataclass, field
|
| 6 |
-
from
|
| 7 |
-
from typing import Any, Dict, List, Optional
|
| 8 |
|
| 9 |
import gradio as gr
|
| 10 |
from huggingface_hub import hf_hub_download
|
| 11 |
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
fallbacks: List[Dict[str, Any]] = field(default_factory=list)
|
| 18 |
-
rejected: List[Dict[str, Any]] = field(default_factory=list)
|
| 19 |
-
reasons: List[str] = field(default_factory=list)
|
| 20 |
-
|
| 21 |
-
def to_dict(self) -> Dict[str, Any]:
|
| 22 |
-
return {"selected": self.selected, "fallbacks": self.fallbacks, "rejected": self.rejected, "reasons": self.reasons}
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
_TASK_TO_TAGS = {
|
| 26 |
-
"code": {"coder", "code"}, "reasoning": {"reasoning"},
|
| 27 |
-
"multimodal": {"multimodal", "image", "audio", "voice", "vision"},
|
| 28 |
-
"embedding": {"embedding", "embed", "retrieval"}, "chat": {"general", "instruct"},
|
| 29 |
-
}
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
def _filter(model: Dict[str, Any], request: Dict[str, Any]) -> Optional[str]:
|
| 33 |
-
req_mod = set(request.get("modalities", []))
|
| 34 |
-
mod = set(model.get("modalities", []))
|
| 35 |
-
if req_mod and not req_mod.issubset(mod):
|
| 36 |
-
return f"modalities missing: needs {sorted(req_mod)}, has {sorted(mod)}"
|
| 37 |
-
if request.get("min_context_tokens") and (model.get("context_tokens") or 0) < request["min_context_tokens"]:
|
| 38 |
-
return f"context_tokens {model.get('context_tokens')} < required {request['min_context_tokens']}"
|
| 39 |
-
if request.get("jurisdiction") and request["jurisdiction"] not in (model.get("residency") or []):
|
| 40 |
-
return f"residency {model.get('residency')} does not include {request['jurisdiction']}"
|
| 41 |
-
if request.get("requires_local") and model.get("runtime") not in ("llama_cpp", "llama_cpp_multimodal", "transformers", "transformers_multimodal", "python_embedding"):
|
| 42 |
-
return f"requires_local but runtime is {model.get('runtime')}"
|
| 43 |
-
if request.get("requires_tools") and not model.get("supports_tools", False):
|
| 44 |
-
return "supports_tools = false"
|
| 45 |
-
if request.get("requires_json") and not model.get("supports_json", False):
|
| 46 |
-
return "supports_json = false"
|
| 47 |
-
if str(request.get("data_sensitivity", "")).lower() in ("restricted", "personal", "health", "iwi", "taonga"):
|
| 48 |
-
if (model.get("sovereignty_tier") or 0) < 2:
|
| 49 |
-
return f"sovereignty_tier {model.get('sovereignty_tier')} < 2 for sensitive data"
|
| 50 |
-
return None
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
def _score(model: Dict[str, Any], request: Dict[str, Any]) -> float:
|
| 54 |
-
q = 6 - int(model.get("quality_rank") or 6)
|
| 55 |
-
c = 6 - int(model.get("cost_rank") or 6)
|
| 56 |
-
s = int(model.get("sovereignty_tier") or 0)
|
| 57 |
-
active = float(model.get("active_params_b") or model.get("total_params_b") or 0)
|
| 58 |
-
base = 2.0 * q + 1.5 * s + 0.5 * c
|
| 59 |
-
wanted = _TASK_TO_TAGS.get(str(request.get("task_type", "")).lower(), set())
|
| 60 |
-
if wanted & set(model.get("tags") or []):
|
| 61 |
-
base += 3.0
|
| 62 |
-
if request.get("requires_local") and model.get("runtime") == "llama_cpp":
|
| 63 |
-
base += 1.0
|
| 64 |
-
if active and active > 50:
|
| 65 |
-
base -= 0.5
|
| 66 |
-
return round(base, 3)
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
def route(registry: Dict[str, Any], request: Dict[str, Any]) -> RouteDecision:
|
| 70 |
-
candidates, rejected = [], []
|
| 71 |
-
for m in registry.get("models", []):
|
| 72 |
-
why = _filter(m, request)
|
| 73 |
-
if why:
|
| 74 |
-
rejected.append({"repo_id": m.get("repo_id"), "reason": why})
|
| 75 |
-
else:
|
| 76 |
-
candidates.append({**m, "_score": _score(m, request)})
|
| 77 |
-
candidates.sort(key=lambda x: x["_score"], reverse=True)
|
| 78 |
-
if not candidates:
|
| 79 |
-
return RouteDecision(None, [], rejected, ["no candidate satisfies the gates"])
|
| 80 |
-
sel = candidates[0]
|
| 81 |
-
max_fb = int(request.get("max_fallbacks", 3))
|
| 82 |
-
return RouteDecision(sel, candidates[1 : 1 + max_fb], rejected, [f"selected {sel['repo_id']} (score {sel['_score']})"])
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
REGISTRY_PATH = Path(__file__).parent / "lumynax_model_registry.json"
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
def load_registry() -> Dict[str, Any]:
|
| 89 |
-
if REGISTRY_PATH.exists():
|
| 90 |
-
return json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
|
| 91 |
-
path = hf_hub_download(repo_id="AbteeXAILab/marama-route", filename="configs/lumynax_model_registry.json", repo_type="model")
|
| 92 |
-
return json.loads(Path(path).read_text(encoding="utf-8"))
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
REGISTRY = load_registry()
|
| 96 |
-
MODEL_COUNT = len(REGISTRY.get("models", []))
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
EXAMPLE_CODE = {
|
| 100 |
-
"prompt": "Refactor this private Python service and explain the diff.",
|
| 101 |
-
"task_type": "code", "modalities": ["text"], "jurisdiction": "NZ",
|
| 102 |
-
"data_sensitivity": "restricted", "min_context_tokens": 4096,
|
| 103 |
-
"requires_local": True, "requires_tools": False, "requires_json": True, "max_fallbacks": 3,
|
| 104 |
-
}
|
| 105 |
-
EXAMPLE_REASON = {
|
| 106 |
-
"prompt": "Plan a multi-step migration of a legacy data pipeline.",
|
| 107 |
-
"task_type": "reasoning", "modalities": ["text"], "jurisdiction": "NZ",
|
| 108 |
-
"data_sensitivity": "restricted", "min_context_tokens": 8192,
|
| 109 |
-
"requires_local": True, "requires_tools": True, "requires_json": True, "max_fallbacks": 3,
|
| 110 |
-
}
|
| 111 |
-
EXAMPLE_MULTIMODAL = {
|
| 112 |
-
"prompt": "Describe this image and draft a short public caption.",
|
| 113 |
-
"task_type": "multimodal", "modalities": ["text", "image"], "jurisdiction": "NZ",
|
| 114 |
-
"data_sensitivity": "public", "min_context_tokens": 4096,
|
| 115 |
-
"requires_local": False, "requires_tools": False, "requires_json": False, "max_fallbacks": 3,
|
| 116 |
-
}
|
| 117 |
-
EXAMPLE_EMBEDDING = {
|
| 118 |
-
"prompt": "Index our internal policy corpus.",
|
| 119 |
-
"task_type": "embedding", "modalities": ["text"], "jurisdiction": "NZ",
|
| 120 |
-
"data_sensitivity": "restricted", "min_context_tokens": 4096,
|
| 121 |
-
"requires_local": True, "max_fallbacks": 3,
|
| 122 |
-
}
|
| 123 |
-
EXAMPLE_TINY = {
|
| 124 |
-
"prompt": "Smoke test on a low-spec laptop.",
|
| 125 |
-
"task_type": "chat", "modalities": ["text"], "jurisdiction": "NZ",
|
| 126 |
-
"data_sensitivity": "public", "min_context_tokens": 2048,
|
| 127 |
-
"requires_local": True, "requires_json": False, "max_fallbacks": 3,
|
| 128 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
<
|
| 133 |
-
<
|
| 134 |
-
<
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
<g transform="translate(60,80)"><rect width="170" height="80" rx="12" ry="12" fill="#e08a2c" stroke="#9a5416" stroke-width="1.4"/><text x="85" y="36" font-family="Georgia,serif" font-size="18" font-weight="500" fill="#0a0a0b">Capability</text><text x="85" y="60">MODE · CTX · JSON</text></g>
|
| 138 |
-
<g transform="translate(255,80)"><rect width="170" height="80" rx="12" ry="12" fill="#e08a2c" stroke="#9a5416" stroke-width="1.4"/><text x="85" y="36" font-family="Georgia,serif" font-size="18" font-weight="500" fill="#0a0a0b">Sovereignty</text><text x="85" y="60">RESIDENCY · TIER</text></g>
|
| 139 |
-
<g transform="translate(450,80)"><rect width="170" height="80" rx="12" ry="12" fill="#e08a2c" stroke="#9a5416" stroke-width="1.4"/><text x="85" y="36" font-family="Georgia,serif" font-size="18" font-weight="500" fill="#0a0a0b">License</text><text x="85" y="60">ALLOWLIST</text></g>
|
| 140 |
-
<g transform="translate(645,80)"><rect width="170" height="80" rx="12" ry="12" fill="#e08a2c" stroke="#9a5416" stroke-width="1.4"/><text x="85" y="36" font-family="Georgia,serif" font-size="18" font-weight="500" fill="#0a0a0b">Runtime</text><text x="85" y="60">LLAMA.CPP · HF</text></g>
|
| 141 |
-
<g transform="translate(840,80)"><rect width="170" height="80" rx="12" ry="12" fill="#e08a2c" stroke="#9a5416" stroke-width="1.4"/><text x="85" y="36" font-family="Georgia,serif" font-size="18" font-weight="500" fill="#0a0a0b">Score</text><text x="85" y="60">QUALITY · COST</text></g>
|
| 142 |
-
<g transform="translate(1035,80)"><rect width="170" height="80" rx="12" ry="12" fill="#0a0a0b" stroke="#0a0a0b" stroke-width="1.4"/><text x="85" y="36" font-family="Georgia,serif" font-size="18" font-weight="500" fill="#fffefa">Audit</text><text x="85" y="60" fill="#e08a2c">DECISION RECORD</text></g>
|
| 143 |
-
</g>
|
| 144 |
-
<g stroke="#e08a2c" stroke-width="2" fill="none">
|
| 145 |
-
<path d="M232 120 L253 120" marker-end="url(#ar2)"/>
|
| 146 |
-
<path d="M427 120 L448 120" marker-end="url(#ar2)"/>
|
| 147 |
-
<path d="M622 120 L643 120" marker-end="url(#ar2)"/>
|
| 148 |
-
<path d="M817 120 L838 120" marker-end="url(#ar2)"/>
|
| 149 |
-
<path d="M1012 120 L1033 120" marker-end="url(#ar2)"/>
|
| 150 |
-
</g>
|
| 151 |
-
</svg>"""
|
| 152 |
|
| 153 |
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
.lx-hero h1 { margin: 14px 0 12px; font-family: Georgia, Cambria, "Times New Roman", serif; font-size: clamp(40px, 6vw, 80px); line-height: 0.95; font-weight: 500; }
|
| 162 |
-
.lx-hero p.lead { color: var(--lx-muted); max-width: 820px; font-size: clamp(15px, 1.6vw, 19px); line-height: 1.55; }
|
| 163 |
-
.lx-tagline { font-family: Georgia, Cambria, serif; font-style: italic; color: var(--lx-accent-dark); margin-top: 8px; }
|
| 164 |
-
.lx-chips { margin-top: 22px; display: flex; flex-wrap: wrap; gap: 10px; }
|
| 165 |
-
.lx-chips span { border: 1px solid var(--lx-line); border-radius: 999px; padding: 8px 12px; background: #fff; color: var(--lx-muted); font: 700 11px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
|
| 166 |
-
.lx-explainer { background: var(--lx-soft); border: 1px solid var(--lx-line); border-left: 4px solid var(--lx-accent); border-radius: 10px; padding: 20px 24px; margin: 24px 0; }
|
| 167 |
-
.lx-explainer h3 { margin: 0 0 8px; font-family: Georgia, Cambria, serif; font-size: 22px; font-weight: 500; }
|
| 168 |
-
.lx-result-allow { background: #f0f7ec; border: 1px solid #4d6b44; border-left: 6px solid #4d6b44; padding: 20px; border-radius: 10px; }
|
| 169 |
-
.lx-result-deny { background: #fbeded; border: 1px solid #b03a3a; border-left: 6px solid #b03a3a; padding: 20px; border-radius: 10px; }
|
| 170 |
-
.gradio-container button.primary { background: var(--lx-ink) !important; border-color: var(--lx-ink) !important; color: #fff !important; border-radius: 999px !important; font-weight: 700 !important; }
|
| 171 |
-
.gradio-container button.primary:hover { background: var(--lx-accent-dark) !important; border-color: var(--lx-accent-dark) !important; }
|
| 172 |
-
.gradio-container button:not(.primary) { background: #fff !important; border-color: var(--lx-line) !important; color: var(--lx-ink) !important; border-radius: 999px !important; }
|
| 173 |
-
.gradio-container textarea, .gradio-container input, .gradio-container .code { background: #fff !important; color: var(--lx-ink) !important; border-color: var(--lx-line) !important; border-radius: 12px !important; }
|
| 174 |
-
.gradio-container label, .gradio-container .block-title, .gradio-container .block-label { color: var(--lx-accent-dark) !important; font: 700 11px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important; letter-spacing: 0.12em !important; text-transform: uppercase !important; }
|
| 175 |
-
footer { display: none !important; }
|
| 176 |
-
"""
|
| 177 |
|
| 178 |
|
| 179 |
-
def
|
| 180 |
try:
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
"
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
else:
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
</ol>
|
| 259 |
-
</div>
|
| 260 |
-
"""
|
| 261 |
-
)
|
| 262 |
-
|
| 263 |
-
with gr.Row():
|
| 264 |
-
with gr.Column():
|
| 265 |
-
request_box = gr.Code(value=json.dumps(EXAMPLE_CODE, indent=2), language="json", label="Request (JSON)", lines=20)
|
| 266 |
-
with gr.Row():
|
| 267 |
-
route_btn = gr.Button("Route", variant="primary")
|
| 268 |
-
ex_code = gr.Button("Restricted code")
|
| 269 |
-
ex_reason = gr.Button("Reasoning + tools")
|
| 270 |
-
ex_mm = gr.Button("Public multimodal")
|
| 271 |
-
ex_emb = gr.Button("Restricted embedding")
|
| 272 |
-
ex_tiny = gr.Button("Tiny / smoke")
|
| 273 |
-
with gr.Column():
|
| 274 |
-
summary = gr.HTML(value='<div style="color:#726b62; padding:18px"><em>Press <b>Route</b> to evaluate a request against the registry.</em></div>')
|
| 275 |
-
explainer = gr.Markdown()
|
| 276 |
-
with gr.Row():
|
| 277 |
-
sel_json = gr.Code(language="json", label="Selected model (full record)", lines=18)
|
| 278 |
-
rej_json = gr.Code(language="json", label="Rejected candidates (top 10)", lines=18)
|
| 279 |
|
| 280 |
-
route_btn.click(run, inputs=request_box, outputs=[summary, explainer, sel_json, rej_json])
|
| 281 |
-
ex_code.click(lambda: json.dumps(EXAMPLE_CODE, indent=2), outputs=request_box)
|
| 282 |
-
ex_reason.click(lambda: json.dumps(EXAMPLE_REASON, indent=2), outputs=request_box)
|
| 283 |
-
ex_mm.click(lambda: json.dumps(EXAMPLE_MULTIMODAL, indent=2), outputs=request_box)
|
| 284 |
-
ex_emb.click(lambda: json.dumps(EXAMPLE_EMBEDDING, indent=2), outputs=request_box)
|
| 285 |
-
ex_tiny.click(lambda: json.dumps(EXAMPLE_TINY, indent=2), outputs=request_box)
|
| 286 |
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
|
| 300 |
|
| 301 |
if __name__ == "__main__":
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MaramaRoute live demo — runs the real router against the live 98-model
|
| 3 |
+
registry pulled from AbteeXAILab/marama-route on Hugging Face.
|
| 4 |
+
"""
|
| 5 |
import json
|
| 6 |
+
import os
|
| 7 |
from dataclasses import dataclass, field
|
| 8 |
+
from typing import Any, Optional
|
|
|
|
| 9 |
|
| 10 |
import gradio as gr
|
| 11 |
from huggingface_hub import hf_hub_download
|
| 12 |
|
| 13 |
+
REGISTRY_REPO = "AbteeXAILab/marama-route"
|
| 14 |
+
REGISTRY_PATH = "configs/lumynax_model_registry.json"
|
| 15 |
|
| 16 |
+
BRAND_CSS = """
|
| 17 |
+
:root {
|
| 18 |
+
--lx-paper:#fffefa; --lx-ink:#0a0a0b; --lx-amber:#e08a2c;
|
| 19 |
+
--lx-amber-dark:#9a5416; --lx-muted:#726b62; --lx-soft:#f6f0e8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
}
|
| 21 |
+
body, .gradio-container { background: var(--lx-paper) !important; color: var(--lx-ink) !important; }
|
| 22 |
+
h1, h2, h3 { font-family: 'Cormorant Garamond','EB Garamond',Georgia,serif; }
|
| 23 |
+
.lx-hero { background: linear-gradient(135deg,#fffefa 0%,#f6f0e8 100%); border: 1px solid rgba(10,10,11,0.08); border-radius: 12px; padding: 18px 22px; margin-bottom: 14px; }
|
| 24 |
+
.lx-pick { background: #f0f7ee; border-left: 4px solid #4caf50; padding: 12px 14px; border-radius: 6px; }
|
| 25 |
+
.lx-runner-up { background: var(--lx-soft); border-left: 3px solid var(--lx-amber); padding: 10px 12px; border-radius: 6px; margin-top: 8px; }
|
| 26 |
+
table.lx-runners { border-collapse: collapse; width: 100%; font-size: 0.9em; }
|
| 27 |
+
table.lx-runners th, table.lx-runners td { border-bottom: 1px solid rgba(10,10,11,0.08); padding: 6px 8px; text-align: left; }
|
| 28 |
+
"""
|
| 29 |
|
| 30 |
+
HERO_HTML = """
|
| 31 |
+
<div class="lx-hero">
|
| 32 |
+
<h1 style="margin:0 0 6px 0;">🧭 MaramaRoute — sovereign router</h1>
|
| 33 |
+
<p style="margin:0; color:var(--lx-muted);"><em>Ko te marama te tuapapa.</em> Pick the right model from the live LumynaX family of 98+ models — gated on residency, modality, tools, and sovereignty tier.</p>
|
| 34 |
+
<p style="margin:8px 0 0 0; font-size:0.9em;">Gates: <b>Capability</b> → <b>Sovereignty</b> → <b>License</b> → <b>Runtime</b> → <b>Score</b> → <b>Audit</b></p>
|
| 35 |
+
</div>
|
| 36 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
|
| 39 |
+
@dataclass
|
| 40 |
+
class RouteDecision:
|
| 41 |
+
pick: Optional[dict] = None
|
| 42 |
+
score: float = 0.0
|
| 43 |
+
runners_up: list = field(default_factory=list)
|
| 44 |
+
rejected: list = field(default_factory=list)
|
| 45 |
+
reason: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
|
| 48 |
+
def _load_registry() -> dict[str, Any]:
|
| 49 |
try:
|
| 50 |
+
p = hf_hub_download(repo_id=REGISTRY_REPO, filename=REGISTRY_PATH,
|
| 51 |
+
repo_type="model", token=os.environ.get("HF_TOKEN"))
|
| 52 |
+
return json.loads(open(p, encoding="utf-8").read())
|
| 53 |
+
except Exception as e:
|
| 54 |
+
return {"registry_id": "fallback-empty", "models": [], "_error": str(e)}
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
REGISTRY = _load_registry()
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def route(prompt: str, modalities, requires_local: bool, requires_tools: bool,
|
| 61 |
+
requires_json: bool, jurisdiction: str, max_params_b: float, task_hint: str) -> RouteDecision:
|
| 62 |
+
models = REGISTRY.get("models", [])
|
| 63 |
+
d = RouteDecision()
|
| 64 |
+
candidates = []
|
| 65 |
+
for m in models:
|
| 66 |
+
if any(mod not in (m.get("modalities") or []) for mod in modalities):
|
| 67 |
+
d.rejected.append((m["repo_id"], "modality")); continue
|
| 68 |
+
if requires_local and (m.get("sovereignty_tier") or 5) < 3:
|
| 69 |
+
d.rejected.append((m["repo_id"], "sovereignty<3")); continue
|
| 70 |
+
if jurisdiction and jurisdiction not in (m.get("residency") or []):
|
| 71 |
+
d.rejected.append((m["repo_id"], f"residency!={jurisdiction}")); continue
|
| 72 |
+
if requires_tools and not m.get("supports_tools"):
|
| 73 |
+
d.rejected.append((m["repo_id"], "no tools")); continue
|
| 74 |
+
if requires_json and not m.get("supports_json"):
|
| 75 |
+
d.rejected.append((m["repo_id"], "no json")); continue
|
| 76 |
+
if max_params_b > 0 and (m.get("total_params_b") or 0) > max_params_b:
|
| 77 |
+
d.rejected.append((m["repo_id"], f"params>{max_params_b}")); continue
|
| 78 |
+
q = int(m.get("quality_rank") or 5)
|
| 79 |
+
s = int(m.get("sovereignty_tier") or 3)
|
| 80 |
+
c = int(m.get("cost_rank") or 5)
|
| 81 |
+
score = (6 - q) * 2 + s * 1.5 + (6 - c) * 0.5
|
| 82 |
+
if task_hint:
|
| 83 |
+
tags = " ".join(m.get("tags") or []).lower() + " " + m["model_id"].lower()
|
| 84 |
+
if task_hint.lower() in tags:
|
| 85 |
+
score += 3
|
| 86 |
+
if max_params_b > 0 and (m.get("total_params_b") or 0) <= max_params_b * 0.5:
|
| 87 |
+
score += 1
|
| 88 |
+
candidates.append((score, m))
|
| 89 |
+
candidates.sort(key=lambda x: -x[0])
|
| 90 |
+
if candidates:
|
| 91 |
+
d.score, d.pick = candidates[0]
|
| 92 |
+
d.runners_up = candidates[1:5]
|
| 93 |
+
d.reason = f"top-of-{len(candidates)} candidates after 5 gates"
|
| 94 |
else:
|
| 95 |
+
d.reason = "no candidates survived gating"
|
| 96 |
+
return d
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def render(prompt: str, modalities, requires_local: bool, requires_tools: bool,
|
| 100 |
+
requires_json: bool, jurisdiction: str, max_params_b: float, task_hint: str) -> str:
|
| 101 |
+
d = route(prompt, modalities, requires_local, requires_tools, requires_json,
|
| 102 |
+
jurisdiction, max_params_b, task_hint)
|
| 103 |
+
if not d.pick:
|
| 104 |
+
return f'<div class="lx-pick" style="background:#fde8e8;border-left-color:#c1351a;"><b>No candidate matches your filters.</b><br/>{d.reason}<br/>Tried {len(d.rejected)} models, all rejected.</div>'
|
| 105 |
+
p = d.pick
|
| 106 |
+
runners_html = "<table class='lx-runners'><tr><th>Runner-up</th><th>Score</th><th>Params</th></tr>"
|
| 107 |
+
for s, m in d.runners_up:
|
| 108 |
+
tp = m.get("total_params_b"); ap = m.get("active_params_b")
|
| 109 |
+
runners_html += f"<tr><td><code>{m['repo_id'].split('/')[-1]}</code></td><td>{s:.2f}</td><td>{tp}B{f'/{ap}Ba' if ap else ''}</td></tr>"
|
| 110 |
+
runners_html += "</table>"
|
| 111 |
+
tp = p.get("total_params_b"); ap = p.get("active_params_b")
|
| 112 |
+
p_str = f"{tp}B" + (f" / {ap}B active" if ap else "")
|
| 113 |
+
return f"""
|
| 114 |
+
<div class='lx-pick'>
|
| 115 |
+
<div style='display:flex; justify-content:space-between; align-items:baseline;'>
|
| 116 |
+
<h2 style='margin:0;'>🎯 {p['title']}</h2>
|
| 117 |
+
<span style='font-family:monospace;'>score {d.score:.2f}</span>
|
| 118 |
+
</div>
|
| 119 |
+
<p style='margin:6px 0 10px 0;'><a href='https://huggingface.co/{p['repo_id']}' target='_blank'><code>{p['repo_id']}</code></a></p>
|
| 120 |
+
<div style='display:grid; grid-template-columns:1fr 1fr 1fr; gap:8px; font-size:0.95em;'>
|
| 121 |
+
<div><b>Params:</b> {p_str}</div>
|
| 122 |
+
<div><b>Context:</b> {p.get('context_tokens','—')}</div>
|
| 123 |
+
<div><b>Runtime:</b> {p.get('runtime','—')}</div>
|
| 124 |
+
<div><b>Modalities:</b> {', '.join(p.get('modalities') or [])}</div>
|
| 125 |
+
<div><b>Sovereignty:</b> tier {p.get('sovereignty_tier')}</div>
|
| 126 |
+
<div><b>License:</b> {p.get('license_id','—')}</div>
|
| 127 |
+
</div>
|
| 128 |
+
<p style='margin:10px 0 4px 0; font-size:0.9em; color:var(--lx-muted);'>{d.reason}. Run with <code>hf download {p['repo_id']}</code> or <code>lumynax run {p['repo_id'].split('/')[-1]}</code>.</p>
|
| 129 |
+
</div>
|
| 130 |
+
<div class='lx-runner-up'>
|
| 131 |
+
<b>Runner-ups (next 4):</b>
|
| 132 |
+
{runners_html}
|
| 133 |
+
</div>
|
| 134 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
+
REGISTRY_NOTE = f"Loaded {len(REGISTRY.get('models', []))} models from {REGISTRY_REPO}"
|
| 138 |
+
|
| 139 |
+
with gr.Blocks(css=BRAND_CSS, title="LumynaX · MaramaRoute") as demo:
|
| 140 |
+
gr.HTML(HERO_HTML)
|
| 141 |
+
gr.Markdown(f"_{REGISTRY_NOTE}_")
|
| 142 |
+
with gr.Row():
|
| 143 |
+
with gr.Column(scale=2):
|
| 144 |
+
prompt = gr.Textbox(label="Prompt or task", placeholder="What do you need the model for?", lines=2)
|
| 145 |
+
with gr.Row():
|
| 146 |
+
modalities = gr.CheckboxGroup(["text", "vision", "audio"], value=["text"], label="Required modalities")
|
| 147 |
+
jurisdiction = gr.Dropdown(["NZ", "AU", "global", ""], value="NZ", label="Jurisdiction (residency)")
|
| 148 |
+
with gr.Row():
|
| 149 |
+
requires_local = gr.Checkbox(False, label="Requires sovereignty tier ≥ 3 (local-runnable)")
|
| 150 |
+
requires_tools = gr.Checkbox(False, label="Requires tool-calling")
|
| 151 |
+
requires_json = gr.Checkbox(False, label="Requires JSON-mode")
|
| 152 |
+
with gr.Row():
|
| 153 |
+
max_params_b = gr.Slider(0, 700, value=0, step=1, label="Max total params (B). 0 = unlimited")
|
| 154 |
+
task_hint = gr.Textbox(label="Task hint (matches tags + slug)", placeholder="e.g. coder, reasoning, vision, translate")
|
| 155 |
+
go = gr.Button("Route", variant="primary")
|
| 156 |
+
with gr.Column(scale=3):
|
| 157 |
+
out = gr.HTML()
|
| 158 |
+
gr.Examples(
|
| 159 |
+
examples=[
|
| 160 |
+
["Fix this Python bug", ["text"], True, False, False, "NZ", 100, "coder"],
|
| 161 |
+
["Describe what's in this image", ["text", "vision"], False, False, False, "NZ", 0, "vision"],
|
| 162 |
+
["Solve this proof", ["text"], False, False, False, "NZ", 0, "reasoning"],
|
| 163 |
+
["Embed these documents", ["text"], True, False, True, "NZ", 5, "embedding"],
|
| 164 |
+
["Translate to te reo Maori", ["text"], True, False, False, "NZ", 5, "translate"],
|
| 165 |
+
["Long-context document analysis (500K tokens)", ["text"], False, False, False, "NZ", 50, "long-context"],
|
| 166 |
+
],
|
| 167 |
+
inputs=[prompt, modalities, requires_local, requires_tools, requires_json, jurisdiction, max_params_b, task_hint],
|
| 168 |
+
)
|
| 169 |
+
go.click(render, [prompt, modalities, requires_local, requires_tools, requires_json, jurisdiction, max_params_b, task_hint], out)
|
| 170 |
+
gr.Markdown("---\n*Real router · live registry · 98 models · Ko te marama te tuapapa.*")
|
| 171 |
|
| 172 |
|
| 173 |
if __name__ == "__main__":
|
requirements.txt
CHANGED
|
@@ -1,2 +1,2 @@
|
|
| 1 |
-
gradio
|
| 2 |
-
|
|
|
|
| 1 |
+
gradio==5.50.0
|
| 2 |
+
huggingface_hub>=0.27
|