mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 02:02:42 +00:00
feat(claude): add Fable 5.1 model
Expose the new 1M-context model through the curated Claude catalog and normalize its dated and context-suffixed runtime IDs back to one canonical entry.
This commit is contained in:
@@ -424,6 +424,7 @@ describe("ClaudeAgentClient.fetchCatalog", () => {
|
||||
});
|
||||
|
||||
expect(models.map((m) => m.id)).toEqual([
|
||||
"claude-fable-5-1",
|
||||
"claude-opus-5",
|
||||
"claude-fable-5",
|
||||
"claude-fable-5[1m]",
|
||||
@@ -495,6 +496,7 @@ describe("ClaudeAgentClient.fetchCatalog", () => {
|
||||
};
|
||||
|
||||
expect(getThinkingIds("claude-opus-5")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-fable-5-1")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-fable-5")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-opus-4-8[1m]")).toContain("ultracode");
|
||||
expect(getThinkingIds("claude-opus-4-8")).toContain("ultracode");
|
||||
|
||||
@@ -34,6 +34,13 @@ export const CLAUDE_DISABLED_THINKING_OPTION_ID = "off";
|
||||
export const CLAUDE_ULTRACODE_THINKING_OPTION_ID = "ultracode";
|
||||
|
||||
export const CLAUDE_MODEL_MANIFEST = [
|
||||
{
|
||||
id: "claude-fable-5-1",
|
||||
label: "Fable 5.1",
|
||||
description: "Fable 5.1 · Most powerful model",
|
||||
contextWindowMaxTokens: 1_000_000,
|
||||
effortLevels: CLAUDE_EFFORT_LEVELS.xhigh,
|
||||
},
|
||||
{
|
||||
id: "claude-opus-5",
|
||||
label: "Opus 5",
|
||||
@@ -50,7 +57,7 @@ export const CLAUDE_MODEL_MANIFEST = [
|
||||
// COMPAT(claudeFable5OneMillionId): added in v0.3.0, remove after 2027-02-06 once pre-v0.3.0 app preferences are outside support.
|
||||
aliases: ["claude-fable-5[1m]"],
|
||||
label: "Fable 5",
|
||||
description: "Fable 5 · Most powerful model",
|
||||
description: "Fable 5 · Previous release",
|
||||
minimumClaudeCodeVersion: "2.1.169",
|
||||
contextWindowMaxTokens: 1_000_000,
|
||||
effortLevels: CLAUDE_EFFORT_LEVELS.xhigh,
|
||||
@@ -326,7 +333,7 @@ export function normalizeClaudeManifestModelId(value: string | null | undefined)
|
||||
}
|
||||
|
||||
const runtimeMatch = trimmed.match(
|
||||
/^(?:claude[-_ ])?(opus|sonnet|haiku)[-_ ]+(\d+)[-.](\d+)(?:\[1m\])?(?:[-_ ]+\d{8})?(?:\[1m\])?$/i,
|
||||
/^(?:claude[-_ ])?(fable|opus|sonnet|haiku)[-_ ]+(\d+)[-.](\d+)(?:\[1m\])?(?:[-_ ]+\d{8})?(?:\[1m\])?$/i,
|
||||
);
|
||||
if (!runtimeMatch) {
|
||||
return null;
|
||||
@@ -371,7 +378,7 @@ export function normalizeClaudeRuntimeModelId(value: string | null | undefined):
|
||||
}
|
||||
|
||||
const runtimeMatch = trimmed.match(
|
||||
/claude[-_ ](opus|sonnet|haiku)[-_ ]+(\d+)[-.](\d+)(\[1m\])?/i,
|
||||
/claude[-_ ](fable|opus|sonnet|haiku)[-_ ]+(\d+)[-.](\d+)(\[1m\])?/i,
|
||||
);
|
||||
if (!runtimeMatch) {
|
||||
return null;
|
||||
@@ -417,6 +424,14 @@ function normalizeMajorMinorClaudeModelId(
|
||||
): string | null {
|
||||
const family = familyValue.toLowerCase();
|
||||
const suffix = hasOneMillionContext ? "[1m]" : "";
|
||||
const candidate = `claude-${family}-${major}-${minor}${suffix}`;
|
||||
return isClaudeManifestModelId(candidate) ? candidate : null;
|
||||
const candidates = [
|
||||
`claude-${family}-${major}-${minor}${suffix}`,
|
||||
`claude-${family}-${major}-${minor}`,
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (isClaudeManifestModelId(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ describe("getClaudeModels", () => {
|
||||
it("returns all claude models", () => {
|
||||
const models = getClaudeModels();
|
||||
expect(models.map((m) => m.id)).toEqual([
|
||||
"claude-fable-5-1",
|
||||
"claude-opus-5",
|
||||
"claude-fable-5",
|
||||
"claude-fable-5[1m]",
|
||||
@@ -81,6 +82,7 @@ describe("getClaudeModels", () => {
|
||||
|
||||
expect(contextWindows).toEqual(
|
||||
new Map([
|
||||
["claude-fable-5-1", 1_000_000],
|
||||
["claude-opus-5", 1_000_000],
|
||||
["claude-fable-5", 1_000_000],
|
||||
["claude-fable-5[1m]", 1_000_000],
|
||||
@@ -160,6 +162,9 @@ describe("getClaudeModels", () => {
|
||||
expect(models.get("claude-fable-5")?.thinkingOptions?.map((option) => option.id)).not.toContain(
|
||||
CLAUDE_DISABLED_THINKING_OPTION_ID,
|
||||
);
|
||||
expect(
|
||||
models.get("claude-fable-5-1")?.thinkingOptions?.map((option) => option.id),
|
||||
).not.toContain(CLAUDE_DISABLED_THINKING_OPTION_ID);
|
||||
expect(models.get("claude-haiku-4-5")?.thinkingOptions).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -170,6 +175,7 @@ describe("getClaudeModels", () => {
|
||||
["claude-sonnet-5[1m]", true, "high"],
|
||||
["claude-sonnet-5-20260101", true, "high"],
|
||||
["claude-fable-5", false, "high"],
|
||||
["claude-fable-5-1", false, "high"],
|
||||
["claude-haiku-4-5", false, undefined],
|
||||
["openrouter/anthropic/claude-opus-4-8", false, undefined],
|
||||
[null, false, undefined],
|
||||
@@ -458,7 +464,7 @@ describe("Claude Opus 5 catalog", () => {
|
||||
describe("Claude Fable 5 catalog", () => {
|
||||
it("offers one selectable Fable 5 entry and a compatibility entry for old apps", () => {
|
||||
const fable5Models = getClaudeModels()
|
||||
.filter((model) => model.id.startsWith("claude-fable-5"))
|
||||
.filter((model) => model.id === "claude-fable-5" || model.id === "claude-fable-5[1m]")
|
||||
.map(({ id, aliases, isSelectable, label, contextWindowMaxTokens }) => ({
|
||||
id,
|
||||
aliases,
|
||||
@@ -491,6 +497,24 @@ describe("Claude Fable 5 catalog", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Claude Fable 5.1 catalog", () => {
|
||||
it("offers one Fable 5.1 entry with a 1M context window", () => {
|
||||
const fable51Models = getClaudeModels()
|
||||
.filter((model) => model.id.startsWith("claude-fable-5-1"))
|
||||
.map(({ id, label, contextWindowMaxTokens }) => ({ id, label, contextWindowMaxTokens }));
|
||||
|
||||
expect(fable51Models).toEqual([
|
||||
{ id: "claude-fable-5-1", label: "Fable 5.1", contextWindowMaxTokens: 1_000_000 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("resolves suffixed and dated Fable 5.1 IDs to the catalog entry", () => {
|
||||
expect(findClaudeModel("claude-fable-5-1[1m]")?.id).toBe("claude-fable-5-1");
|
||||
expect(findClaudeModel("claude-fable-5-1-20260901")?.id).toBe("claude-fable-5-1");
|
||||
expect(findClaudeModel("claude-fable-5-1-20260901[1m]")?.id).toBe("claude-fable-5-1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("claudeManifestModelSupportsFastMode", () => {
|
||||
it("keeps fast mode strict to first-party manifest model IDs", () => {
|
||||
expect(normalizeClaudeManifestModelId("openrouter/anthropic/claude-opus-4-8")).toBeNull();
|
||||
@@ -502,5 +526,6 @@ describe("claudeManifestModelSupportsFastMode", () => {
|
||||
expect(claudeManifestModelSupportsFastMode("claude-opus-5")).toBe(true);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-sonnet-5")).toBe(false);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-fable-5")).toBe(false);
|
||||
expect(claudeManifestModelSupportsFastMode("claude-fable-5-1")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user