AbteeXAILabs commited on
Commit
5bdb1b0
·
verified ·
1 Parent(s): aa4501a

feat: real router + live 98-model registry

Browse files
Files changed (2) hide show
  1. app.py +153 -281
  2. requirements.txt +2 -2
app.py CHANGED
@@ -1,301 +1,173 @@
1
- """MaramaRoute Live — interactive sovereign router demo (v2 polish)."""
2
- from __future__ import annotations
3
-
 
4
  import json
 
5
  from dataclasses import dataclass, field
6
- from pathlib import Path
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
- # ---------- Embedded router ----------
14
- @dataclass
15
- class RouteDecision:
16
- selected: Optional[Dict[str, Any]]
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
- HERO_SVG = """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 220" role="img" aria-label="MaramaRoute architecture">
132
- <defs><marker id="ar2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 z" fill="#e08a2c"/></marker></defs>
133
- <rect width="1280" height="220" fill="#fffefa"/>
134
- <rect x="0" y="0" width="1280" height="3" fill="#0a0a0b"/>
135
- <text x="64" y="32" font-family="ui-monospace,Menlo,Consolas,monospace" font-size="11" font-weight="700" letter-spacing="0.2em" fill="#9a5416">MARAMAROUTE · SIX-GATE SOVEREIGN ROUTER</text>
136
- <g font-family="ui-monospace,Menlo,Consolas,monospace" font-size="11" font-weight="700" letter-spacing="0.14em" fill="#fff7ed" text-anchor="middle">
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
- BRAND_CSS = """
155
- :root { --lx-ink:#0a0a0b; --lx-paper:#fffefa; --lx-soft:#f6f0e8; --lx-accent:#e08a2c; --lx-accent-dark:#9a5416; --lx-muted:#726b62; --lx-line:rgba(10,10,11,0.12); }
156
- body, .gradio-container { background: var(--lx-paper) !important; color: var(--lx-ink) !important; font-family: Aptos, "Avenir Next", "Segoe UI", Helvetica, Arial, sans-serif !important; }
157
- .lx-shell { width: min(1280px, calc(100% - 48px)); margin: 0 auto; padding-bottom: 60px; }
158
- .lx-hero { position: relative; padding: 56px 0 28px; border-bottom: 1px solid var(--lx-line); }
159
- .lx-hero::before { content: ""; position: absolute; top: 0; right: 0; width: min(420px, 42vw); height: 3px; background: var(--lx-accent); }
160
- .lx-eyebrow { color: var(--lx-accent-dark); font: 700 12px/1.3 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.18em; text-transform: uppercase; }
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 run(req_text: str) -> tuple[str, str, str, str]:
180
  try:
181
- request = json.loads(req_text)
182
- except Exception as exc:
183
- err = f'<div class="lx-result-deny"><h3>Parse error</h3><pre>{type(exc).__name__}: {exc}</pre></div>'
184
- return err, "", "", ""
185
- decision = route(REGISTRY, request)
186
- if decision.selected:
187
- s = decision.selected
188
- rows = "".join(
189
- f"<tr><td><b>{k}</b></td><td><code>{v}</code></td></tr>" for k, v in [
190
- ("Repo", s["repo_id"]), ("Family", s.get("family", "—")),
191
- ("Runtime", s.get("runtime", "—")), ("Modalities", ", ".join(s.get("modalities", []))),
192
- ("Context", f"{s.get('context_tokens', '—')} tok"),
193
- ("Sovereignty tier", s.get("sovereignty_tier", "—")),
194
- ("Quality / cost rank", f"{s.get('quality_rank', '—')} / {s.get('cost_rank', '—')}"),
195
- ("Tools / JSON", f"{s.get('supports_tools')} / {s.get('supports_json')}"),
196
- ("Score", s.get("_score", "—")),
197
- ]
198
- )
199
- fb_rows = "".join(f'<li><code>{fb["repo_id"]}</code> · score <b>{fb["_score"]}</b></li>' for fb in decision.fallbacks) or "<li>—</li>"
200
- summary = (
201
- f'<div class="lx-result-allow">'
202
- f'<h3>✓ Selected — <code>{s["repo_id"]}</code></h3>'
203
- f'<table style="margin-top:8px"><tbody>{rows}</tbody></table>'
204
- f'<p style="margin-top:14px"><b>Fallbacks (next-best matches)</b></p><ol>{fb_rows}</ol>'
205
- f'</div>'
206
- )
207
- explainer = (
208
- "### Why this model won\n\n"
209
- f"The router scores each candidate on **quality** (lower rank = better, weighted 2&times;), **sovereignty tier** (weighted 1.5&times;), "
210
- f"and **cost-fit** (lower = lighter, weighted 0.5&times;). Matching the **task tag** (`{request.get('task_type', '—')}`) adds +3. "
211
- f"`requires_local` with a `llama_cpp` runtime adds +1. Frontier-size models (>50B active) lose 0.5.\n\n"
212
- f"**This request asked for:** `{request.get('task_type')}` · modalities `{request.get('modalities')}` · "
213
- f"jurisdiction `{request.get('jurisdiction')}` · sensitivity `{request.get('data_sensitivity')}` · "
214
- f"local `{request.get('requires_local')}` · context `{request.get('min_context_tokens')}`.\n\n"
215
- f"**Rejected candidates:** {len(decision.rejected)}. Top reasons are jurisdiction mismatch, missing modality, runtime not local, or sovereignty tier too low for sensitive data."
216
- )
 
 
 
 
 
 
 
 
217
  else:
218
- summary = (
219
- f'<div class="lx-result-deny"><h3>✗ No model satisfies the request</h3>'
220
- f'<p>{len(decision.rejected)} candidates were rejected. See "Rejected candidates" for the gate each one failed.</p></div>'
221
- )
222
- explainer = "### Nothing matched\n\nRelax one constraint and try again: drop `requires_local`, broaden `modalities`, lower `min_context_tokens`, or change `data_sensitivity`."
223
-
224
- selected_json = json.dumps(decision.selected, indent=2) if decision.selected else "{}"
225
- rejected_json = json.dumps(decision.rejected[:10] + ([{"note": f"... {len(decision.rejected) - 10} more"}] if len(decision.rejected) > 10 else []), indent=2)
226
- return summary, explainer, selected_json, rejected_json
227
-
228
-
229
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", neutral_hue="stone"), css=BRAND_CSS, title="MaramaRoute Live") as demo:
230
- with gr.Column(elem_classes="lx-shell"):
231
- gr.HTML(
232
- f"""
233
- <section class="lx-hero">
234
- <div class="lx-eyebrow">AbteeX AI Labs · Aotearoa New Zealand · Routing across {MODEL_COUNT} LumynaX models</div>
235
- <h1>MaramaRoute <span style="color:#e08a2c">Live</span></h1>
236
- <p class="lead">A <b>sovereign model router</b> for the LumynaX release family. Paste a request &mdash; modality, jurisdiction, data sensitivity, runtime constraints &mdash; and see which model wins, with full fallback chain and rejection reasons for every other candidate.</p>
237
- <p class="lx-tagline">"Ko te mārama te tūāpapa." — the light is the foundation.</p>
238
- <div class="lx-chips">
239
- <span>{MODEL_COUNT} models</span><span>Six gates</span><span>Deterministic</span><span>Sovereignty-weighted</span><span>Aotearoa NZ kaupapa</span>
240
- </div>
241
- </section>
242
- """
243
- )
244
-
245
- gr.HTML(f'<div style="margin: 20px 0 8px"><div style="font: 700 11px/1.2 ui-monospace, Menlo, Consolas, monospace; letter-spacing: 0.18em; color:#9a5416; text-transform:uppercase">Architecture · Six gates</div>{HERO_SVG}</div>')
246
-
247
- gr.HTML(
248
- """
249
- <div class="lx-explainer">
250
- <h3>How the router decides</h3>
251
- <ol>
252
- <li><b>Capability gate</b> — model must support the request's modalities, context window, tool calling, and JSON mode.</li>
253
- <li><b>Sovereignty gate</b> — request's jurisdiction must be in the model's residency; sensitive data requires sovereignty tier ≥ 2.</li>
254
- <li><b>License gate</b> — optional license allowlist and model-card provenance.</li>
255
- <li><b>Runtime gate</b> — <code>requires_local</code> excludes hosted-only runtimes.</li>
256
- <li><b>Score</b> — candidates are scored on quality, cost-fit, sovereignty tier, and task-tag match.</li>
257
- <li><b>Audit</b> — decision, selected model, fallbacks, and rejection reasons are persisted.</li>
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
- gr.HTML(
288
- """
289
- <div style="margin-top:32px; padding-top:24px; border-top:1px solid rgba(10,10,11,0.12); text-align:center; color:#726b62; font-size:13px">
290
- <em>Local roots, global work. · Sovereignty is a design property, not a deployment option.</em><br/>
291
- <b><a href="https://huggingface.co/AbteeXAILab/marama-route" style="color:#9a5416">Model repo</a></b> ·
292
- <b><a href="https://huggingface.co/AbteeXAILab/sovereigncode" style="color:#9a5416">SovereignCode</a></b> ·
293
- <b><a href="https://huggingface.co/spaces/AbteeXAILab/lumynax-live-demo" style="color:#9a5416">Live demo</a></b> ·
294
- <b><a href="https://abteex.com" style="color:#9a5416">abteex.com</a></b> ·
295
- <b><a href="https://lumynax.com" style="color:#9a5416">lumynax.com</a></b>
296
- </div>
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>=5.50,<6
2
- huggingface-hub>=0.25
 
1
+ gradio==5.50.0
2
+ huggingface_hub>=0.27