fix: minor fixes and new cases for webView/ClineProvider unit test, remove outdated lm-studio fetcher

This commit is contained in:
Piotr Wilkin 2025-07-17 16:24:38 +02:00
parent 61d43876f7
commit 47235e0b00
5 changed files with 44 additions and 56 deletions

View file

@ -1,35 +0,0 @@
import axios from "axios"
import { ModelRecord } from "../../../shared/api"
import { openAiModelInfoSaneDefaults } from "@roo-code/types"
export async function getLmStudioModels(baseUrl = "http://localhost:1234"): Promise<ModelRecord> {
try {
if (!URL.canParse(baseUrl)) {
return {}
}
const response = await axios.get(`${baseUrl}/api/v0/models`)
return response.data?.data?.reduce((acc: ModelRecord, model: any) => {
acc[model.id] = {
maxTokens:
model.loaded_context_length ||
model.max_context_length ||
openAiModelInfoSaneDefaults.contextWindow,
contextWindow:
model.loaded_context_length ||
model.max_context_length ||
openAiModelInfoSaneDefaults.contextWindow,
supportsImages: false,
supportsPromptCache: false,
supportsComputerUse: false,
inputPrice: 0,
outputPrice: 0,
cacheWritesPrice: 0,
cacheReadsPrice: 0,
}
return acc
}, {})
} catch (error) {
return {}
}
}

View file

@ -1,23 +1,22 @@
import * as path from "path"
import fs from "fs/promises"
import * as path from "path"
import NodeCache from "node-cache"
import { safeWriteJson } from "../../../utils/safeWriteJson"
import { ContextProxy } from "../../../core/config/ContextProxy"
import { getCacheDirectoryPath } from "../../../utils/storage"
import { RouterName, ModelRecord } from "../../../shared/api"
import { ModelRecord, RouterName } from "../../../shared/api"
import { fileExistsAtPath } from "../../../utils/fs"
import { getCacheDirectoryPath } from "../../../utils/storage"
import { GetModelsOptions } from "../../../shared/api"
import { getGlamaModels } from "./glama"
import { getLiteLLMModels } from "./litellm"
import { getLMStudioModels } from "./lmstudio"
import { getOllamaModels } from "./ollama"
import { getOpenRouterModels } from "./openrouter"
import { getRequestyModels } from "./requesty"
import { getGlamaModels } from "./glama"
import { getUnboundModels } from "./unbound"
import { getLiteLLMModels } from "./litellm"
import { getLmStudioModels } from "./lm-studio"
import { GetModelsOptions } from "../../../shared/api"
import { getOllamaModels } from "./ollama"
import { getLMStudioModels } from "./lmstudio"
const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 })

View file

@ -16,6 +16,7 @@ import { Task, TaskOptions } from "../../task/Task"
import { safeWriteJson } from "../../../utils/safeWriteJson"
import { ClineProvider } from "../ClineProvider"
import { LmStudioHandler } from "../../../api/providers"
// Mock setup must come before imports
vi.mock("../../prompts/sections/custom-instructions")
@ -2409,6 +2410,7 @@ describe("ClineProvider - Router Models", () => {
unboundApiKey: "unbound-key",
litellmApiKey: "litellm-key",
litellmBaseUrl: "http://localhost:4000",
lmStudioBaseUrl: "http://localhost:1234",
},
} as any)
@ -2442,6 +2444,10 @@ describe("ClineProvider - Router Models", () => {
apiKey: "litellm-key",
baseUrl: "http://localhost:4000",
})
expect(getModels).toHaveBeenCalledWith({
provider: "lmstudio",
baseUrl: "http://localhost:1234",
})
// Verify response was sent
expect(mockPostMessage).toHaveBeenCalledWith({
@ -2453,7 +2459,7 @@ describe("ClineProvider - Router Models", () => {
unbound: mockModels,
litellm: mockModels,
ollama: {},
lmstudio: {},
lmstudio: mockModels,
},
})
})
@ -2470,6 +2476,7 @@ describe("ClineProvider - Router Models", () => {
unboundApiKey: "unbound-key",
litellmApiKey: "litellm-key",
litellmBaseUrl: "http://localhost:4000",
lmStudioBaseUrl: "http://localhost:1234",
},
} as any)
@ -2485,6 +2492,7 @@ describe("ClineProvider - Router Models", () => {
.mockResolvedValueOnce(mockModels) // glama success
.mockRejectedValueOnce(new Error("Unbound API error")) // unbound fail
.mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm fail
.mockRejectedValueOnce(new Error("LMStudio API error")) // lmstudio fail
await messageHandler({ type: "requestRouterModels" })
@ -2530,6 +2538,13 @@ describe("ClineProvider - Router Models", () => {
error: "LiteLLM connection failed",
values: { provider: "litellm" },
})
expect(mockPostMessage).toHaveBeenCalledWith({
type: "singleRouterModelFetchResponse",
success: false,
error: "LMStudio API error",
values: { provider: "lmstudio" },
})
})
test("handles requestRouterModels with LiteLLM values from message", async () => {
@ -2608,7 +2623,7 @@ describe("ClineProvider - Router Models", () => {
unbound: mockModels,
litellm: {},
ollama: {},
lmstudio: {},
lmstudio: mockModels,
},
})
})

