mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: replace axios with fetch to respect VSCode proxy settings
- Replace axios.get with fetch in getOpenAiModels function - Replace axios.get with fetch in getLmStudioModels function - Update tests to mock fetch instead of axios - This ensures model fetching respects VSCode proxy settings like PAC files Fixes #6991
This commit is contained in:
parent
12d1959bbd
commit
61d3f94778
3 changed files with 89 additions and 57 deletions
|
|
@ -6,7 +6,6 @@ import { Anthropic } from "@anthropic-ai/sdk"
|
|||
import OpenAI from "openai"
|
||||
import { openAiModelInfoSaneDefaults } from "@roo-code/types"
|
||||
import { Package } from "../../../shared/package"
|
||||
import axios from "axios"
|
||||
|
||||
const mockCreate = vitest.fn()
|
||||
|
||||
|
|
@ -69,12 +68,9 @@ vitest.mock("openai", () => {
|
|||
}
|
||||
})
|
||||
|
||||
// Mock axios for getOpenAiModels tests
|
||||
vitest.mock("axios", () => ({
|
||||
default: {
|
||||
get: vitest.fn(),
|
||||
},
|
||||
}))
|
||||
// Mock fetch for getOpenAiModels tests
|
||||
const mockFetch = vitest.fn()
|
||||
global.fetch = mockFetch
|
||||
|
||||
describe("OpenAiHandler", () => {
|
||||
let handler: OpenAiHandler
|
||||
|
|
@ -787,80 +783,84 @@ describe("OpenAiHandler", () => {
|
|||
|
||||
describe("getOpenAiModels", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(axios.get).mockClear()
|
||||
mockFetch.mockClear()
|
||||
})
|
||||
|
||||
it("should return empty array when baseUrl is not provided", async () => {
|
||||
const result = await getOpenAiModels(undefined, "test-key")
|
||||
expect(result).toEqual([])
|
||||
expect(axios.get).not.toHaveBeenCalled()
|
||||
expect(mockFetch).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should return empty array when baseUrl is empty string", async () => {
|
||||
const result = await getOpenAiModels("", "test-key")
|
||||
expect(result).toEqual([])
|
||||
expect(axios.get).not.toHaveBeenCalled()
|
||||
expect(mockFetch).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should trim whitespace from baseUrl", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
data: [{ id: "gpt-4" }, { id: "gpt-3.5-turbo" }],
|
||||
},
|
||||
const mockResponseData = {
|
||||
data: [{ id: "gpt-4" }, { id: "gpt-3.5-turbo" }],
|
||||
}
|
||||
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponseData,
|
||||
})
|
||||
|
||||
const result = await getOpenAiModels(" https://api.openai.com/v1 ", "test-key")
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith("https://api.openai.com/v1/models", expect.any(Object))
|
||||
expect(mockFetch).toHaveBeenCalledWith("https://api.openai.com/v1/models", expect.any(Object))
|
||||
expect(result).toEqual(["gpt-4", "gpt-3.5-turbo"])
|
||||
})
|
||||
|
||||
it("should handle baseUrl with trailing spaces", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
data: [{ id: "model-1" }, { id: "model-2" }],
|
||||
},
|
||||
const mockResponseData = {
|
||||
data: [{ id: "model-1" }, { id: "model-2" }],
|
||||
}
|
||||
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponseData,
|
||||
})
|
||||
|
||||
const result = await getOpenAiModels("https://api.example.com/v1 ", "test-key")
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
|
||||
expect(mockFetch).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
|
||||
expect(result).toEqual(["model-1", "model-2"])
|
||||
})
|
||||
|
||||
it("should handle baseUrl with leading spaces", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
data: [{ id: "model-1" }],
|
||||
},
|
||||
const mockResponseData = {
|
||||
data: [{ id: "model-1" }],
|
||||
}
|
||||
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponseData,
|
||||
})
|
||||
|
||||
const result = await getOpenAiModels(" https://api.example.com/v1", "test-key")
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
|
||||
expect(mockFetch).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
|
||||
expect(result).toEqual(["model-1"])
|
||||
})
|
||||
|
||||
it("should return empty array for invalid URL after trimming", async () => {
|
||||
const result = await getOpenAiModels(" not-a-valid-url ", "test-key")
|
||||
expect(result).toEqual([])
|
||||
expect(axios.get).not.toHaveBeenCalled()
|
||||
expect(mockFetch).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should include authorization header when apiKey is provided", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
data: [{ id: "model-1" }],
|
||||
},
|
||||
const mockResponseData = {
|
||||
data: [{ id: "model-1" }],
|
||||
}
|
||||
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponseData,
|
||||
})
|
||||
|
||||
await getOpenAiModels("https://api.example.com/v1", "test-api-key")
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith(
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
"https://api.example.com/v1/models",
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
|
|
@ -871,12 +871,13 @@ describe("getOpenAiModels", () => {
|
|||
})
|
||||
|
||||
it("should include custom headers when provided", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
data: [{ id: "model-1" }],
|
||||
},
|
||||
const mockResponseData = {
|
||||
data: [{ id: "model-1" }],
|
||||
}
|
||||
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponseData,
|
||||
})
|
||||
|
||||
const customHeaders = {
|
||||
"X-Custom-Header": "custom-value",
|
||||
|
|
@ -884,7 +885,7 @@ describe("getOpenAiModels", () => {
|
|||
|
||||
await getOpenAiModels("https://api.example.com/v1", "test-key", customHeaders)
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith(
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
"https://api.example.com/v1/models",
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
|
|
@ -896,7 +897,18 @@ describe("getOpenAiModels", () => {
|
|||
})
|
||||
|
||||
it("should handle API errors gracefully", async () => {
|
||||
vi.mocked(axios.get).mockRejectedValueOnce(new Error("Network error"))
|
||||
mockFetch.mockRejectedValueOnce(new Error("Network error"))
|
||||
|
||||
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
|
||||
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
|
||||
it("should handle non-ok response", async () => {
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 401,
|
||||
})
|
||||
|
||||
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
|
||||
|
||||
|
|
@ -904,7 +916,10 @@ describe("getOpenAiModels", () => {
|
|||
})
|
||||
|
||||
it("should handle malformed response data", async () => {
|
||||
vi.mocked(axios.get).mockResolvedValueOnce({ data: null })
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => null,
|
||||
})
|
||||
|
||||
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
|
||||
|
||||
|
|
@ -912,12 +927,13 @@ describe("getOpenAiModels", () => {
|
|||
})
|
||||
|
||||
it("should deduplicate model IDs", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
data: [{ id: "gpt-4" }, { id: "gpt-4" }, { id: "gpt-3.5-turbo" }, { id: "gpt-4" }],
|
||||
},
|
||||
const mockResponseData = {
|
||||
data: [{ id: "gpt-4" }, { id: "gpt-4" }, { id: "gpt-3.5-turbo" }, { id: "gpt-4" }],
|
||||
}
|
||||
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockResponseData,
|
||||
})
|
||||
|
||||
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import OpenAI from "openai"
|
||||
import axios from "axios"
|
||||
|
||||
import { type ModelInfo, openAiModelInfoSaneDefaults, LMSTUDIO_DEFAULT_TEMPERATURE } from "@roo-code/types"
|
||||
|
||||
|
|
@ -177,8 +176,20 @@ export async function getLmStudioModels(baseUrl = "http://localhost:1234") {
|
|||
return []
|
||||
}
|
||||
|
||||
const response = await axios.get(`${baseUrl}/v1/models`)
|
||||
const modelsArray = response.data?.data?.map((model: any) => model.id) || []
|
||||
// Use fetch instead of axios to respect VSCode's proxy settings
|
||||
const response = await fetch(`${baseUrl}/v1/models`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return []
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const modelsArray = data?.data?.map((model: any) => model.id) || []
|
||||
return [...new Set<string>(modelsArray)]
|
||||
} catch (error) {
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import OpenAI, { AzureOpenAI } from "openai"
|
||||
import axios from "axios"
|
||||
|
||||
import {
|
||||
type ModelInfo,
|
||||
|
|
@ -423,7 +422,6 @@ export async function getOpenAiModels(baseUrl?: string, apiKey?: string, openAiH
|
|||
return []
|
||||
}
|
||||
|
||||
const config: Record<string, any> = {}
|
||||
const headers: Record<string, string> = {
|
||||
...DEFAULT_HEADERS,
|
||||
...(openAiHeaders || {}),
|
||||
|
|
@ -433,12 +431,19 @@ export async function getOpenAiModels(baseUrl?: string, apiKey?: string, openAiH
|
|||
headers["Authorization"] = `Bearer ${apiKey}`
|
||||
}
|
||||
|
||||
if (Object.keys(headers).length > 0) {
|
||||
config["headers"] = headers
|
||||
// Use fetch instead of axios to respect VSCode's proxy settings
|
||||
// This matches how the OpenAI SDK makes requests internally
|
||||
const response = await fetch(`${trimmedBaseUrl}/models`, {
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return []
|
||||
}
|
||||
|
||||
const response = await axios.get(`${trimmedBaseUrl}/models`, config)
|
||||
const modelsArray = response.data?.data?.map((model: any) => model.id) || []
|
||||
const data = await response.json()
|
||||
const modelsArray = data?.data?.map((model: any) => model.id) || []
|
||||
return [...new Set<string>(modelsArray)]
|
||||
} catch (error) {
|
||||
return []
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue