fix(ai): recognise Claude 5 and Fable models as vision-capable

The Anthropic vision check listed the Claude 3 and 4 families by prefix,
so a key pinned to claude-sonnet-5, claude-opus-5 or a Fable model was
told the provider cannot read images, and lab and document OCR refused
the upload even though the model handles image and PDF blocks.

Replace the per-release prefix list with a rule: any claude-<tier>-<major>
slug from the 4 family upward, plus every claude-fable-* model, counts as
vision-capable. Claude 3 handling is unchanged. The Settings preset list
gains claude-sonnet-5 and claude-opus-5 alongside the existing entries.
This commit is contained in:
Marc-André Bombeck committed 2026-09-02 10:32:50 +02:00
1 parent a463206774
commit 7d93af9d09
4 files changed
+36 -9

No files matched your search

+6
View File
@@ -17,6 +17,12 @@
plain instruction to reply with a single JSON object and nothing else,
and the reply is narrowed to the object body on the way out, so a fenced
or prefixed answer still parses. Reported independently by @sweidinger.
- **Lab and document OCR no longer refuse a current Claude model.** The
vision check knew the Claude 3 and 4 families by name, so a key pinned to
`claude-sonnet-5`, `claude-opus-5` or a Fable model was told the provider
cannot read images even though it can. Any Claude tier from the 4 family
upward, and every Fable model, now counts as vision-capable. The model
preset list in Settings also offers `claude-sonnet-5` and `claude-opus-5`.
## [1.38.1] — 2026-09-01
+2
View File
@@ -101,6 +101,8 @@ export const OPENAI_MODEL_PRESETS = [
export const ANTHROPIC_MODEL_PRESETS = [
"claude-sonnet-4-6",
"claude-opus-4-7",
"claude-sonnet-5",
"claude-opus-5",
"claude-haiku-4-5",
"claude-3-5-sonnet-latest",
] as const;
@@ -21,6 +21,19 @@ describe("supportsVisionForConfig", () => {
}
});
it("accepts current Claude 5-generation and Fable pins for anthropic", () => {
for (const model of [
"claude-sonnet-5",
"claude-opus-5",
"claude-haiku-4-5",
"claude-fable-5",
"claude-fable-5-1",
"claude-opus-4-8",
]) {
expect(supportsVisionForConfig("anthropic", model)).toBe(true);
}
});
it("rejects legacy / text-only anthropic models", () => {
expect(supportsVisionForConfig("anthropic", "claude-2.1")).toBe(false);
expect(supportsVisionForConfig("anthropic", "claude-instant-1")).toBe(
+15 -9
View File
@@ -15,27 +15,33 @@
/**
* Anthropic vision models. The Messages API `image` + `document` blocks are
* accepted by the whole Claude 3 family (3 / 3.5 / 3.7) and the Claude 4
* family. An older `claude-2*` / `claude-instant*` pin is text-only.
* accepted by the whole Claude 3 family (3 / 3.5 / 3.7) and by every family
* from Claude 4 upward, Fable included. An older `claude-2*` /
* `claude-instant*` pin is text-only.
*
* The bare `claude-3-opus` / `claude-3-sonnet` / `claude-3-haiku` prefixes are
* listed explicitly: a self-hoster pinned to e.g. `claude-3-opus-20240229`
* reads images, but the `claude-3-5` prefix does not match it.
* reads images, but the `claude-3-5` prefix does not match it. From Claude 4
* on the slug is `claude-<tier>-<major>[-<minor>]`, so a rule on the major
* version covers `claude-sonnet-4-6`, `claude-opus-5`, `claude-haiku-4-5` and
* whatever ships next without this list growing per release.
*/
function anthropicSupportsVision(model: string): boolean {
const m = model.toLowerCase();
return (
if (
m.startsWith("claude-3-opus") ||
m.startsWith("claude-3-sonnet") ||
m.startsWith("claude-3-haiku") ||
m.startsWith("claude-3-5") ||
m.startsWith("claude-3.5") ||
m.startsWith("claude-3-7") ||
m.startsWith("claude-3.7") ||
m.startsWith("claude-sonnet-4") ||
m.startsWith("claude-opus-4") ||
m.startsWith("claude-haiku-4")
);
m.startsWith("claude-3.7")
) {
return true;
}
if (m.startsWith("claude-fable-")) return true;
const tiered = m.match(/^claude-(?:sonnet|opus|haiku)-(\d+)/);
return tiered !== null && Number(tiered[1]) >= 4;
}
/**