View file

@ -101,6 +101,7 @@ describe("webviewMessageHandler - requestRouterModels", () => {
unboundApiKey: "unbound-key",
litellmApiKey: "litellm-key",
litellmBaseUrl: "http://localhost:4000",
lmStudioBaseUrl: "http://localhost:1234",
},
})
})
@ -137,6 +138,10 @@ describe("webviewMessageHandler - requestRouterModels", () => {
apiKey: "litellm-key",
baseUrl: "http://localhost:4000",
})
expect(mockGetModels).toHaveBeenCalledWith({
provider: "lmstudio",
baseUrl: "http://localhost:1234",
})
// Verify response was sent
expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
@ -148,7 +153,7 @@ describe("webviewMessageHandler - requestRouterModels", () => {
unbound: mockModels,
litellm: mockModels,
ollama: {},
lmstudio: {},
lmstudio: mockModels,
},
})
})
@ -235,7 +240,7 @@ describe("webviewMessageHandler - requestRouterModels", () => {
unbound: mockModels,
litellm: {},
ollama: {},
lmstudio: {},
lmstudio: mockModels,
},
})
})
@ -257,6 +262,7 @@ describe("webviewMessageHandler - requestRouterModels", () => {
.mockResolvedValueOnce(mockModels) // glama
.mockRejectedValueOnce(new Error("Unbound API error")) // unbound
.mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm
.mockRejectedValueOnce(new Error("LMStudio API error")) // lmstudio"))
await webviewMessageHandler(mockClineProvider, {
type: "requestRouterModels",
@ -307,6 +313,7 @@ describe("webviewMessageHandler - requestRouterModels", () => {
.mockRejectedValueOnce(new Error("Glama API error")) // glama
.mockRejectedValueOnce(new Error("Unbound API error")) // unbound
.mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm
.mockRejectedValueOnce(new Error("LMStudio API error")) // lmstudio
await webviewMessageHandler(mockClineProvider, {
type: "requestRouterModels",
@ -347,6 +354,13 @@ describe("webviewMessageHandler - requestRouterModels", () => {
error: "LiteLLM connection failed",
values: { provider: "litellm" },
})
expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
type: "singleRouterModelFetchResponse",
success: false,
error: "LMStudio API error",
values: { provider: "lmstudio" },
})
})
it("prefers config values over message values for LiteLLM", async () => {

View file

@ -605,9 +605,8 @@ export const webviewMessageHandler = async (
const fetchedRouterModels: Partial<Record<RouterName, ModelRecord>> = {
...routerModels,
// Initialize ollama and lmstudio with empty objects since they use separate handlers
// Initialize ollama with empty objects since it uses separate handlers
ollama: {},
lmstudio: {},
}
results.forEach((result, index) => {
@ -616,18 +615,14 @@ export const webviewMessageHandler = async (
if (result.status === "fulfilled") {
fetchedRouterModels[routerName] = result.value.models
// Ollama and LM Studio settings pages still need these events
// Ollama settings pages still need these events
if (routerName === "ollama" && Object.keys(result.value.models).length > 0) {
provider.postMessageToWebview({
type: "ollamaModels",
ollamaModels: Object.keys(result.value.models),
})
} else if (routerName === "lmstudio" && Object.keys(result.value.models).length > 0) {
provider.postMessageToWebview({
type: "lmStudioModels",
lmStudioModels: Object.keys(result.value.models),
})
}
// LM Studio models have moved to main router models message
} else {
// Handle rejection: Post a specific error message for this provider
const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason)