pi-inferx-provider
Pi coding-agent extension that registers InferX (https://model.inferx.net) as a model provider — live /v1/models discovery with a disk cache, a built-in public catalog fallback, and vLLM compatibility shims (system role, max_tokens, Qwen/DeepSeek/GLM thin
Package details
Install pi-inferx-provider from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-inferx-provider- Package
pi-inferx-provider- Version
0.1.2- Published
- Aug 11, 2026
- Downloads
- 473/mo · 61/wk
- Author
- nrynss
- License
- MIT
- Types
- extension
- Size
- 46.1 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-inferx-provider
A pi coding-agent
extension that registers InferX (https://model.inferx.net) as a model provider.
InferX is a serverless GPU inference platform exposing an OpenAI-compatible, vLLM-backed
endpoint (/endpoints/v1). This extension discovers its models and applies the small set
of compatibility shims its vLLM serving stack needs, so InferX models "just work" in pi.
Features
- Provider registration — registers the
inferxprovider withhttps://model.inferx.net/endpoints/v1as base URL. - Live model discovery — when
INFERX_API_KEYis set, models are fetched from/v1/models(with a 5-minute on-disk cache keyed by base URL + key hash, so the round-trip is skipped on subsequent launches). - Self-refreshing public catalog — the endpoint catalog page
(
https://model.inferx.net/catalog/endpoints) is publicly readable, so the provider scrapes it at startup (24-hour on-disk cache) to pick up new endpoints as InferX publishes them — even before the API key is set or when/v1/modelsis unreachable. - Embedded catalog floor — the current catalog is also embedded in the package, so there is always a model list even if both live sources fail.
- Model metadata — context windows from the catalog (DeepSeek V4 Flash: 1M, GLM 5.2 /
Qwen3.6 / Agents-A1 / Ornith: ~260K, …), reasoning enabled where the model supports
thinking, off for the
-no-thinkingvariant. - vLLM thinking control — DeepSeek and GLM models use
chat_template_kwargs.thinking.enabled(chat-template), verified against the live endpoint: both families separate reasoning (streamed asdelta.reasoning/reasoning, which pi reads into the thinking block) with clean content. Qwen endpoints are registered non-reasoning — live testing showed they render their chain of thought into plaincontentwith an empty reasoning field, so pi does not wait on thinking blocks that never arrive. systemrole instead ofdeveloper— vLLM chat templates generally reject unknown roles; pi'sdevelopersystem prompt is downgraded viacompat.supportsDeveloperRole: false.max_tokensfield — vLLM usesmax_tokens, notmax_completion_tokens(compat.maxTokensField).- Transient-error retry — wraps the provider stream with a 1 s / 3 s / 8 s backoff
ladder for connect-time blips (cold-start 5xx, gateway resets), and requests
provider-level retries so
Retry-Afteron 429 is honoured via pi-ai. Bad-key errors (401/403) are never retried.
All behaviour is scoped to the inferx provider; other providers are unaffected.
Requirements
- pi (the
@earendil-works/pi-coding-agentCLI), v0.80 or newer. - An InferX API key in the
INFERX_API_KEYenvironment variable (required to make requests; models are listed from the public catalog even without it). - Node 18+ (for global
fetch).
Install
pi install npm:pi-inferx-provider
Then set your key and select a model:
export INFERX_API_KEY="ix-..." # PowerShell: $env:INFERX_API_KEY = "ix-..."
pi
# /model -> pick an inferx-* model
Add the export line to your shell config (~/.zshrc, ~/.bashrc) to keep the key across
sessions.
Other useful commands:
pi list # show installed packages
pi update npm:pi-inferx-provider # update to the latest release
pi remove npm:pi-inferx-provider # uninstall
pi install -l npm:pi-inferx-provider # install into this project only (.pi/npm/)
Alternatively, list the package in the packages array of your pi settings.json
(~/.pi/agent/settings.json) and pi will install it on next start:
{
"packages": [
"npm:pi-inferx-provider"
]
}
If you previously had a local
inferx.tsin~/.pi/agent/extensions/, delete it after installing this package to avoid registering the provider twice.
Configuration
| Variable | Required | Description |
|---|---|---|
INFERX_API_KEY |
yes* | Your InferX API key. *Required to make requests; without it the models are still listed from the embedded catalog but every request fails with an auth error. |
INFERX_DEBUG |
no | Set to true for per-request debug logging and a metrics summary on exit. |
INFERX_PROVIDER_RETRIES |
no | Provider-level retries for InferX traffic (default 2). These are what honour a Retry-After header on 429. Set to 0 to disable. |
The base URL is https://model.inferx.net/endpoints/v1.
Models
As of the embedded catalog, the provider registers (thinking flags reflect live verification against the endpoint):
| Model ID | Context | Thinking |
|---|---|---|
deepseek-v4-flash |
1,000,000 | yes* |
deepseek-v4-flash-0731 |
1,048,576 | yes* |
glm-52 |
260,000 | yes |
Agents-A1 |
262,000 | no |
Devstral-2-123B-Instruct-2512-int4-AutoRound |
128,000 | no |
Ornith-1.0-35B-FP8 |
262,000 | no |
Qwen3-Coder-Next-FP8 |
256,144 | no |
Qwen3.6-35B-A3B-FP8 |
262,000 | no |
Qwen3.6-35B-A3B-fp8-no-thinking |
262,000 | no |
* DeepSeek separated reasoning correctly in non-streaming tests; its streaming key
could not be re-confirmed (the endpoint was at capacity), but pi reads both
reasoning_content and reasoning stream keys, so either shape works.
Notes from live testing:
glm-52anddeepseek-v4-flashemit thinking as a separate reasoning field withchat_template_kwargs.thinking(verified). GLM also thinks by default even without kwargs;thinking: falsedisables the reasoning field but the model then writes its analysis into content — leave thinking on.Qwen3.6-35B-A3B-FP8(the "thinking" variant) always writes its chain of thought intocontent— with and withoutenable_thinkingkwargs in any shape — so it is registered non-reasoning. For clean short answers preferQwen3.6-35B-A3B-fp8-no-thinking.Qwen3-Coder-Next-FP8ignores thinking kwargs entirely (clean direct answers, empty reasoning field).- The endpoint accepts the
developerrole; the provider still downgrades tosystemfor safety across vLLM templates.
When INFERX_API_KEY is set, live /v1/models discovery replaces this table with the
server's authoritative list; ids not in the catalog keep their discovered metadata and
get conservative defaults (text input, no reasoning). New endpoints published on the
catalog page are picked up automatically at startup (see
Model resolution); to force a re-scrape sooner, delete
~/.cache/pi-inferx-provider/catalog.json.
Notes & limitations
- Thinking controls are chosen to match vLLM serving conventions for each model family.
If a model's thinking toggle differs on the InferX side, override the model in
~/.pi/agent/models.json(e.g.thinkingFormat,chatTemplateKwargs). - The transient retry handles brief gateway blips (seconds), not sustained outages. For a sustained outage, wait or switch models.
Development
pnpm install
pnpm run typecheck
To try a local change without installing the package, load the source directly — -ne
disables other extensions so nothing else interferes:
pi -ne -e ./src/index.ts --provider inferx --model deepseek-v4-flash
Note that an installed copy of this package registers the same provider name as a local
one. Remove the installed copy (pi remove npm:pi-inferx-provider) before testing locally.
Publishing
Releases follow Semantic Versioning. Before publishing a new version:
Bump
versioninpackage.jsonand add an entry toCHANGELOG.mdunder the new version heading.Run the checks:
pnpm run typecheck pnpm pack --dry-run # confirm the tarball contains only src/, README, CHANGELOG, LICENSEPublish (the tarball is what pi installs; the
pi.extensionsfield points pi at./src/index.ts):npm publish # or: pnpm run publishTag the release in git:
git tag v0.1.0 && git push origin main --tags
First-time publishing requires npm authentication:
npm login
Users then install with pi install npm:pi-inferx-provider (see Install).
Internals
The package exports one default function — pi's extension entry point. Everything below lives inside it; nothing else is importable.
Model resolution
Three tiers, each falling through to the next on failure (registration never hard-fails):
- Live API — if
INFERX_API_KEYis set, try the on-disk cache ($XDG_CACHE_HOME/pi-inferx-provider/models.json, mode 600, 5-minute TTL, invalidated by base URL + key hash), thenGET /v1/modelswith the key. Success is authoritative and replaces the list below. - Public catalog scrape — fetch the unauthored endpoint catalog page
(
https://model.inferx.net/catalog/endpoints) and parse the model table (id, context length, upstream provider). Cached for 24 hours in$XDG_CACHE_HOME/pi-inferx-provider/catalog.json. This is what keeps newly published endpoints visible without waiting for a package update. - Embedded catalog — the table in this source file, so there is always a floor even fully offline.
Models found by tiers 1–2 that are not in the embedded catalog get conservative
metadata: context from the live source, maxTokens 16384, and reasoning enabled only
when the family is known to think (qwen/deepseek/glm) and the id does not say
no-thinking.
Retry layers
Two independent layers covering different failures:
| Layer | Handles | Timing |
|---|---|---|
pi-ai retryProviderRequest |
408, 409, 429, 5xx | Retry-After when sent, else exponential backoff with jitter, capped by retry.provider.maxRetryDelayMs (60 s) |
| This extension's stream wrapper | Connect-time transient errors | Fixed ladder: 1 s / 3 s / 8 s |
pi ships retry.provider.maxRetries: 0, which leaves the first layer inert, so the
extension requests retries for InferX traffic (INFERX_PROVIDER_RETRIES) to get
Retry-After honoured. The stream wrapper only retries when the first event is a
transient error (safe: a connect-time failure streams nothing); 401/403 auth errors are
never retried.
Thinking control
Verified against the live endpoint:
deepseek/glmmodels →thinkingFormat: "chat-template"withchatTemplateKwargs: { thinking: { $var: "thinking.enabled" } }, sendingchat_template_kwargs: { thinking: true|false }. Both families stream reasoning underdelta.reasoning(pi readsreasoning_content/reasoning/reasoning_text), so thinking lands in pi's thinking block.- Qwen endpoints render thinking into plain content, so their presets are
reasoning: false(no kwargs sent). Theqwen-chat-templateformat (chat_template_kwargs.enable_thinking) is kept incompatForfor future Qwen deployments that separate reasoning, but no registered model uses it today. - Unknown ids discovered later default to non-reasoning, except new deepseek/glm ids
(families verified to separate reasoning), unless the id says
no-thinking.