Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/calm-forks-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Normalize legacy Claude Code model IDs so dated aliases resolve to canonical model metadata and preserve capabilities (including image support).
12 changes: 12 additions & 0 deletions src/api/providers/__tests__/claude-code.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ describe("ClaudeCodeHandler", () => {
expect(model.id).toBe("claude-sonnet-4-5") // default model
})

test("should normalize legacy dated model ids to canonical claude-code models", () => {
const options: ApiHandlerOptions = {
apiModelId: "claude-opus-4-5-20251101",
}
const handlerWithLegacyModel = new ClaudeCodeHandler(options)
const model = handlerWithLegacyModel.getModel()

expect(model.id).toBe("claude-opus-4-5")
expect(model.info.supportsImages).toBe(true)
expect(model.info.supportsPromptCache).toBe(true)
})

test("should return model maxTokens from model definition", () => {
const options: ApiHandlerOptions = {
apiModelId: "claude-opus-4-5",
Expand Down
7 changes: 4 additions & 3 deletions src/api/providers/claude-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
claudeCodeReasoningConfig,
type ClaudeCodeReasoningLevel,
type ModelInfo,
normalizeClaudeCodeModelId,
} from "@roo-code/types"
import { type ApiHandler, ApiHandlerCreateMessageMetadata, type SingleCompletionHandler } from ".."
import { ApiStreamUsageChunk, type ApiStream } from "../transform/stream"
Expand Down Expand Up @@ -303,9 +304,9 @@ export class ClaudeCodeHandler implements ApiHandler, SingleCompletionHandler {
}

getModel(): { id: string; info: ModelInfo } {
const modelId = this.options.apiModelId
if (modelId && Object.hasOwn(claudeCodeModels, modelId)) {
const id = modelId as ClaudeCodeModelId
const modelId = this.options.apiModelId?.trim()
if (modelId) {
const id = normalizeClaudeCodeModelId(modelId) as ClaudeCodeModelId
return { id, info: { ...claudeCodeModels[id] } }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,39 @@ describe("useSelectedModel", () => {
expect(result.current.info).toBeDefined()
expect(result.current.info?.supportsImages).toBe(true) // Claude Code now supports images
})

it("should normalize dated claude-code model ids and keep image support enabled", () => {
mockUseRouterModels.mockReturnValue({
data: {
openrouter: {},
requesty: {},
glama: {},
unbound: {},
litellm: {},
"io-intelligence": {},
},
isLoading: false,
isError: false,
} as any)

mockUseOpenRouterModelProviders.mockReturnValue({
data: {},
isLoading: false,
isError: false,
} as any)

const apiConfiguration: ProviderSettings = {
apiProvider: "claude-code",
apiModelId: "claude-opus-4-5-20251101",
}

const wrapper = createWrapper()
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })

expect(result.current.provider).toBe("claude-code")
expect(result.current.id).toBe("claude-opus-4-5")
expect(result.current.info?.supportsImages).toBe(true)
})
})

// kilocode_change start
Expand Down
Loading