| author | |
| committer | |
| log | 9d66b104feb761cff07c305d570610112bb5c8dc |
| tree | f0a02f1c5e8d5215656a78c1170294513cfc4a3f |
| parent | 51ea084c870457f22090e57b5c180144759cb993 |
| signature | Signed by PGP key B5690EEEBB952194 |
4 files changed, 43 insertions(+), 92 deletions(-)
apps/server/src/provider/Layers/ClaudeProvider.ts+36-1| ... | @@ -193,6 +193,36 @@ const BUILT_IN_MODELS: ReadonlyArray<ServerProviderModel> = [ | ... | @@ -193,6 +193,36 @@ const BUILT_IN_MODELS: ReadonlyArray<ServerProviderModel> = [ |
| 193 | ], | 193 | ], |
| 194 | }), | 194 | }), |
| 195 | }, | 195 | }, |
| 196 | { | ||
| 197 | slug: "claude-sonnet-5", | ||
| 198 | name: "Claude Sonnet 5", | ||
| 199 | isCustom: false, | ||
| 200 | capabilities: createModelCapabilities({ | ||
| 201 | optionDescriptors: [ | ||
| 202 | buildSelectOptionDescriptor({ | ||
| 203 | id: "effort", | ||
| 204 | label: "Reasoning", | ||
| 205 | options: [ | ||
| 206 | { value: "low", label: "Low" }, | ||
| 207 | { value: "medium", label: "Medium" }, | ||
| 208 | { value: "high", label: "High", isDefault: true }, | ||
| 209 | { value: "xhigh", label: "Extra High" }, | ||
| 210 | { value: "max", label: "Max" }, | ||
| 211 | { value: "ultrathink", label: "Ultrathink" }, | ||
| 212 | ], | ||
| 213 | promptInjectedValues: ["ultrathink"], | ||
| 214 | }), | ||
| 215 | buildSelectOptionDescriptor({ | ||
| 216 | id: "contextWindow", | ||
| 217 | label: "Context Window", | ||
| 218 | options: [ | ||
| 219 | { value: "200k", label: "200k", isDefault: true }, | ||
| 220 | { value: "1m", label: "1M" }, | ||
| 221 | ], | ||
| 222 | }), | ||
| 223 | ], | ||
| 224 | }), | ||
| 225 | }, | ||
| 196 | { | 226 | { |
| 197 | slug: "claude-sonnet-4-6", | 227 | slug: "claude-sonnet-4-6", |
| 198 | name: "Claude Sonnet 4.6", | 228 | name: "Claude Sonnet 4.6", |
| ... | @@ -322,7 +352,12 @@ export function normalizeClaudeCliEffort( | ... | @@ -322,7 +352,12 @@ export function normalizeClaudeCliEffort( |
| 322 | if (effort === "ultracode") { | 352 | if (effort === "ultracode") { |
| 323 | return "xhigh"; | 353 | return "xhigh"; |
| 324 | } | 354 | } |
| 325 | if (effort === "xhigh" && model !== "claude-fable-5" && model !== "claude-opus-4-8") { | 355 | if ( |
| 356 | effort === "xhigh" && | ||
| 357 | model !== "claude-fable-5" && | ||
| 358 | model !== "claude-opus-4-8" && | ||
| 359 | model !== "claude-sonnet-5" | ||
| 360 | ) { | ||
| 326 | return "max"; | 361 | return "max"; |
| 327 | } | 362 | } |
| 328 | if (effort === "max" && model === "claude-sonnet-4-6") { | 363 | if (effort === "max" && model === "claude-sonnet-4-6") { |
apps/web/src/components/settings/ProviderModelsSection.tsx+1-1| ... | @@ -32,7 +32,7 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; | ... | @@ -32,7 +32,7 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; |
| 32 | */ | 32 | */ |
| 33 | const CUSTOM_MODEL_PLACEHOLDER_BY_KIND: Partial<Record<ProviderDriverKind, string>> = { | 33 | const CUSTOM_MODEL_PLACEHOLDER_BY_KIND: Partial<Record<ProviderDriverKind, string>> = { |
| 34 | [ProviderDriverKind.make("codex")]: "gpt-6.7-codex-ultra-preview", | 34 | [ProviderDriverKind.make("codex")]: "gpt-6.7-codex-ultra-preview", |
| 35 | [ProviderDriverKind.make("claudeAgent")]: "claude-sonnet-5-0", | 35 | [ProviderDriverKind.make("claudeAgent")]: "claude-sonnet-5", |
| 36 | [ProviderDriverKind.make("cursor")]: "claude-sonnet-4-6", | 36 | [ProviderDriverKind.make("cursor")]: "claude-sonnet-4-6", |
| 37 | [ProviderDriverKind.make("opencode")]: "openai/gpt-5", | 37 | [ProviderDriverKind.make("opencode")]: "openai/gpt-5", |
| 38 | }; | 38 | }; |
packages/contracts/src/model.ts+5-2| ... | @@ -138,7 +138,7 @@ export const DEFAULT_GIT_TEXT_GENERATION_MODEL = "gpt-5.4-mini"; | ... | @@ -138,7 +138,7 @@ export const DEFAULT_GIT_TEXT_GENERATION_MODEL = "gpt-5.4-mini"; |
| 138 | 138 | ||
| 139 | export const DEFAULT_MODEL_BY_PROVIDER: Partial<Record<ProviderDriverKind, string>> = { | 139 | export const DEFAULT_MODEL_BY_PROVIDER: Partial<Record<ProviderDriverKind, string>> = { |
| 140 | [CODEX_DRIVER_KIND]: DEFAULT_MODEL, | 140 | [CODEX_DRIVER_KIND]: DEFAULT_MODEL, |
| 141 | [CLAUDE_DRIVER_KIND]: "claude-sonnet-4-6", | 141 | [CLAUDE_DRIVER_KIND]: "claude-sonnet-5", |
| 142 | [CURSOR_DRIVER_KIND]: "auto", | 142 | [CURSOR_DRIVER_KIND]: "auto", |
| 143 | [GROK_DRIVER_KIND]: "grok-build", | 143 | [GROK_DRIVER_KIND]: "grok-build", |
| 144 | [OPENCODE_DRIVER_KIND]: "openai/gpt-5", | 144 | [OPENCODE_DRIVER_KIND]: "openai/gpt-5", |
| ... | @@ -174,7 +174,10 @@ export const MODEL_SLUG_ALIASES_BY_PROVIDER: Partial< | ... | @@ -174,7 +174,10 @@ export const MODEL_SLUG_ALIASES_BY_PROVIDER: Partial< |
| 174 | "opus-4.6": "claude-opus-4-6", | 174 | "opus-4.6": "claude-opus-4-6", |
| 175 | "claude-opus-4.6": "claude-opus-4-6", | 175 | "claude-opus-4.6": "claude-opus-4-6", |
| 176 | "claude-opus-4-6-20251117": "claude-opus-4-6", | 176 | "claude-opus-4-6-20251117": "claude-opus-4-6", |
| 177 | sonnet: "claude-sonnet-4-6", | 177 | sonnet: "claude-sonnet-5", |
| 178 | "sonnet-5": "claude-sonnet-5", | ||
| 179 | "claude-sonnet-5.0": "claude-sonnet-5", | ||
| 180 | "claude-sonnet-5-0": "claude-sonnet-5", | ||
| 178 | "sonnet-4.6": "claude-sonnet-4-6", | 181 | "sonnet-4.6": "claude-sonnet-4-6", |
| 179 | "claude-sonnet-4.6": "claude-sonnet-4-6", | 182 | "claude-sonnet-4.6": "claude-sonnet-4-6", |
| 180 | "claude-sonnet-4-6-20251117": "claude-sonnet-4-6", | 183 | "claude-sonnet-4-6-20251117": "claude-sonnet-4-6", |
packages/shared/src/model.test.ts+1-88| ... | @@ -1,13 +1,7 @@ | ... | @@ -1,13 +1,7 @@ |
| 1 | import { describe, expect, it } from "vite-plus/test"; | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | import { | 2 | import { ProviderInstanceId, type ModelCapabilities } from "@t3tools/contracts"; |
| 3 | DEFAULT_MODEL, | ||
| 4 | ProviderDriverKind, | ||
| 5 | ProviderInstanceId, | ||
| 6 | type ModelCapabilities, | ||
| 7 | } from "@t3tools/contracts"; | ||
| 8 | 3 | ||
| 9 | import { | 4 | import { |
| 10 | applyClaudePromptEffortPrefix, | ||
| 11 | buildProviderOptionSelectionsFromDescriptors, | 5 | buildProviderOptionSelectionsFromDescriptors, |
| 12 | createModelCapabilities, | 6 | createModelCapabilities, |
| 13 | createModelSelection, | 7 | createModelSelection, |
| ... | @@ -16,11 +10,6 @@ import { | ... | @@ -16,11 +10,6 @@ import { |
| 16 | getProviderOptionDescriptors, | 10 | getProviderOptionDescriptors, |
| 17 | getProviderOptionBooleanSelectionValue, | 11 | getProviderOptionBooleanSelectionValue, |
| 18 | getProviderOptionStringSelectionValue, | 12 | getProviderOptionStringSelectionValue, |
| 19 | isClaudeUltrathinkPrompt, | ||
| 20 | normalizeModelSlug, | ||
| 21 | resolveModelSlugForProvider, | ||
| 22 | resolveSelectableModel, | ||
| 23 | trimOrNull, | ||
| 24 | } from "./model.ts"; | 13 | } from "./model.ts"; |
| 25 | 14 | ||
| 26 | const codexCaps: ModelCapabilities = createModelCapabilities({ | 15 | const codexCaps: ModelCapabilities = createModelCapabilities({ |
| ... | @@ -70,82 +59,6 @@ const claudeCaps: ModelCapabilities = createModelCapabilities({ | ... | @@ -70,82 +59,6 @@ const claudeCaps: ModelCapabilities = createModelCapabilities({ |
| 70 | ], | 59 | ], |
| 71 | }); | 60 | }); |
| 72 | 61 | ||
| 73 | describe("normalizeModelSlug", () => { | ||
| 74 | it("maps known aliases to canonical slugs", () => { | ||
| 75 | const claude = ProviderDriverKind.make("claudeAgent"); | ||
| 76 | expect(normalizeModelSlug("gpt-5-codex")).toBe("gpt-5.4"); | ||
| 77 | expect(normalizeModelSlug("5.3")).toBe("gpt-5.3-codex"); | ||
| 78 | expect(normalizeModelSlug("sonnet", claude)).toBe("claude-sonnet-4-6"); | ||
| 79 | }); | ||
| 80 | |||
| 81 | it("returns null for empty or missing values", () => { | ||
| 82 | expect(normalizeModelSlug("")).toBeNull(); | ||
| 83 | expect(normalizeModelSlug(" ")).toBeNull(); | ||
| 84 | expect(normalizeModelSlug(null)).toBeNull(); | ||
| 85 | expect(normalizeModelSlug(undefined)).toBeNull(); | ||
| 86 | }); | ||
| 87 | }); | ||
| 88 | |||
| 89 | describe("resolveModelSlugForProvider", () => { | ||
| 90 | it("returns defaults when the model is missing", () => { | ||
| 91 | expect(resolveModelSlugForProvider(ProviderDriverKind.make("codex"), undefined)).toBe( | ||
| 92 | DEFAULT_MODEL, | ||
| 93 | ); | ||
| 94 | expect(resolveModelSlugForProvider(ProviderDriverKind.make("ollama"), undefined)).toBe( | ||
| 95 | DEFAULT_MODEL, | ||
| 96 | ); | ||
| 97 | expect(resolveModelSlugForProvider(ProviderDriverKind.make("grok"), undefined)).toBe( | ||
| 98 | "grok-build", | ||
| 99 | ); | ||
| 100 | }); | ||
| 101 | |||
| 102 | it("preserves normalized unknown models", () => { | ||
| 103 | expect( | ||
| 104 | resolveModelSlugForProvider(ProviderDriverKind.make("codex"), "custom/internal-model"), | ||
| 105 | ).toBe("custom/internal-model"); | ||
| 106 | }); | ||
| 107 | }); | ||
| 108 | |||
| 109 | describe("resolveSelectableModel", () => { | ||
| 110 | it("resolves exact slugs, labels, and aliases", () => { | ||
| 111 | const options = [ | ||
| 112 | { slug: "gpt-5.3-codex", name: "GPT-5.3 Codex" }, | ||
| 113 | { slug: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, | ||
| 114 | ]; | ||
| 115 | expect(resolveSelectableModel(ProviderDriverKind.make("codex"), "gpt-5.3-codex", options)).toBe( | ||
| 116 | "gpt-5.3-codex", | ||
| 117 | ); | ||
| 118 | expect(resolveSelectableModel(ProviderDriverKind.make("codex"), "gpt-5.3 codex", options)).toBe( | ||
| 119 | "gpt-5.3-codex", | ||
| 120 | ); | ||
| 121 | expect(resolveSelectableModel(ProviderDriverKind.make("claudeAgent"), "sonnet", options)).toBe( | ||
| 122 | "claude-sonnet-4-6", | ||
| 123 | ); | ||
| 124 | }); | ||
| 125 | }); | ||
| 126 | |||
| 127 | describe("misc helpers", () => { | ||
| 128 | it("detects ultrathink prompts", () => { | ||
| 129 | expect(isClaudeUltrathinkPrompt("Please ultrathink about this")).toBe(true); | ||
| 130 | expect(isClaudeUltrathinkPrompt("Ultrathink:\nInvestigate")).toBe(true); | ||
| 131 | expect(isClaudeUltrathinkPrompt("Investigate")).toBe(false); | ||
| 132 | }); | ||
| 133 | |||
| 134 | it("prefixes ultrathink prompts once", () => { | ||
| 135 | expect(applyClaudePromptEffortPrefix("Investigate", "ultrathink")).toBe( | ||
| 136 | "Ultrathink:\nInvestigate", | ||
| 137 | ); | ||
| 138 | expect(applyClaudePromptEffortPrefix("Ultrathink:\nInvestigate", "ultrathink")).toBe( | ||
| 139 | "Ultrathink:\nInvestigate", | ||
| 140 | ); | ||
| 141 | }); | ||
| 142 | |||
| 143 | it("trims strings to null", () => { | ||
| 144 | expect(trimOrNull(" hi ")).toBe("hi"); | ||
| 145 | expect(trimOrNull(" ")).toBeNull(); | ||
| 146 | }); | ||
| 147 | }); | ||
| 148 | |||
| 149 | describe("descriptor helpers", () => { | 62 | describe("descriptor helpers", () => { |
| 150 | it("applies selection values to capability descriptors", () => { | 63 | it("applies selection values to capability descriptors", () => { |
| 151 | expect( | 64 | expect( |