pi-airpx

extensionmaintained

Log in to the airpx LLM proxy (https://airpx.cc) with an sk-proxy API key and auto-import its model catalog (prices, context windows, reasoning, vision) into the pi coding agent.

by · v0.4.0 · published 1w ago

$ pi install npm:pi-airpx
downloads/mo
0
stars
0
last push
1w ago
open issues
0

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

498 downloads · last 12 weeks (weekly)

README

pi-airpx

npm

Log in to the airpx LLM proxy with an sk-proxy-… key and auto-import its model catalog (prices, context windows, reasoning/vision flags) into the pi coding agent — instead of hand-maintaining hundreds of entries in models.json.

The airpx proxy is an OpenAI-/Anthropic-compatible gateway that routes one sk-proxy-… key to many upstream models (Claude, GPT, Gemini, Kimi, DeepSeek, GLM, Qwen, Grok, …) with pooled keys, fallback routing, and unified pricing. This plugin makes those models appear natively in pi's /model picker.

By default the plugin loads the proxy's main list: the anonymous GET https://airpx.cc/v1/models response (proxy_public_models, the ~25 super-current canonical models), with pricing and any global discount already applied. It skips alias rows and provider-group pseudo rows, and it does not load free/service models beyond what the main list already exposes.

Model ids and names are the proxy's, verbatim — the plugin keeps no rename or relabel tables, so pi's picker shows exactly what the airpx landing page lists. If a model is missing from pi, it is missing from proxy_public_models; publish it on the server rather than patching the client.

The list is fetched anonymously (no Authorization header) on purpose: a key's role is derived from its owner's user role, so a normal user key may return the full ~348-model catalog. The curated main list is only obtainable anonymously.

Install / run

# ad-hoc
pi -e ./extensions/index.ts
# then inside pi:
/login airpx        # paste your sk-proxy-... key

Or via env var (no /login needed):

AIRPX_API_KEY=sk-proxy-... pi -e ./extensions/index.ts

As an installed package — add to ~/.pi/agent/settings.jsonpackages:

{ "packages": ["npm:pi-airpx"] }

pi installs it from npm automatically. Then run /login airpx once and paste your sk-proxy-… key (get one at https://airpx.cc).

Get a key

Sign in at https://airpx.cc, create an sk-proxy-… API key, and use it with /login airpx or AIRPX_API_KEY. The same key works with any OpenAI-compatible client pointed at https://airpx.cc/v1.

Config

EnvDefaultMeaning
AIRPX_API_KEYsk-proxy-... key (alternative to /login airpx)
AIRPX_BASE_URLhttps://airpx.cc/v1proxy base URL (use http://127.0.0.1:18100/v1 for local dev)
AIRPX_ALL_MODELSset to 1 to fetch the FULL keyed catalog (still skips alias/pseudo rows)

How it works

  • async factory resolves a key (auth.json airpx entry → AIRPX_API_KEY), fetches /v1/models (anonymously by default), maps → pi.registerProvider("airpx", { models }).
  • /login airpx prompts for the key, validates it with an explicit keyed probe (a bad/mistyped key is rejected, not silently saved), then fetches the default catalog and re-registers the provider live (no /reload).
  • The on-disk cache (~/.pi/agent/airpx-catalog.json) is a last-good snapshot: a successful fetch replaces it with exactly the fresh response (so removed/re-priced models don't linger, and AIRPX_ALL_MODELS never permanently pollutes the default list); the cache is used only as a fallback when a fetch fails, so pi still boots offline.
  • First-run hint: with no key configured the models load but pi keeps them unavailable in /model until auth is set (documented pi behavior), so the plugin prints a one-line hint pointing you to /login airpx.

Field mapping (proxy → pi)

proxypi
idid
display_name (else id)name
reasoningreasoning
supports_visioninput: ["text","image"] else ["text"]
context_window (0 → 128000)contextWindow
max_output_tokens (0 → 16384)maxTokens
effective_pricing|pricing .inputcost.input (0 if absent)
…​.outputcost.output (0 if absent)
…​.cache_readcost.cacheRead (0 if absent)
…​.cache_writecost.cacheWrite (0 if absent)

A small hand-maintained _OVERRIDES table plus apiFromCatalog() add per-model tweaks. GPT-5.x rows default to api: "openai-responses" so Pi renders native Responses reasoning modules; explicit proxy fields recommended_api/api_format win once airpx exposes them. kimi-for-coding is renamed to Kimi K2.6 Coding.

Notes:

  • Prices are runtime-only — they come from the live /v1/models response and are never committed. Test fixtures deliberately use undiscounted pricing (not live effective_pricing) so they don't rot.
  • Only canonical ids are loaded — alias rows (those carrying alias_of) are skipped, as are provider-group pseudo rows (e.g. anthropic, detected by a known provider name or by having neither pricing nor supported_parameters, not by context_window === 0).

Tests

npm test
# = node --experimental-strip-types --test

Pure, offline unit tests of the mapper (src/map.ts) against a frozen fixture (test/fixtures/v1_models.sample.json) — no network.

Architecture

For the design rationale (module boundary, SOLID/DRY/KISS walkthrough, data flow, failure model, extension points) see docs/ARCHITECTURE.md.

TODO / open

  • Map thinkingLevelMap from supported_parameters (xhigh/max) per model.
  • Replace the transitional GPT-5.x fallback in apiFromCatalog() once the proxy exposes recommended_api/api_format per model.
  • Decide openai-completions vs anthropic-messages per model family (currently all via openai-completions; the proxy normalizes).