mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-26 15:35:07 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2823f34c7d | ||
|
|
ca14e6e834 |
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Claude Opus 5 (`claude-opus-5`), and the `opus` alias now resolves to it.** New `models.json` entry — `/v1/models` goes 6 → 7 and OpenClaw picks it up on the next `ocp update` (via `scripts/sync-openclaw.mjs`). Verified against the installed CLI rather than assumed: the compiled `claude` 2.1.220 bundle carries `latest_per_family:{fable:"claude-fable-5",opus:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5"}`, so repointing `opus` mirrors what the CLI itself defaults to — the same reasoning as #168 (sonnet → sonnet-5). Availability confirmed with a live `claude -p --model claude-opus-5` completion on the subscription pool. Pricing is unchanged from Opus 4.8 ($5/$25 per MTok, CLI registry `pricing:"tier_5_25"`), so the alias repoint carries no cost change. `claude-opus-4-8` is retained for pinning.
|
||||||
|
- `contextWindow` is deliberately **200000**, not Opus 5's native 1M. Two reasons, both verified: (1) `MAX_PROMPT_CHARS` is a **single global** budget — `derivePromptCharBudget` takes `max(contextWindow) × 3` across *all* entries (`lib/prompt.mjs`), so a 1M entry would raise the truncation ceiling to 3,000,000 chars for `claude-haiku-4-5` too, which is genuinely 200k native, converting clean OCP-side truncation into an upstream API rejection; (2) OpenClaw scales its history budget linearly off this value (`contextWindow × maxHistoryShare × SAFETY_MARGIN` = `× 0.6`, plus an oversized-message threshold at `× 0.5`, per `compaction-planning` in OpenClaw 2026.7.1), and its own bundled registry hardcodes 200000 for Claude — the upstream request to raise it to 1M ([openclaw#22979](https://github.com/openclaw/openclaw/issues/22979)) was closed *not planned*. A new regression test pins the invariant so a future 1M entry has to be a deliberate, reviewed change. Raising it for real needs per-model budgets — tracked separately, ADR-level.
|
||||||
|
|
||||||
## v3.24.0 — 2026-07-21
|
## v3.24.0 — 2026-07-21
|
||||||
|
|
||||||
Minor release. Headline: two long-requested **OpenAI-compat features** land — **multimodal vision** (`image_url` parts) and **structured outputs** (`response_format` / JSON schema). Also: the prompt-char budget now derives from the model SPOT instead of a hand-set constant, an agentic-turn bug that dropped the model's final answer is fixed, and `OCP_LOCAL_TOOLS` supports the OpenClaw-backend use case. Four of the six landed from external contributors (@vvlasy-openclaw). Every code PR carried a fresh-context reviewer (Iron Rule 10); no new endpoint, no new `cli.js` wire behavior.
|
Minor release. Headline: two long-requested **OpenAI-compat features** land — **multimodal vision** (`image_url` parts) and **structured outputs** (`response_format` / JSON schema). Also: the prompt-char budget now derives from the model SPOT instead of a hand-set constant, an agentic-turn bug that dropped the model's final answer is fixed, and `OCP_LOCAL_TOOLS` supports the OpenClaw-backend use case. Four of the six landed from external contributors (@vvlasy-openclaw). Every code PR carried a fresh-context reviewer (Iron Rule 10); no new endpoint, no new `cli.js` wire behavior.
|
||||||
|
|||||||
@@ -113,11 +113,11 @@ node setup.mjs
|
|||||||
|
|
||||||
`setup.mjs` verifies the Claude CLI, starts the proxy on port 3456, and installs auto-start (launchd on macOS, systemd on Linux). The `ocp` CLI lands at `~/ocp/ocp` — symlink it onto your PATH (`sudo ln -sf ~/ocp/ocp /usr/local/bin/ocp`, or `ln -sf ~/ocp/ocp ~/.local/bin/ocp`) or alias it (`alias ocp=~/ocp/ocp`); the rest of the docs assume `ocp` is on your PATH.
|
`setup.mjs` verifies the Claude CLI, starts the proxy on port 3456, and installs auto-start (launchd on macOS, systemd on Linux). The `ocp` CLI lands at `~/ocp/ocp` — symlink it onto your PATH (`sudo ln -sf ~/ocp/ocp /usr/local/bin/ocp`, or `ln -sf ~/ocp/ocp ~/.local/bin/ocp`) or alias it (`alias ocp=~/ocp/ocp`); the rest of the docs assume `ocp` is on your PATH.
|
||||||
|
|
||||||
**Verify** — should list 6 models:
|
**Verify** — should list 7 models:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl http://127.0.0.1:3456/v1/models
|
curl http://127.0.0.1:3456/v1/models
|
||||||
# claude-opus-4-8, claude-opus-4-7, claude-opus-4-6, claude-sonnet-5, claude-sonnet-4-6, claude-haiku-4-5-20251001
|
# claude-opus-5, claude-opus-4-8, claude-opus-4-7, claude-opus-4-6, claude-sonnet-5, claude-sonnet-4-6, claude-haiku-4-5-20251001
|
||||||
```
|
```
|
||||||
|
|
||||||
**Connect one IDE** — point any OpenAI-compatible tool at the proxy, then reload your shell and start a tool (Cline / Continue / Cursor / OpenCode):
|
**Connect one IDE** — point any OpenAI-compatible tool at the proxy, then reload your shell and start a tool (Cline / Continue / Cursor / OpenCode):
|
||||||
@@ -169,8 +169,9 @@ Any tool use happens server-side, under the `--allowedTools` set configured on t
|
|||||||
|
|
||||||
| Model ID | Notes |
|
| Model ID | Notes |
|
||||||
|----------|-------|
|
|----------|-------|
|
||||||
| `claude-opus-4-8` | Most capable (default for `opus` alias) |
|
| `claude-opus-5` | Most capable (default for `opus` alias) |
|
||||||
| `claude-opus-4-7` | Previous Opus, retained for pinning |
|
| `claude-opus-4-8` | Previous Opus, retained for pinning |
|
||||||
|
| `claude-opus-4-7` | Older Opus, retained for pinning |
|
||||||
| `claude-opus-4-6` | Older Opus, retained for pinning |
|
| `claude-opus-4-6` | Older Opus, retained for pinning |
|
||||||
| `claude-sonnet-5` | Latest Sonnet (default for `sonnet` alias) |
|
| `claude-sonnet-5` | Latest Sonnet (default for `sonnet` alias) |
|
||||||
| `claude-sonnet-4-6` | Previous Sonnet, retained for pinning |
|
| `claude-sonnet-4-6` | Previous Sonnet, retained for pinning |
|
||||||
|
|||||||
+6
-5
@@ -78,7 +78,7 @@ Run `ocp lan` to see your IP and ready-to-share instructions.
|
|||||||
**Verify:**
|
**Verify:**
|
||||||
```bash
|
```bash
|
||||||
curl http://127.0.0.1:3456/v1/models
|
curl http://127.0.0.1:3456/v1/models
|
||||||
# Returns: claude-opus-4-8, claude-opus-4-7, claude-opus-4-6, claude-sonnet-5, claude-sonnet-4-6, claude-haiku-4-5-20251001
|
# Returns: claude-opus-5, claude-opus-4-8, claude-opus-4-7, claude-opus-4-6, claude-sonnet-5, claude-sonnet-4-6, claude-haiku-4-5-20251001
|
||||||
```
|
```
|
||||||
|
|
||||||
### Headless install notes
|
### Headless install notes
|
||||||
@@ -114,7 +114,7 @@ Please follow https://github.com/dtzp555-max/ocp/blob/main/README.md
|
|||||||
installed and logged in (`claude auth status`). Install missing pieces
|
installed and logged in (`claude auth status`). Install missing pieces
|
||||||
using my system's package manager.
|
using my system's package manager.
|
||||||
2. git clone the repo, cd in, and run `node setup.mjs`.
|
2. git clone the repo, cd in, and run `node setup.mjs`.
|
||||||
3. Verify with `curl http://127.0.0.1:3456/v1/models` (should list 6 models).
|
3. Verify with `curl http://127.0.0.1:3456/v1/models` (should list 7 models).
|
||||||
4. Add `export OPENAI_BASE_URL=http://127.0.0.1:3456/v1` to my shell rc.
|
4. Add `export OPENAI_BASE_URL=http://127.0.0.1:3456/v1` to my shell rc.
|
||||||
5. Tell me to reload my shell and try a tool like Cline / Continue / Cursor.
|
5. Tell me to reload my shell and try a tool like Cline / Continue / Cursor.
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ Please follow https://github.com/dtzp555-max/ocp/blob/main/docs/lan-mode.md
|
|||||||
5. Add OCP_ADMIN_KEY to my shell rc (~/.zshrc or ~/.bashrc).
|
5. Add OCP_ADMIN_KEY to my shell rc (~/.zshrc or ~/.bashrc).
|
||||||
6. Run `ocp lan` to show me the LAN IP and connect command.
|
6. Run `ocp lan` to show me the LAN IP and connect command.
|
||||||
7. Optionally create example keys: `ocp keys add laptop`, `ocp keys add tablet`.
|
7. Optionally create example keys: `ocp keys add laptop`, `ocp keys add tablet`.
|
||||||
8. Verify: `curl http://127.0.0.1:3456/v1/models` returns 6 models.
|
8. Verify: `curl http://127.0.0.1:3456/v1/models` returns 7 models.
|
||||||
|
|
||||||
Tell me each step before running it. On error, diagnose before retrying.
|
Tell me each step before running it. On error, diagnose before retrying.
|
||||||
```
|
```
|
||||||
@@ -163,7 +163,7 @@ Please follow https://github.com/dtzp555-max/ocp/blob/main/docs/lan-mode.md
|
|||||||
chmod +x ocp-connect
|
chmod +x ocp-connect
|
||||||
2. Run `./ocp-connect <SERVER_IP>` (add `--key <KEY>` if you have one).
|
2. Run `./ocp-connect <SERVER_IP>` (add `--key <KEY>` if you have one).
|
||||||
3. Follow any IDE-specific manual hints it prints.
|
3. Follow any IDE-specific manual hints it prints.
|
||||||
4. Verify: `curl http://<SERVER_IP>:3456/v1/models` returns 6 models.
|
4. Verify: `curl http://<SERVER_IP>:3456/v1/models` returns 7 models.
|
||||||
5. Tell me to reload my shell + restart any IDE that was already running.
|
5. Tell me to reload my shell + restart any IDE that was already running.
|
||||||
|
|
||||||
Don't auto-retry on error. Tell me the failure mode first.
|
Don't auto-retry on error. Tell me the failure mode first.
|
||||||
@@ -216,7 +216,7 @@ OCP Connect v1.3.0
|
|||||||
(set by admin via PROXY_ANONYMOUS_KEY; see issue #12 §14 Path A)
|
(set by admin via PROXY_ANONYMOUS_KEY; see issue #12 §14 Path A)
|
||||||
|
|
||||||
Testing API access...
|
Testing API access...
|
||||||
✓ API accessible (6 models available)
|
✓ API accessible (7 models available)
|
||||||
|
|
||||||
Shell config:
|
Shell config:
|
||||||
✓ .bashrc
|
✓ .bashrc
|
||||||
@@ -246,6 +246,7 @@ OCP Connect v1.3.0
|
|||||||
✓ OpenClaw configured
|
✓ OpenClaw configured
|
||||||
Provider: ocp
|
Provider: ocp
|
||||||
Models:
|
Models:
|
||||||
|
• ocp/claude-opus-5
|
||||||
• ocp/claude-opus-4-8
|
• ocp/claude-opus-4-8
|
||||||
• ocp/claude-opus-4-7
|
• ocp/claude-opus-4-7
|
||||||
• ocp/claude-opus-4-6
|
• ocp/claude-opus-4-6
|
||||||
|
|||||||
+9
-1
@@ -2,6 +2,14 @@
|
|||||||
"$schema": "./models.schema.json",
|
"$schema": "./models.schema.json",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"models": [
|
"models": [
|
||||||
|
{
|
||||||
|
"id": "claude-opus-5",
|
||||||
|
"displayName": "Claude Opus 5",
|
||||||
|
"openclawName": "Claude Opus 5 (via CLI)",
|
||||||
|
"reasoning": true,
|
||||||
|
"contextWindow": 200000,
|
||||||
|
"maxTokens": 16384
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "claude-opus-4-8",
|
"id": "claude-opus-4-8",
|
||||||
"displayName": "Claude Opus 4.8",
|
"displayName": "Claude Opus 4.8",
|
||||||
@@ -52,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"opus": "claude-opus-4-8",
|
"opus": "claude-opus-5",
|
||||||
"sonnet": "claude-sonnet-5",
|
"sonnet": "claude-sonnet-5",
|
||||||
"haiku": "claude-haiku-4-5-20251001"
|
"haiku": "claude-haiku-4-5-20251001"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4067,6 +4067,10 @@ test("models.json aliases.sonnet === 'claude-sonnet-5' (default-request-model SP
|
|||||||
assert.equal(_spotModels.aliases.sonnet, "claude-sonnet-5");
|
assert.equal(_spotModels.aliases.sonnet, "claude-sonnet-5");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("models.json aliases.opus === 'claude-opus-5' (opus-alias SPOT)", () => {
|
||||||
|
assert.equal(_spotModels.aliases.opus, "claude-opus-5");
|
||||||
|
});
|
||||||
|
|
||||||
// ── Referential integrity (PR #152 review) ──────────────────────────────────
|
// ── Referential integrity (PR #152 review) ──────────────────────────────────
|
||||||
// The value-mirror assertions above only prove the alias equals a string literal —
|
// The value-mirror assertions above only prove the alias equals a string literal —
|
||||||
// they pass even if that literal points at a model that does not exist in
|
// they pass even if that literal points at a model that does not exist in
|
||||||
@@ -4080,6 +4084,25 @@ test("models.json: claude-sonnet-5 is present in models[] (the entry this PR add
|
|||||||
assert.ok(_spotModelIds.has("claude-sonnet-5"), "claude-sonnet-5 must exist as a models[].id");
|
assert.ok(_spotModelIds.has("claude-sonnet-5"), "claude-sonnet-5 must exist as a models[].id");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("models.json: claude-opus-5 is present in models[] (the entry this PR adds)", () => {
|
||||||
|
assert.ok(_spotModelIds.has("claude-opus-5"), "claude-opus-5 must exist as a models[].id");
|
||||||
|
});
|
||||||
|
|
||||||
|
// The prompt-char budget is GLOBAL (max across every entry × 3 chars/token), not
|
||||||
|
// per-model — see lib/prompt.mjs derivePromptCharBudget. An entry declaring a native 1M
|
||||||
|
// window would therefore raise the truncation ceiling for claude-haiku-4-5 too (genuinely
|
||||||
|
// 200k), turning OCP-side truncation into an upstream API rejection.
|
||||||
|
//
|
||||||
|
// Asserts the MAX, deliberately, not every entry: ADR 0009 states the budget "scales
|
||||||
|
// automatically — no code change", so a future entry with a SMALLER window (say a 128k
|
||||||
|
// model) must stay legal and must not fail this suite. Only raising the ceiling is the
|
||||||
|
// hazard, and that is an ADR-level decision requiring per-model budgets first.
|
||||||
|
test("models.json: max contextWindow is 200000 (global prompt-budget ceiling)", () => {
|
||||||
|
const windows = _spotModels.models.map(m => m.contextWindow);
|
||||||
|
assert.equal(Math.max(...windows), 200000,
|
||||||
|
`max contextWindow re-scales MAX_PROMPT_CHARS for ALL models incl. the 200k-native haiku (see lib/prompt.mjs + ADR 0009)`);
|
||||||
|
});
|
||||||
|
|
||||||
test("models.json: every aliases value resolves to a real models[].id (referential integrity)", () => {
|
test("models.json: every aliases value resolves to a real models[].id (referential integrity)", () => {
|
||||||
for (const [name, target] of Object.entries(_spotModels.aliases)) {
|
for (const [name, target] of Object.entries(_spotModels.aliases)) {
|
||||||
assert.ok(_spotModelIds.has(target), `aliases.${name} -> '${target}' is a dangling alias (no matching models[].id)`);
|
assert.ok(_spotModelIds.has(target), `aliases.${name} -> '${target}' is a dangling alias (no matching models[].id)`);
|
||||||
|
|||||||
Reference in New Issue
Block a user