Spaces:
Running
Running
| # Loom Model Panel: A Privacy-First Gateway for Uncorrelated Frontier-Model Judgment on Free Tiers | |
| *System design document β 2026-06. All claims cross-checked against the code at the | |
| commit that ships this file; constants quoted are the env-overridable defaults.* | |
| ## 1. Problem & motivation | |
| A single LLM reviewing its own plans inherits its own blind spots. The highest-leverage | |
| use of *other* frontier models is as **uncorrelated second opinions**: a panel of | |
| models with different training lineages (Kimi, DeepSeek, GLM, Nemotron, Llama, | |
| MiniMaxβ¦) judging one artifact produces failure-mode diversity a single model cannot. | |
| Three constraints shape the system: | |
| 1. **Cost: zero.** Every model must come from recurring-free tiers (NVIDIA NIM free | |
| credits, Cloudflare Workers AI, GitHub Models, OpenRouter `:free`). Free tiers are | |
| throttled, flaky, and quota-capped β so reliability must be *engineered around* the | |
| providers, not assumed from them. | |
| 2. **Privacy: per-user, verifiable.** The deployment model is **one instance per user, | |
| the user's own provider keys, nothing shared** β privacy by isolation rather than by | |
| access control. On top, a request-level **privacy router** refuses providers that | |
| train on or log prompts. | |
| 3. **Operations: a single ephemeral container.** The target platform (Hugging Face | |
| Spaces, free Docker tier) gives one container that is recycled on every deploy. | |
| Anything that must survive β async jobs, metrics, templates, cache β needs explicit | |
| durability machinery. | |
| The result is a stdlib-only Python service (plus `httpx` and optionally | |
| `huggingface_hub`) exposing a token-guarded HTTP API: one-shot panels | |
| (`/api/critique`, `/api/panel`), whole-repo review (`files`/`repo_url` forms), and | |
| multi-stage orchestrated pipelines (`/api/run` + templates). | |
| ## 2. Architecture overview | |
| ``` | |
| client ββHTTPβββΆ BoundedThreadingHTTPServer (MAX_WORKERS=48, REQUEST_TIMEOUT_S=30) | |
| β bearer auth (constant-time), body cap 8MB, 503/429 backpressure | |
| βΌ | |
| request builders (panel / critique / run) | |
| ββ repopack: {files|repo_url} β packed blob + per-judge context budgeting | |
| ββ privacy router: strict | fallback | off | |
| ββ prompt cache (exact-match, per-space) | |
| β | |
| ββββββββββββ΄ββββββββββββ | |
| sync (asyncio.run) async JobRunner (one background event loop) | |
| β β durable: JobStore (atomic file + HF Dataset mirror) | |
| βΌ βΌ reload-on-boot; MAX_INFLIGHT_JOBS=200 | |
| route_judge / orchestrator stages | |
| β per logical model: | |
| βΌ | |
| SlotScheduler βββΆ provider hosts (NVIDIA / Cloudflare / GitHub / OpenRouter) | |
| rotation Β· pacing Β· cooldown/blacklist Β· cross-provider failover | |
| β | |
| βΌ every call β per-profile MetricStore (HF-mirrored) | |
| ``` | |
| Two execution planes share one substrate. The **synchronous plane** runs a request to | |
| completion on a bounded HTTP worker via `asyncio.run`. The **asynchronous plane** | |
| (`{"async": true}`) hands work to a single long-lived background event loop in | |
| `JobRunner`, returns a `job_id` immediately, and the client polls `GET /api/jobs/<id>` | |
| for partial results. Both planes call the same `route_judge`/scheduler/metrics core, so | |
| routing, privacy, caching, and telemetry behave identically regardless of plane. | |
| The canonical request is for a **logical model** (e.g. `kimi-k2.6`); the scheduler maps | |
| it to an ordered list of physical `(provider, model)` hosts and bounces across them on | |
| failure. Catalogs (`providers_catalog.json`, `models_catalog.json`, `benchmarks.json`, | |
| `reasoning_catalog.json`, `panel.json`) are the single source of truth β adding a model | |
| or provider is a config edit, not a code change. | |
| ## 3. Subsystems | |
| ### 3.1 Routing & cross-provider failover (`scheduler.py`) | |
| `SlotScheduler` owns rotation and resilience. `pick_slot_from(candidates, role, | |
| exclude_providers)` returns the best eligible host for a logical model, where eligible = | |
| not blacklisted, not in cooldown, not excluded. Ranking favors the least-recently-attempted | |
| provider (rotation), then healthier model families, then freshness β so a flaky provider | |
| drifts to the back instead of being hammered. `route_judge` (`jobs.py`) drives the bounce | |
| loop: on any 429/5xx/empty/timeout it records a penalty (cooldown or blacklist), emits a | |
| metric, and re-picks the next provider hosting the *same logical model*. Retries are | |
| cross-provider by default (`JUDGE_RETRIES=0`): we move hosts rather than re-hit a failing | |
| one. **Pacing** reserves each provider's next allowed call time under a lock | |
| (`wait_for_provider_pacing`), so concurrent callers across both planes stagger to | |
| distinct slots and stay under per-provider RPM. | |
| ### 3.2 Privacy router (`jobs.py` `_privacy_tiers` / `_pick_tiered`, `providers.slot_is_privacy_safe`) | |
| Each provider carries a `trains_on_data` posture (`true`/`false`/`"per-model"`); a host is | |
| privacy-safe iff it does not train on or log submissions. OpenRouter `:free` routes require | |
| training consent, so they are `per-model` β unsafe; HF Inference is a proxy whose posture is | |
| the routed backend's. Three request modes: **strict** (default) drops every unsafe host and | |
| returns `privacy_blocked` rather than leak; **fallback** tries safe hosts first and an unsafe | |
| host only as a last resort; **off** uses all. `PRIVACY_MODE` locks an instance to strict; | |
| `no_store` runs a job without persisting its content anywhere. | |
| ### 3.3 Prompt cache (`promptcache.py`) | |
| A per-space exact-match cache keyed by `sha256(logical, system_prompt, user_msg, max_tokens, | |
| reasoning, research, privacy)`. **Privacy is part of the key** β a result produced under | |
| `privacy=off` can never be served to a `strict` request. In-memory LRU (`CACHE_MAX=512`) + | |
| atomic disk mirror, TTL `CACHE_TTL_S=24h`. Only successful calls are cached; `no_store` | |
| skips writes. A hit returns prior output with zero provider calls and transmits nothing. | |
| ### 3.4 Durable async jobs (`jobs.py` `JobRunner`, `jobstore.py`) | |
| Free containers recycle on deploy, which previously killed long async runs. `JobStore` | |
| writes each job to `data/jobs/<id>.json` via atomic tmp-rename on every state transition and | |
| mirrors to a private HF Dataset, so jobs survive both crashes and redeploys. On boot the | |
| runner re-hydrates: panel jobs reschedule any unfinished judge (the `_exec` block carries the | |
| prompt/budgets needed to resume), and mid-flight orchestration runs are marked `interrupted`. | |
| `MAX_INFLIGHT_JOBS=200` caps simultaneously-running jobs (the async plane bypasses the HTTP | |
| worker cap); `JOB_TTL_S=6h` prunes finished jobs. | |
| ### 3.5 Codebase ingestion (`repopack.py`) | |
| Turns `{path: content}` or a fetched `repo_url` tarball (GitHub codeload, stdlib only, | |
| `REPO_MAX_BYTES=50MB`) into one annotated blob: binaries/lockfiles/`node_modules` filtered, | |
| per-file `===== FILE: =====` delimiters, token estimate chars/3 for code. Each judge gets a | |
| pack **fitted to its own context window** (`ctx` per host in `models_catalog.json`); when a | |
| repo exceeds a judge's budget, low-value files (generated/bulk) are dropped first, tests and | |
| docs last, and the result reports `coverage` so partial-context reviews are honest. A | |
| `diff_mode {base_ref, head_ref}` packs only the changed files plus a unified diff. | |
| ### 3.6 Orchestrator & template library (`orchestrator/`, `orchestrator/templates/`) | |
| Beyond one-shot panels, `/api/run` executes multi-stage **schematics**: stages run | |
| sequentially, a stage may **fan out** (one shard per item in a JSON list a prior stage | |
| emitted, shards spread across providers via shared in-flight exclusion), and a **judge loop** | |
| retries the final body up to `max_rounds` if rules/criteria fail. The built-in power | |
| templates β `repo_audit`, `design_doc`, `redteam`, `panel_debate` β each map β fan-out β | |
| synthesize β judge, and bake in hard-won lessons (e.g. `repo_audit` quarantines | |
| confident-but-unverifiable concurrency claims into a "False-positive watch" section). | |
| File-output mode reconstructs a multi-file artifact tree from marker blocks. | |
| ### 3.7 Three-layer judge (`judge/`) | |
| Output hardening runs in order and merges reports: **Layer 1** declarative rules | |
| (`engine.py`: min/max words, required sections, banned phrases, regex); **Layer 2** code | |
| plugins (`citation_integrity`, `url_health`, `json_parses`); **Layer 3** LLM-as-judge | |
| natural-language criteria. Hard fails trigger a retry round; soft flags annotate. | |
| ### 3.8 Per-profile metrics (`metrics.py`) | |
| Every call emits a metric tagged with a `profile`, so cost/throttle/latency are tracked per | |
| identity, not globally. Rolling per-provider call/token windows (pruned on write, 24h) feed a | |
| **budget guard** that cools a provider when a profile hits its published daily ceiling. | |
| `HOT_WINDOW=5000` events in memory; flushed every `FLUSH_EVERY=25` and mirrored to the HF | |
| Dataset. Outputs are sampled only if `METRICS_SAMPLE_OUTPUTS=1` (off by default), and never | |
| for `no_store` jobs. Exposed via `GET /api/stats`, `/api/metrics?profile=`, `/api/roster`. | |
| ## 4. Guarantees | |
| - **β₯2 privacy-safe frontier models, always.** `panel.roster()` joins the benchmark catalog | |
| with live host health and the privacy classification; `/health.frontier_ok` is true iff at | |
| least two *frontier* logical models have a privacy-safe host routable right now. It reports | |
| false honestly rather than silently routing to an unsafe or non-frontier host. | |
| - **Privacy by isolation + verifiable routing.** One instance per user with their own keys; | |
| strict-by-default routing; `no_store` for zero retention. Backpressure responses (503/429) | |
| carry no prompt content. | |
| - **Durability across crash and redeploy.** Atomic local writes + private HF Dataset mirror; | |
| reload-on-boot resumes unfinished panel judges. | |
| ## 5. Threat model & security | |
| - **Auth.** Every non-health route is bearer-gated with a constant-time compare | |
| (`hmac.compare_digest`); an unset `CRITIQUE_TOKEN` refuses service (503) rather than | |
| serving open. | |
| - **Resource exhaustion / DoS.** `BoundedThreadingHTTPServer` caps concurrent sync workers | |
| (`MAX_WORKERS=48`) and returns 503+`Retry-After` over capacity without spawning a thread; | |
| `REQUEST_TIMEOUT_S=30` drops slowloris connections; `MAX_INFLIGHT_JOBS=200` caps the async | |
| plane with 429+`Retry-After`; `MAX_BODY_BYTES=8MB` bounds request size. Per-user instances | |
| make these per-tenant by construction; the frontend can tune the env knobs per user. | |
| - **Data at rest.** Job snapshots and the cache live on the instance's local disk and the | |
| user's *private* HF Dataset. `no_store` opts a job out entirely (trading deploy-resume for | |
| zero retention). Metrics store no prompt/output text unless explicitly enabled. | |
| - **Provider trust.** The privacy router is the control that keeps prompts off | |
| training/logging hosts; `:free` and unpinned-proxy routes are classified unsafe. | |
| - **Secret hygiene.** Keys are Space secrets, never committed; `data/` is gitignored. | |
| ## 6. Evaluation | |
| - **Offline simulation (zero quota).** A `MOCK_MODE` provider yields deterministic, | |
| fault-injectable responses; nine sims exercise rotation/failover/budget/profile isolation | |
| (`sim_scenarios`), research ReAct (`sim_research`), crash-resume (`sim_persistence`), | |
| orchestration + fanout diversity (`sim_orchestrator`), the privacy router (`sim_privacy`), | |
| the cache incl. the privacy-key (`sim_cache`), ingestion + budget fitting (`sim_repopack`), | |
| the template library (`sim_templates`), and the concurrency bounds β 503/slowloris/429 | |
| (`sim_server_limits`). All green. | |
| - **Live verification.** Every roster slug is probed (16-token call) before catalog entry | |
| (`docs/ROSTER-2026-06.md`); failures are recorded and excluded, not shipped broken. | |
| - **Whole-repo reality check.** A 163k-token review of this codebase confirmed strong hosts | |
| (Kimi, MiniMax) genuinely reason over the full repo, surfaced two real P1 bugs since fixed | |
| (cache privacy bypass; `privacy_blocked` fallthrough) and several false positives (later | |
| disproved), and exposed reliability variance (qwen3.5-397b returned empty content β demoted | |
| from the default panel). | |
| ## 7. Limitations & future work | |
| - **`/api/run` does not context-fit the prompt.** The repo-pack per-judge budgeting lives in | |
| the `/api/panel` `files` form, not the orchestrator path β so a large prompt sent through a | |
| template hits small-context hosts (e.g. Cloudflare llama-3.3, 24k) with **413** and only | |
| survives via cross-provider failover to big-context hosts. Fix: have the scheduler skip | |
| hosts whose `ctx` cannot fit the estimated input, or extend ingestion budgeting to `/api/run`. | |
| - **Per-request event loop on the sync plane.** Each sync request runs `asyncio.run` with a | |
| fresh `httpx.AsyncClient`, discarding connection pooling; acceptable under the worker cap, | |
| but a shared loop/client would be more efficient at scale. | |
| - **Benchmarks are indicative.** `benchmarks.json` scores are hand-curated and dated, used | |
| only to order the roster and define "frontier" β not an authoritative ranking. | |
| - **Provider breadth.** Current pool is four providers; expanding to Groq, Cerebras, Mistral | |
| La Plateforme, and HF Inference (each with a privacy block + live probe) widens the | |
| frontier-safety margin (planned). | |
| ## 8. Reproducibility | |
| Duplicate the Hugging Face Space, set your own provider keys as Space secrets | |
| (`NVIDIA_API_KEY`, `CF_API_TOKEN`+`CF_ACCOUNT_ID`, `GITHUB_TOKEN`, `OPENROUTER_API_KEY`) and | |
| a `CRITIQUE_TOKEN`; optionally `METRICS_HF_REPO`/`JOBS_HF_REPO` for cross-deploy durability. | |
| The service boots with whatever keys are present and reports its live posture at `/health` | |
| and `/api/roster`. Everything is stdlib + `httpx`; no build step. See `README.md` for the | |
| API surface and `PRIVACY.md` for the full privacy/off-switch matrix. | |