pi-blablador0.1.2
Blablador (Helmholtz / JSC) provider for Pi: live alias discovery, per-family thinking effort, measured context windows
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-blablador
Blablador is the research LLM service run by JSC (Forschungszentrum
Jülich) for Helmholtz: a rotating set of open models behind an OpenAI-compatible API. This is a
Pi provider for it. It discovers the live alias-* catalog on every refresh, maps
Pi's thinking levels onto each backend's own reasoning_effort vocabulary, and reports the context
window the gateway actually enforces. A models.json entry cannot do any of those three, because the
alias set changes without notice and /v1/models only has the data at runtime.
Install
pi install npm:pi-blablador
Then /reload: pi --list-models blablador should list 21 chat models.
Auth
Blablador takes a Helmholtz Codebase personal access token (a GitLab PAT with the read_user scope):
export BLABLADOR_API_TOKEN=glpat-...
Create one at https://codebase.helmholtz.cloud/-/user_settings/personal_access_tokens. Tokens last at most one year — a sudden 401 is usually this.
/login works too: pick Blablador, paste the key, and it is stored in ~/.pi/agent/auth.json,
where it takes precedence over the environment variable. The provider id stays lowercase — blablador
is what --model blablador/… and the settings resolve against, Blablador is only the label.
pi auth check --provider blablador prints ready when a key is resolvable and not_ready when not,
which is the fastest way to tell a missing token from a throttled one.
Needs Pi 1.4.0 or newer — the only version this was tested against.
Thinking levels
Every backend has its own reasoning_effort vocabulary and answers HTTP 400 for anything outside it,
so the map is per model family:
| family | accepts |
|---|---|
| qwen | none | low | medium | xhigh |
| oss (gpt-oss) | low | medium | high |
| kimi | none | low | high | max |
| deepseek | none | low | high | xhigh | max |
| muse | low | medium | high | xhigh |
| generic (glm, minimax, mimo, aleph, …) | none | low | medium | high | xhigh |
| none (apertus, eve, ocr) | — (the field is never sent) |
Pi's own levels are folded onto these, so you always pick from Pi's list: --thinking high on a Qwen
model sends xhigh, --thinking medium on DeepSeek sends high, --thinking minimal sends low.
Context windows
The context windows the gateway enforces: 1,048,576 for the 1M deployments, 262,144 for the Qwen 3.8,
Apertus and OCR families, and 131,072 for alias-fast and alias-eve. Output is capped at 16k
tokens so reasoning cannot consume the whole answer budget.
Which models appear
alias-* ids, plus two verified unaliased ids (deepseek-v4.1-flash-at-hzdr, Swift-Qwen3.8-27b).
The alias is the stable handle — JSC's guide says to use it so the model behind it can change. The rest
of the catalog is either a descriptive deployment name ("90 - Kimi-K3 1M on Juwels Booster"), an
OpenAI-name shim for the embedding endpoint (gpt-3.5-turbo, text-davinci-003), or a model that
cannot call tools (trustllm-*). Embeddings, rerankers, image/video/voice and the anonymizer are not
chat models and never appear.
The filter is an allowlist (alias-* plus those two ids), not a blocklist: of the 27 non-alias entries
the API returned today, 24 are noise — 16 duplicate "NN - …" deployment names, 3 tool-broken
trustllm, 3 embedding shims, an anonymizer, a whisper and two image generators. The price is that a
new unaliased chat model stays invisible until it is added here, in a release; the alternative admits
backends nobody has probed.
JSC throttles per account tier, so a long agent loop on a busy alias spends real time in backoff. Pi retries 429s on its own; nothing here needs configuring.
Precedence, and keeping this honest
GET /v1/models— live catalog,root,max_model_len,max_input_tokens.- The ceiling probe — for the models the API leaves blank.
- The model guide — prose and descriptions. It is a snapshot and can lag the live catalog.
A live answer that shrinks below 5 chat aliases is treated as a gateway glitch and ignored — the last good list is kept, silently. That is the one thing to know if JSC ever serves a genuinely smaller catalog.
Do not hardcode aliases in your own code; JSC says they come and go.
What was actually checked
npm run test:live drives the real CLI against the live API and asserts on the --mode json event
stream: the checklist from pi's custom-provider
guide. --list shows the groups without calling anything,
and a partial name runs one: npm run test:live overflow. npm test needs no token and no network —
it pins the pi-side mapping (picker names, the effort map under pi's level names, the offline catalog
shape) plus the one spawn behaviour that cannot be waited out live: pi ignoring the interrupt. The
shared rules (family patterns, effort vocabularies, live merge, token trimming) are pinned by
blablador-core, the catalog package this provider depends on; its own live contract test (npm run test:live there) is the check that runs straight
against /v1/models. CI runs the offline suites on every push, and a weekly job runs the live ones
when the token secret is set. Before a release, npm run test:all runs both halves. Last full live
run: 14/14.
| ordinary text, usage and cost | ✅ in=2482 out=20 cost=0 |
thinking level on alias-code |
✅ --thinking high passes, reasoning comes back (qwen got xhigh) |
| tool call, empty assistant text, tool result | ✅ turn stops with toolUse and no text |
image input on alias-code |
✅ answers "Red" for a red PNG |
| unicode round-trip | ✅ 🐍 Ω 你好 café é |
| context overflow | ✅ recognised, compaction_start then compaction_end |
| abort | ✅ exits on SIGINT, no hard kill needed |
| abort, when the interrupt is ignored | ✅ offline against a stub: SIGINT → SIGKILL, hard_kill=true |
| catalog | ✅ 21 models listed, thinking column right for qwen, gpt-oss and eve |
Deliberately not covered: malformed or partial streams (they need a mock server and live in pi-ai's
stream code, which this extension does not touch), cross-provider session handoff (needs a second
provider), and the offline/partial-catalog path — where /v1/models omits a root and the merge from
the table is the only thing that keeps a Qwen on its own effort map. The hard-kill test proves the
escalation mechanism with a 300ms grace; the shipped 30s value is a judgement call about 429
backoff, not a measured number. One failure mode belongs to pi rather than to this extension: when the
overflow cannot be compacted away — a single attachment larger than the window — pi exits 0 having
printed nothing.
Maintaining it
Re-measure a window. /v1/models leaves max_model_len blank for most aliases, so the bundled
windows are measured values, not guide numbers; re-measure them when a deployment changes.
Two things look like bugs and are not. The refresh merges root/max_model_len from the offline
table because the live catalog omits them, and it can only add, never overwrite — dropping that reverts
Qwen models to the generic effort map, which they answer with HTTP 400. Same for the family match: it
reads root, because alias-code is a Qwen whose id never says so. blablador-core's tests pin both, and
the live thinking group asks alias-code for --thinking high, which only succeeds if the family map
translated it to xhigh. The merge from the table is the half neither can reach: it only happens when
/v1/models leaves a root out, which needs a mock server (see "What was actually checked"). When a
new alias 400s on every
thinking level, it has an unfamiliar effort vocabulary: add a family pattern to FAMILY and an entry to
EFFORT in blablador-core — there is no runtime detection.
Shared with OpenCode. The catalog list, family/effort maps, live /v1/models merge and token
trimming live in blablador-core; blablador.ts is the thin pi adapter over it (refresh TTL, force/allowNetwork, last-good
retention, pi's off level name). Change the rules there, never here — opencode-blablador reads
the same rules and must not drift. Both packages live in this monorepo, so a root npm install
links the core (no symlinks); publish order when the rules change: blablador-core first, then
this package, pins bumped in the same commit.
Publishing. npm publish runs prepublishOnly — typecheck plus npm test — and refuses to ship
on a failure. It ships blablador-core as a real dependency, so publish the core first. CI runs the
same checks without a token, on purpose: the live suite costs real API calls,
so a Blablador-side change (a new effort vocabulary, a renamed alias, a shrunken catalog) is caught by
the weekly live job (or by someone running npm run test:live), not by a per-push robot. Tag the
commit you publish, not an earlier one.
Publishing an unscoped name puts blablador.ts in a public registry: readable by anyone, cached by
mirrors, and not really withdrawn by npm unpublish. The repo being private does not contain it.
MIT licensed.