Skip to content

feat: upstream model autodetection + one long-context rule for every family (v4.8.57)#489

Merged
askalf merged 1 commit into
masterfrom
feat/model-catalog-autodetect
Jun 10, 2026
Merged

feat: upstream model autodetection + one long-context rule for every family (v4.8.57)#489
askalf merged 1 commit into
masterfrom
feat/model-catalog-autodetect

Conversation

@askalf

@askalf askalf commented Jun 10, 2026

Copy link
Copy Markdown
Owner

What

Operator direction (2026-06-10): model availability should be autodetected, and all models should use the same method for context windows.

1. Upstream model autodetection

New src/model-catalog.ts: GET /v1/models now asks api.anthropic.com/v1/models what actually exists (authenticated the same way the request path is — OAuth bearer, or x-api-key in ANTHROPIC_UPSTREAM_API_KEY mode), normalizes the listing (claude-only; legacy claude-3-x dropped; CC-style short ids preferred over dated duplicates; deterministic family-rank/version-desc order; unknown future families kept — the next fable-like launch appears on the next refresh with no dario release), and serves it from a stale-while-revalidate TTL cache (1h, DARIO_MODEL_CATALOG_TTL_MS; failed fetches back off 5 min). Every failure path falls back to the baked list — cold start offline, broken auth, upstream 4xx/5xx, garbage listing — so the route always 200s and behaves exactly as before when upstream is unreachable. Prewarmed at startup so the first client call is answered from cache.

2. One long-context rule

[1m] handling was hand-sprinkled: the listing carried claude-fable-5[1m] but no opus/sonnet variants, and each <family>1m alias was a hand-picked pin — already drifted (#389 bumped opus to 4-8; opus1m silently stayed on 4-7).

Now:

  • longContextEligible() is the single rule: every claude family takes a [1m] variant except haiku (real CC never offers 1M haiku).
  • withLongContextVariants() generates the advertised variants from that rule — the listing now carries claude-opus-4-8[1m], claude-sonnet-4-6[1m], etc., exactly like fable's.
  • <family>1m aliases derive from resolve(<family>) + '[1m]' — the pair can never disagree again.

Wire mechanics unchanged (and already uniform): [1m] stays a client-side label — base id + context-1m-2025-08-07 on the wire, existing billing-rejection auto-retry untouched.

⚠️ Behavior change

opus1m now resolves to claude-opus-4-8[1m] (was the stale claude-opus-4-7[1m]). Want the old target? Send claude-opus-4-7[1m] explicitly.

Tests

  • New test/model-catalog.mjs — 59 assertions: the eligibility rule, variant generation/adjacency, upstream normalization (dated-id dedupe, legacy-generation filter, unknown-family retention), family resolution + alias derivation, cache success/fallback/TTL/backoff, x-api-key auth mode, payload shape.
  • test/provider-prefix.mjs updated for the derived opus1m.
  • Full suite 85/85; tsc clean.

Risk

Low. The hot request path only gains a synchronous cache read in resolveClaudeAlias. /v1/models is the only route that awaits the catalog, and it cannot throw. Offline/air-gapped deployments serve the identical baked list as today.

…family (v4.8.57)

/v1/models now serves Anthropic's live catalog (TTL-cached, stale-while-
revalidate, baked fallback on every failure path) instead of a hardcoded
list, and family shortcuts resolve against it — a new model shows up and
routes the day it lands, no dario release needed.

[1m] long-context variants are generated by ONE rule for every family
(everything except haiku) instead of being hand-listed: the listing now
carries claude-opus-4-8[1m] / claude-sonnet-4-6[1m] exactly like fable's,
and <family>1m aliases derive from <family> + '[1m]' so they can't drift.
Behavior change: opus1m now resolves to claude-opus-4-8[1m] (was stale
claude-opus-4-7[1m] from before #389 bumped opus to 4-8).

New src/model-catalog.ts + test/model-catalog.mjs (59 assertions);
full suite 85/85.
@github-actions

Copy link
Copy Markdown
Contributor

Compat test: ✅ PASSED

Ran node test/compat.mjs against dario proxy --passthrough on the self-hosted runner for commit 0182c02270c8cfa2b80ce68b493dcf7beec11e29.

Output
============================================================
  dario Compatibility Validation (--passthrough via dario)
  2026-06-10T21:29:33.439Z
============================================================

--- Anthropic Messages API (Hermes) ---
✅ #1 Anthropic non-stream: "COMPAT OK" | thinking_injected=false | service_tier=-
✅ #2 Anthropic stream: 8 events | order=correct | "**STREAM COMPAT**"
✅ #3 SSE framing: event:/data: pairs correctly paired

--- Passthrough Verification ---
✅ #4 No thinking injection: No thinking block in response (passthrough clean)
✅ #5 Client betas preserved: Client-requested thinking honored (status=200)

--- Tool Use (OpenClaw) ---
✅ #6 Tool use: tool=get_weather | input={"location":"Tokyo"}
✅ #7 Tool use stream: 10 events | tool_use=true | input_json_delta=true

--- OpenAI Compat ---
✅ #8 OpenAI non-stream: "OPENAI COMPAT"
✅ #9 OpenAI stream: 3 chunks | [DONE]=true | "**STREAM OPENAI**"

--- Header Visibility ---
✅ #10 Header visibility: request-id=true | ratelimit=true (12 headers)

============================================================
  RESULTS: 10 passed, 0 failed, 0 warnings
============================================================

Full workflow run

@askalf askalf merged commit 66673eb into master Jun 10, 2026
12 checks passed
@askalf askalf deleted the feat/model-catalog-autodetect branch June 10, 2026 21:33
askalf added a commit that referenced this pull request Jun 10, 2026
RELEASING.md pre-merge checklist item that #489 (and the 4.8.56 release
before it) missed. Lockfile-only; no dependency changes, no version bump —
does not trigger auto-release.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant