fix: strip the [1m] context marker Claude Code appends to the model name

With the 1M-context beta enabled, Claude Code sends model: "claude-opus-5[1m]".
The marker is a client-side annotation — it matches no combo name, no alias and
no provider/model pair — so the request dies at model resolution with
"Invalid model format" and the client reports "There's an issue with the
selected model". Every request from that session fails until the beta is
switched off.

New open-sse/utils/modelMarkers.js exporting stripModelContextMarker(modelStr)
-> { model, contextMarker }. handleChat strips the marker before resolution and
normalizes body.model so downstream logging and translation see the real name.
Only a trailing marker is stripped, so a model whose name genuinely contains
brackets is left alone.

The capability itself travels in anthropic-beta: context-1m-2025-08-07, which
the default executor already forwards untouched — only the routing key needed
cleaning.

Fixes #3690.

Tests: tests/unit/model-context-marker.test.js (6 cases).
This commit is contained in:
Federico Liva
2026-09-02 20:20:12 +07:00
committed by decolua
parent f68d2f5ee5
commit ee7a961633
3 changed files with 80 additions and 1 deletions

View File

@@ -23,6 +23,7 @@ import { detectFormatByEndpoint } from "open-sse/translator/formats.js";
import * as log from "../utils/logger.js";
import { updateProviderCredentials, checkAndRefreshToken } from "../services/tokenRefresh.js";
import { getProjectIdForConnection } from "open-sse/services/projectId.js";
import { stripModelContextMarker } from "open-sse/utils/modelMarkers.js";
/**
* Handle chat completion request
@@ -47,7 +48,11 @@ export async function handleChat(request, clientRawRequest = null) {
headers: Object.fromEntries(request.headers.entries())
};
}
const modelStr = body.model;
// Claude Code marks a 1M-context request as `<model>[1m]`; the marker matches
// no combo, alias or provider/model pair, so it must not reach resolution.
// The capability travels in the anthropic-beta header, forwarded as-is.
const { model: modelStr, contextMarker } = stripModelContextMarker(body.model);
if (contextMarker) body.model = modelStr;
// Request summary is emitted as the unified "▶" line in chatCore (has fmt/thinking/account)