mirror of
https://github.com/dtzp555-max/olp.git
synced 2026-07-21 21:15:10 +00:00
cold-audit catch from 2026-05-24 (round 3) Round-3 P3 docs batch. Two unrelated docs items grouped per IDR cleanup convention (D19/D20/D25 precedent). **F7** — README Environment Variables table drift. Pre-D30 table documented `OLP_HOME` and `OLP_LOG_LEVEL` — neither is read by any code in the repo. The table missed `OLP_CLAUDE_BIN`, `OLP_CODEX_BIN`, `OLP_VIBE_BIN` which the 3 provider plugins DO read to override the path to each provider's CLI binary. `grep -rn "process\.env\.OLP_" server.mjs lib/` returns exactly 4 reads: - `OLP_PORT` (server.mjs:49) - `OLP_CLAUDE_BIN` (anthropic.mjs:69) - `OLP_CODEX_BIN` (codex.mjs:143) - `OLP_VIBE_BIN` (mistral.mjs:240) Fix: README env-vars table now lists exactly these 4 active vars with their actual defaults. `OLP_HOME` + `OLP_LOG_LEVEL` moved to a "📋 Planned (Phase 2)" callout block beneath the table, with honest explanations linking to the actual code state (`loadFallbackConfigSync` hardcodes the config path; `logEvent` writes unconditionally). **F20** — Author docs/openai-spec-pin.md v0.1 baseline. ALIGNMENT.md Authority 2 + Annual Alignment Audit § Scope both referenced `docs/openai-spec-pin.md` as the artifact the annual audit diffs against. Pre-D30 the file didn't exist (D20 marked it 📋 Planned). This left the entry-surface audit without a diff baseline — v0.1 ships with no provable "the OpenAI spec was THIS on the day OLP implemented its entry surface" anchor. Fix: author a 175-line minimal v0.1 baseline. Every field claim is verified against source (openai-to-ir.mjs + ir-to-openai.mjs + server.mjs). Structure: - POST /v1/chat/completions: 14 supported request fields (model, messages + 6 message-level subfields, stream, temperature, max_tokens, top_p, stop, tools, tool_choice, response_format) + 11 NOT-yet-supported fields (n, seed, frequency_penalty, presence_penalty, logit_bias, logprobs, top_logprobs, user, service_tier, parallel_tool_calls, stream_options) - Response shapes: chat.completion (non-stream), chat.completion.chunk (streaming) — verified against irResponseToOpenAINonStream and irChunkToOpenAISSE - `finish_reason` enum: verified against OPENAI_FINISH_REASON_ENUM constant in ir-to-openai.mjs (post-D19 + D26) - Error response shape: HTTP 4xx/5xx + `{error: {message, type}}` — verified against `sendError` in server.mjs - GET /v1/models: verified against handleModels (post-D18 + D27 F15) - Streaming SSE semantics: framing, terminator, post-D26 F19 truncation marker The pin also documents the v0.1 → v1.0 forward-looking expansion plan: the "NOT yet supported" fields are explicit candidates for v1.0+ implementation via openai-to-ir.mjs amendments + ADR 0003 updates. ALIGNMENT.md + README Implementation status table both flip the marker from 📋 Planned to ✅ Shipped (D30) with the 2026-05-24 timestamp. Changes (3 files, +180 / -8): - ALIGNMENT.md +2/-2 (2 markers updated: Authority 2 + Annual Audit) - README.md +11/-6 (env-vars table delta + status table marker flip + Planned callout for OLP_HOME/OLP_LOG_LEVEL) - docs/openai-spec-pin.md (new, 175 lines) Tests: 400/400 unchanged — pure docs change. Authority: - F7 → process.env reads verified by direct grep - F20 → OpenAI Chat Completions spec https://platform.openai.com/docs/api-reference/chat/create https://platform.openai.com/docs/api-reference/chat/streaming https://platform.openai.com/docs/api-reference/chat/object https://platform.openai.com/docs/api-reference/models/list - F20 internal source-of-truth: openai-to-ir.mjs + ir-to-openai.mjs + server.mjs (all field claims traced) - ALIGNMENT.md § Authority 2 + § Annual Alignment Audit - CC 开发铁律 v1.6 § 10.x — Round-3 Cold Audit caught both items Reviewer (Iron Rule v1.6 § 10.x Mode A, fresh-context opus, independent of drafter): APPROVE. Independent verification: - Grep confirmed exactly 4 process.env.OLP_* reads — matches new env vars table - Each new var's default value verified against the plugin code (anthropic.mjs:69 → 'claude'; codex.mjs:143 → 'codex'; mistral.mjs:240 → 'vibe') - All 14 spec-pin supported request fields traced to openAIToIR line references (model L129, messages L45-89, stream L141, temperature L160, max_tokens L152, top_p L168, stop L176, tools L183, tool_choice L187, response_format L191) - All 11 NOT-supported fields confirmed absent via grep - Response shape claims (chat.completion + chat.completion.chunk + /v1/models) all match source code line-by-line - ALIGNMENT.md markers — pure markup flip, no rule changes - 400/400 tests pass 3 non-blocking suggestions noted (function_call finish_reason caveat; README slug rendering; ADR 0003 cross-link in spec-pin) — all cosmetic, not folded. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
229 lines
14 KiB
Markdown
229 lines
14 KiB
Markdown
# OLP — Open LLM Proxy
|
||
|
||
A personal- and family-scale multi-provider LLM proxy. One HTTP endpoint, many subscriptions behind it, automatic routing, automatic fallback, content-addressed caching — so your IDEs and family clients keep working as long as *any* of your subscriptions has quota left.
|
||
|
||
> **Status:** v0.1 — bootstrap. Most of this README is a skeleton; sections marked _placeholder_ land alongside the relevant phase of work (see [phase plan](#phase-plan)).
|
||
|
||
---
|
||
|
||
## Why OLP
|
||
|
||
On 2026-05-14, Anthropic announced (effective 2026-06-15) that `claude -p`, the Agent SDK, and third-party agent traffic move out of the Pro/Max subscription pool into a separate fixed monthly Agent SDK Credit pool. [OCP](https://github.com/dtzp555-max/ocp), OLP's predecessor, was a proxy around a single CLI — its core assumption was *"subscription = unlimited within rate limits"*. That assumption breaks for Anthropic on the effective date.
|
||
|
||
The structural response is to stop relying on one provider's subscription terms remaining favourable. OLP spreads risk across multiple providers whose subscriptions still include CLI/programmatic use, routes intelligently between them, and caches aggressively so every request that does spawn a CLI counts.
|
||
|
||
OLP is **not**: a commercial multi-tenant SaaS; an enterprise gateway competing with LiteLLM / OpenCode / CLIProxyAPI on breadth; a model-capability router ("route to the smartest model" — you pick the model); a conversation-state store (your client handles that).
|
||
|
||
See [`ALIGNMENT.md`](./ALIGNMENT.md) for OLP's constitution and [`docs/adr/`](./docs/adr/) for the founding ADRs.
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
|
||
_placeholder — lands with Phase 1._
|
||
|
||
Anticipated shape:
|
||
|
||
```bash
|
||
# install
|
||
npm install -g @dtzp555-max/olp
|
||
|
||
# run setup (writes ~/.olp/config.json, asks which providers to enable)
|
||
olp setup
|
||
|
||
# start the proxy (default port 3456 — same as OCP if you migrate)
|
||
olp start
|
||
|
||
# point your IDE at http://localhost:3456/v1/chat/completions with the OLP API key from `olp keys list`.
|
||
```
|
||
|
||
---
|
||
|
||
## Supported Providers
|
||
|
||
Source of truth: [`models-registry.json`](./models-registry.json). This table is regenerated from the registry per the [`release_kit`](./CLAUDE.md) overlay; do not edit it out of sync.
|
||
|
||
OLP distinguishes **Candidate Providers** (declared as intended, not yet pinned) from **Enabled Providers** (authority pin filled + plugin landed + Phase audit passed). The v0.1 founding commit ships **zero Enabled Providers** — enablement is a Phase audit deliverable, not a bootstrap claim. See [`ALIGNMENT.md` § Provider Inventory](./ALIGNMENT.md) for the transition gate.
|
||
|
||
### Candidate Providers
|
||
|
||
| Provider key | CLI | Subscription / auth | Anticipated Tier | Anticipated Phase |
|
||
|---|---|---|---|---|
|
||
| `anthropic` | `claude -p` | Pro / Max OAuth (pre-2026-06-15); Agent SDK Credit pool after | D (re-eval post-2026-06-15) | Phase 1 |
|
||
| `openai` | `codex exec --json` | ChatGPT Pro OAuth or API key | D | Phase 2 |
|
||
| `mistral` | `vibe --prompt --output json` | Le Chat Pro API key | D | Phase 3 |
|
||
| `grok` | `grok -p --output-format streaming-json` | xAI Build `xai-...` API key | C | Phase 8+ |
|
||
| `kimi` | `kimi -p --output-format stream-json` | Moonshot Kimi API key | C | Phase 8+ |
|
||
| `minimax` | TBD | MiniMax Token Plan (¥29+/mo) | B | Phase 8+ |
|
||
| `glm` | TBD | Zhipu Coding Plan ($10+/mo) | B | Phase 8+ |
|
||
| `qwen` | TBD | Alibaba Coding Plan ($50/mo) | B | Phase 8+ |
|
||
|
||
**Risk tier guide.** D = permissive / safe (eligible for default-enabled); C = tightening signal, no enforcement history (opt-in); B = service-level key revocation risk (opt-in + consent); A = excluded by default (cannot be opt-in enabled). Tier B providers prompt for explicit consent on first enable and record consent in `~/.olp/config.json`. See [`ALIGNMENT.md` § Risk Tier Framework](./ALIGNMENT.md#risk-tier-framework).
|
||
|
||
**Excluded by default (Tier A — evidence-backed, pending primary-source pin).** Google Antigravity. See [ADR 0006](./docs/adr/0006-provider-inclusion.md) for the named-prohibition + no-cost-advantage + reinstatement-friction rationale, and for the primary-source pinning follow-up that may force a Tier reconsideration if the Google FAQ language cannot be sourced within 90 days of 2026-05-23.
|
||
|
||
---
|
||
|
||
## Configuration
|
||
|
||
_placeholder — full configuration reference lands with Phase 4 (fallback engine)._
|
||
|
||
OLP reads its config from `~/.olp/config.json`. The minimum useful shape:
|
||
|
||
```json
|
||
{
|
||
"routing": {
|
||
"chains": {
|
||
"<requested-model>": [
|
||
{ "provider": "<key>", "model": "<provider-model-id>" },
|
||
{ "provider": "<key>", "model": "<provider-model-id>" }
|
||
]
|
||
},
|
||
"soft_triggers": {
|
||
"<provider-key>": { "<trigger>": <threshold> }
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
> **Note:** `routing.soft_triggers` thresholds are parsed and stored but have **no runtime effect at v0.1** — the quota polling path (`quotaStatus()` per hop) is deferred to v1.x per [ADR 0004 Amendment 2](./docs/adr/0004-fallback-engine.md#amendment-2--2026-05-24-soft-triggers-deferred-to-v1x-d22). The evaluation logic exists and is tested; only the production data ingestion path is deferred.
|
||
|
||
Trigger types, fallback safety, idempotency rules, and the full example config land here when Phase 4 ships. See [ADR 0004 (Fallback Engine Semantics & Safety)](./docs/adr/0004-fallback-engine.md) for the design.
|
||
|
||
---
|
||
|
||
## API Endpoints
|
||
|
||
_placeholder — full table lands as each endpoint lands._
|
||
|
||
| Endpoint | Method | Phase | Description |
|
||
|---|---|---|---|
|
||
| `/v1/chat/completions` | POST | 1 | OpenAI-compatible Chat Completions entry. Internally normalized to IR, dispatched to a provider plugin, response shape converted back. |
|
||
| `/v1/models` | GET | 1 | Lists models from `models-registry.json`. |
|
||
| `/health` | GET | 1 | Per-provider health snapshot (owner-only). |
|
||
| `/cache/stats` | GET | 5 | Cache hit rate, by-provider breakdown. |
|
||
| `/v0/management/quota` | GET | 6 | Per-provider quota / credit pool status (best-effort). |
|
||
| `/dashboard` | GET | 6 | Owner-only dashboard (localhost-bound by default). |
|
||
|
||
---
|
||
|
||
## Environment Variables
|
||
|
||
_placeholder — full table lands per-phase as variables are introduced._
|
||
|
||
| Variable | Default | Description |
|
||
|---|---|---|
|
||
| `OLP_PORT` | `3456` | HTTP listener port. |
|
||
| `OLP_CLAUDE_BIN` | `claude` (from PATH) | Override path to the `claude` binary (Anthropic provider). Useful when multiple `claude` installs are present. |
|
||
| `OLP_CODEX_BIN` | `codex` (from PATH) | Override path to the `codex` binary (OpenAI provider). |
|
||
| `OLP_VIBE_BIN` | `vibe` (from PATH) | Override path to the `vibe` binary (Mistral provider). |
|
||
|
||
> **📋 Planned (Phase 2) — not yet read by the codebase:**
|
||
> - `OLP_HOME` (`~/.olp`) — Config, providers, keys, cache, logs root. Currently hardcoded to `~/.olp/config.json` in `loadFallbackConfigSync`; the env override path is a Phase 2 config-layer deliverable.
|
||
> - `OLP_LOG_LEVEL` (`info`) — Log level filter (`error`/`warn`/`info`/`debug`). `logEvent` currently writes unconditionally; level filtering is a Phase 2 observability deliverable.
|
||
|
||
Further variables (per-provider auth path overrides, cache size limits, fallback-engine knobs) land with the relevant phase. See also the [Implementation status](#implementation-status-as-of-2026-05-24) table.
|
||
|
||
---
|
||
|
||
## Response Headers
|
||
|
||
Every response served through OLP carries:
|
||
|
||
- `X-OLP-Provider-Used: <provider-key>` — which provider's plugin served the request.
|
||
- `X-OLP-Model-Used: <model-id>` — which model the served provider used.
|
||
- `X-OLP-Fallback-Hops: <n>` — number of fallback hops (`0` if served by the primary chain entry).
|
||
- `X-OLP-Cache: hit | miss | bypass` — cache layer outcome.
|
||
- `X-OLP-Latency-Ms: <ms>` — end-to-end latency observed at the proxy.
|
||
|
||
If a fallback chain is exhausted, `X-OLP-Fallback-Exhausted` lists the tried providers in order.
|
||
|
||
---
|
||
|
||
## Implementation status (as of 2026-05-24)
|
||
|
||
Phase 1 is in progress. This table reflects what is currently shipped vs. what is designed for later phases.
|
||
|
||
| File / artifact | Status | Notes |
|
||
|---|---|---|
|
||
| `server.mjs` | ✅ Shipped | HTTP listener + dispatcher |
|
||
| `lib/ir/` | ✅ Shipped | IR definition + serializers (ADR 0003) |
|
||
| `lib/providers/anthropic.mjs` | ✅ Shipped | `claude -p` spawn-binary plugin |
|
||
| `lib/providers/codex.mjs` | ✅ Shipped | `codex exec --json` plugin |
|
||
| `lib/providers/mistral.mjs` | ✅ Shipped | `vibe --prompt` plugin |
|
||
| `lib/cache/keys.mjs` | ✅ Shipped | Content-addressed key computation |
|
||
| `lib/cache/store.mjs` | ✅ Shipped | In-memory Map (file-backed layout: 📋 Phase 2 storage adapter) |
|
||
| `lib/fallback/engine.mjs` | ✅ Shipped | Trigger evaluation + chain advancement (ADR 0004) |
|
||
| Soft trigger data path (`quotaStatus()` polling) | 📋 Planned (v1.x) | Evaluation logic shipped + tested; data ingestion deferred per ADR 0004 Amendment 2 |
|
||
| `models-registry.json` | ✅ Shipped | SPOT for `(provider, model)` metadata |
|
||
| `test-features.mjs` | ✅ Shipped | Comprehensive test suite covering IR, cache, fallback, and integration paths (CI: `test.yml`) |
|
||
| `lib/keys.mjs` | 📋 Planned (Phase 2) | Multi-key auth, per-key namespacing, audit log |
|
||
| `dashboard.html` | 📋 Planned (Phase 6) | Owner-only multi-provider dashboard |
|
||
| `docs/provider-caveats.md` | 📋 Planned (Phase 3+) | Lossy-translation reference; for now documented inline in each plugin header |
|
||
| `docs/openai-spec-pin.md` | ✅ Shipped (D30) | OpenAI spec snapshot for annual audit; v0.1 baseline pinned 2026-05-24 |
|
||
| `docs/alignment-audits/` | 📋 Planned | Output directory for annual alignment audits (first audit: 2027-05-14) |
|
||
| `scripts/migrate-from-ocp.mjs` | 📋 Planned (Phase 7) | OCP → OLP migration tool |
|
||
| `setup.mjs` | 📋 Planned | Setup wizard / initial config |
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
OLP is a Node.js (ESM, `.mjs`) HTTP proxy with no build step and minimal dependencies. The high-level shape:
|
||
|
||
- **Entry surface** — `server.mjs` handles `/v1/chat/completions` and the administrative endpoints. Governed by OpenAI's `/v1/chat/completions` specification as the wire authority. See [`ALIGNMENT.md` § Authorities](./ALIGNMENT.md#authorities).
|
||
- **Intermediate Representation (IR)** — `lib/ir/` normalizes between the entry surface and provider-native shapes. The IR is the lingua franca; any extension is an [ADR 0003](./docs/adr/0003-intermediate-representation.md) amendment.
|
||
- **Provider plugins** — `lib/providers/<name>.mjs`. Each plugin implements the contract in [ADR 0002 (Plugin Architecture for Providers)](./docs/adr/0002-plugin-architecture.md), spawns its CLI, and translates between IR and provider-native IO.
|
||
- **Cache layer** — `lib/cache/` is a content-addressed cache keyed on the 11-field tuple defined in [ADR 0005 § "Cache key composition (v1.0)"](./docs/adr/0005-cache-cross-provider.md#decision) (provider, model, messages, tools, temperature, response_format, cache_control, max_tokens, top_p, stop, tool_choice — as of Amendment 2/D15). Per-key isolation, prompt-caching bypass, chunked stream replay, and singleflight. See [ADR 0005 (Cache Layer Cross-Provider Design)](./docs/adr/0005-cache-cross-provider.md). Current backing store is an in-memory Map; file-backed storage is planned for Phase 2.
|
||
- **Fallback engine** — `lib/fallback/` advances a configured chain one provider at a time on configured triggers, never retrying after the first response chunk has been emitted to the client. See [ADR 0004](./docs/adr/0004-fallback-engine.md).
|
||
- **Multi-key auth** — `lib/keys.mjs` (📋 planned, Phase 2) will carry OCP's per-OLP-key namespace isolation forward. Each OLP API key will have independent quota, cache namespace, and audit log; each key declares which providers it can access.
|
||
|
||
Read the ADRs in `docs/adr/` in order before proposing structural changes.
|
||
|
||
---
|
||
|
||
## Phase plan
|
||
|
||
OLP lands in phases. Each phase has its own PR series and Iron-Rule-10 reviewer; this README's placeholders are filled per-phase via the [`release_kit`](./CLAUDE.md) overlay.
|
||
|
||
- Phase 0 — Repo bootstrap, `ALIGNMENT.md`, founding ADRs, CI workflows, PR template. **(current)**
|
||
- Phase 1 — `server.mjs` skeleton, IR, Anthropic plugin, cache D1+D4. Port from OCP.
|
||
- Phase 2 — OpenAI Codex plugin.
|
||
- Phase 3 — Mistral Vibe plugin.
|
||
- Phase 4 — Fallback engine + routing chains config + quota poll worker.
|
||
- Phase 5 — Cache cross-provider hardening (D2+D3).
|
||
- Phase 6 — Dashboard + observability (`/v0/management/quota`).
|
||
- Phase 7 — Release v0.1, OCP enters maintenance.
|
||
- Phase 8+ — Optional Grok / Kimi / tier-2 plugins; provider-native protocol endpoints; deterministic triggers.
|
||
|
||
Full spec (decision rationale, open questions, risks): `~/.cc-rules/memory/projects/olp_v0_1_spec.md` on the maintainer's workstations.
|
||
|
||
---
|
||
|
||
## Migration from OCP
|
||
|
||
_placeholder — `scripts/migrate-from-ocp.mjs` lands with Phase 7 (📋 planned, not yet authored)._
|
||
|
||
Anticipated user-facing flow (target: <5 minutes):
|
||
|
||
1. Stop OCP (`launchctl bootout` the OCP service or `ocp stop`).
|
||
2. Install OLP.
|
||
3. Run `olp migrate-from-ocp` — copies `~/.ocp/keys/` to `~/.olp/keys/` and points provider plugins at OCP's existing auth artifacts where applicable.
|
||
4. Start OLP. Clients pointing at port 3456 keep working; their existing OLP API keys remain valid.
|
||
|
||
OCP's cache directory is *not* migrated: OLP's cache key format includes provider+model and warms cold naturally. OCP enters maintenance mode (stability fixes only) when OLP v0.1 ships; new development happens in OLP.
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
MIT.
|
||
|
||
---
|
||
|
||
## Acknowledgements
|
||
|
||
OLP evolved from [OCP (Open Claude Proxy)](https://github.com/dtzp555-max/ocp). OCP's per-key isolation model, cache-layer design (D1–D4), dashboard, and alignment-constitution discipline are all carried forward. The structural generalization from single-CLI to multi-provider is what makes this a new project rather than an OCP minor version — see [`ALIGNMENT.md` § Reference: How OCP's `cli.js` discipline maps to OLP](./ALIGNMENT.md#reference-how-ocps-clijs-discipline-maps-to-olp).
|
||
|
||
Authors: project maintainer (with AI drafting assistance).
|