From 334c698da4bac9738020e6d2bb048541b4d21e3a Mon Sep 17 00:00:00 2001 From: hannesrudolph Date: Tue, 24 Jun 2025 23:21:36 -0600 Subject: [PATCH 1/3] fix: resolve claude-code provider image hang (#5100) - Add missing claude-code case in useSelectedModel hook - Ensures proper model info is returned with supportsImages: false - Prevents UI from allowing image uploads to claude-code provider - Fixes application hang when attempting to send images to claude-code --- webview-ui/src/components/ui/hooks/useSelectedModel.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 09cae03e5d..1a0fcdd796 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -30,6 +30,8 @@ import { glamaDefaultModelId, unboundDefaultModelId, litellmDefaultModelId, + claudeCodeDefaultModelId, + claudeCodeModels, } from "@roo-code/types" import type { RouterModels } from "@roo/api" @@ -199,6 +201,11 @@ function getSelectedModel({ const info = vscodeLlmModels[modelFamily as keyof typeof vscodeLlmModels] return { id, info: { ...openAiModelInfoSaneDefaults, ...info, supportsImages: false } } // VSCode LM API currently doesn't support images. } + case "claude-code": { + const id = apiConfiguration.apiModelId ?? claudeCodeDefaultModelId + const info = claudeCodeModels[id as keyof typeof claudeCodeModels] + return { id, info } + } // case "anthropic": // case "human-relay": // case "fake-ai": From 552e04ecb746dd74936bd4672608c5452dc8d14d Mon Sep 17 00:00:00 2001 From: hannesrudolph Date: Wed, 25 Jun 2025 00:23:59 -0600 Subject: [PATCH 2/3] test: add test coverage for claude-code provider - Add comprehensive test cases for claude-code provider in useSelectedModel hook - Verify that claude-code models correctly report supportsImages: false - Test default model selection when no modelId is specified - Add inline documentation explaining claude-code's disabled features --- .../hooks/__tests__/useSelectedModel.spec.ts | 72 +++++++++++++++++++ .../components/ui/hooks/useSelectedModel.ts | 1 + 2 files changed, 73 insertions(+) diff --git a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts index 0d58268d14..fd5950bf35 100644 --- a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts +++ b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts @@ -369,4 +369,76 @@ describe("useSelectedModel", () => { expect(result.current.info).toBeUndefined() }) }) + + describe("claude-code provider", () => { + it("should return claude-code model with supportsImages disabled", () => { + mockUseRouterModels.mockReturnValue({ + data: { + openrouter: {}, + requesty: {}, + glama: {}, + unbound: {}, + litellm: {}, + }, + isLoading: false, + isError: false, + } as any) + + mockUseOpenRouterModelProviders.mockReturnValue({ + data: {}, + isLoading: false, + isError: false, + } as any) + + const apiConfiguration: ProviderSettings = { + apiProvider: "claude-code", + apiModelId: "claude-sonnet-4-20250514", + } + + const wrapper = createWrapper() + const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper }) + + expect(result.current.provider).toBe("claude-code") + expect(result.current.id).toBe("claude-sonnet-4-20250514") + expect(result.current.info).toBeDefined() + expect(result.current.info?.supportsImages).toBe(false) + expect(result.current.info?.supportsPromptCache).toBe(false) + // Verify it inherits other properties from anthropic models + expect(result.current.info?.maxTokens).toBe(64_000) + expect(result.current.info?.contextWindow).toBe(200_000) + expect(result.current.info?.supportsComputerUse).toBe(true) + }) + + it("should use default claude-code model when no modelId is specified", () => { + mockUseRouterModels.mockReturnValue({ + data: { + openrouter: {}, + requesty: {}, + glama: {}, + unbound: {}, + litellm: {}, + }, + isLoading: false, + isError: false, + } as any) + + mockUseOpenRouterModelProviders.mockReturnValue({ + data: {}, + isLoading: false, + isError: false, + } as any) + + const apiConfiguration: ProviderSettings = { + apiProvider: "claude-code", + } + + const wrapper = createWrapper() + const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper }) + + expect(result.current.provider).toBe("claude-code") + expect(result.current.id).toBe("claude-sonnet-4-20250514") // Default model + expect(result.current.info).toBeDefined() + expect(result.current.info?.supportsImages).toBe(false) + }) + }) }) diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 1a0fcdd796..13e1f49dab 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -202,6 +202,7 @@ function getSelectedModel({ return { id, info: { ...openAiModelInfoSaneDefaults, ...info, supportsImages: false } } // VSCode LM API currently doesn't support images. } case "claude-code": { + // Claude Code models extend anthropic models but with images and prompt caching disabled const id = apiConfiguration.apiModelId ?? claudeCodeDefaultModelId const info = claudeCodeModels[id as keyof typeof claudeCodeModels] return { id, info } From 2bd3b910372aaecfac24109e45afb280ed00a4b4 Mon Sep 17 00:00:00 2001 From: Daniel <57051444+daniel-lxs@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:26:32 -0500 Subject: [PATCH 3/3] Update webview-ui/src/components/ui/hooks/useSelectedModel.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- webview-ui/src/components/ui/hooks/useSelectedModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 13e1f49dab..40c1ff2431 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -205,7 +205,7 @@ function getSelectedModel({ // Claude Code models extend anthropic models but with images and prompt caching disabled const id = apiConfiguration.apiModelId ?? claudeCodeDefaultModelId const info = claudeCodeModels[id as keyof typeof claudeCodeModels] - return { id, info } + return { id, info: { ...openAiModelInfoSaneDefaults, ...info } } } // case "anthropic": // case "human-relay":