https://github.com/bntvllnt/pi-gateway
OpenAI-compatible local API on top of pi.dev — LiteLLM-shape stateless protocol translator. Standalone CLI + pi extension.
https://github.com/bntvllnt/pi-gateway
ai anthropic chat-completions claude cline codex cursor gateway gpt librechat litellm llm openai openai-api openai-compatible openrouter openwebui pi-dev pi-extension typescript
Last synced: 16 days ago
JSON representation
OpenAI-compatible local API on top of pi.dev — LiteLLM-shape stateless protocol translator. Standalone CLI + pi extension.
- Host: GitHub
- URL: https://github.com/bntvllnt/pi-gateway
- Owner: bntvllnt
- License: mit
- Created: 2026-05-27T14:59:43.000Z (16 days ago)
- Default Branch: main
- Last Pushed: 2026-05-27T17:43:52.000Z (16 days ago)
- Last Synced: 2026-05-27T19:24:57.922Z (16 days ago)
- Topics: ai, anthropic, chat-completions, claude, cline, codex, cursor, gateway, gpt, librechat, litellm, llm, openai, openai-api, openai-compatible, openrouter, openwebui, pi-dev, pi-extension, typescript
- Language: TypeScript
- Homepage: https://github.com/bntvllnt/pi-gateway
- Size: 162 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# pi-gateway
[](https://www.npmjs.com/package/pi-gateway)
[](https://github.com/bntvllnt/pi-gateway/actions/workflows/ci.yml)
[](LICENSE)
> **OpenAI-compatible local API on top of [pi.dev](https://github.com/badlogic/pi-mono).**
> LiteLLM-shape stateless protocol translator. Re-exposes every model `pi` can reach (Anthropic, OpenAI, Google, Mistral, Bedrock, Vertex, **plus OAuth subscriptions** like Claude Pro / ChatGPT Codex / GitHub Copilot / Gemini CLI) through `POST /v1/chat/completions` + `GET /v1/models`, so Open WebUI / LibreChat / Cursor / Continue.dev / Cline can use them without re-entering credentials.
## Install
Two install paths from one package:
### 1. Standalone CLI (no pi required)
```bash
pnpm dlx pi-gateway --port 4000
# or globally
npm i -g pi-gateway && pi-gateway --port 4000
```
Runs in the foreground; Ctrl+C to stop.
### 2. Pi extension (recommended if you already use pi)
Add to `~/.pi/agent/settings.json`:
```jsonc
{
"packages": [
"git:git@github.com:bntvllnt/pi-gateway"
]
}
```
Restart `pi`. Then inside pi:
```
/gateway:start # spawn the daemon (detached, survives pi exit)
/gateway:status # show url + pid + model count
/gateway:stop # SIGTERM the daemon
```
## Wire your client
Point any OpenAI-compatible client at `http://127.0.0.1:4000/v1`. The API key field accepts any non-empty string on loopback.
Open WebUI
Settings → Connections → OpenAI API:
- **Base URL:** `http://127.0.0.1:4000/v1`
- **API key:** `pi-gateway` (any non-empty string)
Model dropdown populates from `GET /v1/models`.
LibreChat
```yaml
# librechat.yaml
endpoints:
custom:
- name: "pi-gateway"
baseURL: "http://127.0.0.1:4000/v1"
apiKey: "pi-gateway"
models:
fetch: true
```
Cursor
Settings → Models → "Override OpenAI Base URL" → `http://127.0.0.1:4000/v1`.
Cursor may need a bare model id alias (it can be strict about slashes). Add aliases under Settings → Models → Add Model, e.g. `claude-sonnet-4-5` → `anthropic/claude-sonnet-4-5`.
Continue.dev
```json
{
"models": [{
"provider": "openai",
"apiBase": "http://127.0.0.1:4000/v1",
"apiKey": "pi-gateway",
"model": "anthropic/claude-sonnet-4-5",
"title": "Claude via pi-gateway"
}]
}
```
Cline (VS Code)
API Provider → OpenAI Compatible.
- **Base URL:** `http://127.0.0.1:4000/v1`
- **API key:** any non-empty string
- **Model:** `provider/model-id` (e.g., `openai/gpt-4o`, `anthropic/claude-sonnet-4-5`)
## Endpoints
| Method | Path | Notes |
|--------|------|-------|
| `GET` | `/healthz` | Liveness; returns `{ ok, uptimeMs }` |
| `GET` | `/v1/models` | OpenAI list payload; `id: "provider/model-id"` |
| `POST` | `/v1/chat/completions` | Stream + non-stream Chat Completions |
Out of scope for v1: `/v1/responses`, `/v1/embeddings`, `/v1/images/generations`, `/v1/audio/*`, `/v1/messages` (Anthropic Messages format).
## What pi-gateway is — and isn't
**Is:** a stateless HTTP frontend. Validates → resolves model → resolves auth → calls `pi-ai.complete()` or `pi-ai.stream()` → translates back into OpenAI Chat Completions shape (JSON or SSE).
**Is not:**
- ❌ A pi agent. No pi `AgentSession` is created.
- ❌ A prompt injector. What the client sends is what pi-ai sees — no system prompt, no tools, no skills are added.
- ❌ A tool runner. `tools` and `tool_choice` are forwarded; the client executes and posts `role: "tool"` results.
- ❌ A conversation store. Each `POST /v1/chat/completions` is independent.
## CLI flags
| Flag | Default | Notes |
|------|---------|-------|
| `--port N` | `4000` | `0` = OS-assigned |
| `--bind HOST` | `127.0.0.1` | Non-loopback requires `apiKey` in config file |
| `--config PATH` | — | Extra JSON layered after `~/.pi/agent/gateway.json` |
| `--auth-dir PATH` | `~/.pi/agent` | Where to find `auth.json` and `models.json` |
| `--allow-origin ORIGIN` | empty | Repeatable; `"*"` = any |
| `--log-level LEVEL` | `info` | `debug` / `info` / `warn` / `error` |
| `--model-allowlist ID` | — | Repeatable; matches `provider/model-id` or bare id |
| `--model-denylist ID` | — | Repeatable |
| `--expose-oauth-subscriptions` | on loopback only | Expose Claude Pro / Codex / Copilot on non-loopback too |
| `--require-key-on-loopback` | off | Force bearer auth even on `127.0.0.1` |
| `--version` | — | Print version + exit |
> **No `--api-key` flag.** Argv leaks via `/proc//cmdline` + `ps aux`. Set the key in `~/.pi/agent/gateway.json` (`{ "apiKey": "..." }`) or via `PI_GATEWAY_API_KEY` env var.
Subcommands:
```bash
pi-gateway models # Print available models then exit
pi-gateway --help
pi-gateway --version
```
## Security defaults
- **Default bind: `127.0.0.1`.** `server.address()` is asserted after `listening` so a refactor can't silently bind `0.0.0.0`.
- **Non-loopback bind requires `apiKey` in the config file.** CLI flag is refused.
- **PID lockfile** at `~/.pi/agent/gateway.pid` via atomic `O_CREAT|O_EXCL`. Single instance enforced; stale files cleaned automatically.
- **OAuth subscriptions default-allow on loopback** (so Claude Pro / Codex work from Open WebUI), **default-deny on non-loopback** unless `--expose-oauth-subscriptions`.
- **Access log** redacts everything outside a hardcoded allowlist (`content-type`, `content-length`, `user-agent`, `accept`, `accept-encoding`, `host`). No `authorization` / token / key headers in logs.
## Programmatic SDK
```ts
import { startServer, stopServer } from "pi-gateway";
import { DEFAULT_CONFIG } from "pi-gateway/config"; // future export
const handle = await startServer({
config: { ...DEFAULT_CONFIG, port: 0, bindAddress: "127.0.0.1" },
});
const url = `http://${handle.address.address}:${handle.address.port}`;
const r = await fetch(`${url}/v1/chat/completions`, { method: "POST", /* ... */ });
await stopServer(handle);
```
Used by the test suite to bind real listeners on `127.0.0.1:0` without invoking the binary.
## Development
```bash
git clone https://github.com/bntvllnt/pi-gateway.git
cd pi-gateway
pnpm install
pnpm run check # lint + typecheck + build + smoke + e2e + contract
```
Quality gates:
| Gate | Command |
|------|---------|
| Lint | `pnpm run lint` |
| Typecheck | `pnpm run typecheck` |
| Build | `pnpm run build` |
| Smoke | `node tests/smoke.mjs` |
| E2E | `node tests/extension-e2e.mjs` |
| **Contract** | `node tests/contract.mjs` — ajv field-by-field validation against the pinned OpenAPI doc + Chat Completions schemas |
All gates run on `pre-commit` and CI.
## Schemas
`schemas/openresponses.openapi.json` pins https://www.openresponses.org/openapi/openapi.json (OpenAI API v2.3.0, 108 component schemas) in-repo to avoid drift. The contract test loads this file and rejects any response that doesn't match.
## License
[MIT](LICENSE) © [bntvllnt](https://github.com/bntvllnt)
## See also
- [llms.txt](llms.txt) — short hub for AI consumption
- [llms-full.txt](llms-full.txt) — full reference for AI consumption
- [CLAUDE.md](CLAUDE.md) — project rules + contract guarantees
- [pi.dev](https://github.com/badlogic/pi-mono) — the underlying agent runtime
- [pi-claude-code](https://github.com/bntvllnt/pi-claude-code), [pi-git-worktrees](https://github.com/bntvllnt/pi-git-worktrees) — sibling pi extensions