Skip to content

Commit 61d3f94

Browse files
committed
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
1 parent 12d1959 commit 61d3f94

File tree

3 files changed

+89
-57
lines changed

3 files changed

+89
-57
lines changed

src/api/providers/__tests__/openai.spec.ts

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { Anthropic } from "@anthropic-ai/sdk"
66
import OpenAI from "openai"
77
import { openAiModelInfoSaneDefaults } from "@roo-code/types"
88
import { Package } from "../../../shared/package"
9-
import axios from "axios"
109

1110
const mockCreate = vitest.fn()
1211

@@ -69,12 +68,9 @@ vitest.mock("openai", () => {
6968
}
7069
})
7170

72-
// Mock axios for getOpenAiModels tests
73-
vitest.mock("axios", () => ({
74-
default: {
75-
get: vitest.fn(),
76-
},
77-
}))
71+
// Mock fetch for getOpenAiModels tests
72+
const mockFetch = vitest.fn()
73+
global.fetch = mockFetch
7874

7975
describe("OpenAiHandler", () => {
8076
let handler: OpenAiHandler
@@ -787,80 +783,84 @@ describe("OpenAiHandler", () => {
787783

788784
describe("getOpenAiModels", () => {
789785
beforeEach(() => {
790-
vi.mocked(axios.get).mockClear()
786+
mockFetch.mockClear()
791787
})
792788

793789
it("should return empty array when baseUrl is not provided", async () => {
794790
const result = await getOpenAiModels(undefined, "test-key")
795791
expect(result).toEqual([])
796-
expect(axios.get).not.toHaveBeenCalled()
792+
expect(mockFetch).not.toHaveBeenCalled()
797793
})
798794

799795
it("should return empty array when baseUrl is empty string", async () => {
800796
const result = await getOpenAiModels("", "test-key")
801797
expect(result).toEqual([])
802-
expect(axios.get).not.toHaveBeenCalled()
798+
expect(mockFetch).not.toHaveBeenCalled()
803799
})
804800

805801
it("should trim whitespace from baseUrl", async () => {
806-
const mockResponse = {
807-
data: {
808-
data: [{ id: "gpt-4" }, { id: "gpt-3.5-turbo" }],
809-
},
802+
const mockResponseData = {
803+
data: [{ id: "gpt-4" }, { id: "gpt-3.5-turbo" }],
810804
}
811-
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
805+
mockFetch.mockResolvedValueOnce({
806+
ok: true,
807+
json: async () => mockResponseData,
808+
})
812809

813810
const result = await getOpenAiModels(" https://api.openai.com/v1 ", "test-key")
814811

815-
expect(axios.get).toHaveBeenCalledWith("https://api.openai.com/v1/models", expect.any(Object))
812+
expect(mockFetch).toHaveBeenCalledWith("https://api.openai.com/v1/models", expect.any(Object))
816813
expect(result).toEqual(["gpt-4", "gpt-3.5-turbo"])
817814
})
818815

819816
it("should handle baseUrl with trailing spaces", async () => {
820-
const mockResponse = {
821-
data: {
822-
data: [{ id: "model-1" }, { id: "model-2" }],
823-
},
817+
const mockResponseData = {
818+
data: [{ id: "model-1" }, { id: "model-2" }],
824819
}
825-
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
820+
mockFetch.mockResolvedValueOnce({
821+
ok: true,
822+
json: async () => mockResponseData,
823+
})
826824

827825
const result = await getOpenAiModels("https://api.example.com/v1 ", "test-key")
828826

829-
expect(axios.get).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
827+
expect(mockFetch).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
830828
expect(result).toEqual(["model-1", "model-2"])
831829
})
832830

833831
it("should handle baseUrl with leading spaces", async () => {
834-
const mockResponse = {
835-
data: {
836-
data: [{ id: "model-1" }],
837-
},
832+
const mockResponseData = {
833+
data: [{ id: "model-1" }],
838834
}
839-
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
835+
mockFetch.mockResolvedValueOnce({
836+
ok: true,
837+
json: async () => mockResponseData,
838+
})
840839

841840
const result = await getOpenAiModels(" https://api.example.com/v1", "test-key")
842841

843-
expect(axios.get).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
842+
expect(mockFetch).toHaveBeenCalledWith("https://api.example.com/v1/models", expect.any(Object))
844843
expect(result).toEqual(["model-1"])
845844
})
846845

847846
it("should return empty array for invalid URL after trimming", async () => {
848847
const result = await getOpenAiModels(" not-a-valid-url ", "test-key")
849848
expect(result).toEqual([])
850-
expect(axios.get).not.toHaveBeenCalled()
849+
expect(mockFetch).not.toHaveBeenCalled()
851850
})
852851

853852
it("should include authorization header when apiKey is provided", async () => {
854-
const mockResponse = {
855-
data: {
856-
data: [{ id: "model-1" }],
857-
},
853+
const mockResponseData = {
854+
data: [{ id: "model-1" }],
858855
}
859-
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
856+
mockFetch.mockResolvedValueOnce({
857+
ok: true,
858+
json: async () => mockResponseData,
859+
})
860860

861861
await getOpenAiModels("https://api.example.com/v1", "test-api-key")
862862

863-
expect(axios.get).toHaveBeenCalledWith(
863+
expect(mockFetch).toHaveBeenCalledWith(
864864
"https://api.example.com/v1/models",
865865
expect.objectContaining({
866866
headers: expect.objectContaining({
@@ -871,20 +871,21 @@ describe("getOpenAiModels", () => {
871871
})
872872

873873
it("should include custom headers when provided", async () => {
874-
const mockResponse = {
875-
data: {
876-
data: [{ id: "model-1" }],
877-
},
874+
const mockResponseData = {
875+
data: [{ id: "model-1" }],
878876
}
879-
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
877+
mockFetch.mockResolvedValueOnce({
878+
ok: true,
879+
json: async () => mockResponseData,
880+
})
880881

881882
const customHeaders = {
882883
"X-Custom-Header": "custom-value",
883884
}
884885

885886
await getOpenAiModels("https://api.example.com/v1", "test-key", customHeaders)
886887

887-
expect(axios.get).toHaveBeenCalledWith(
888+
expect(mockFetch).toHaveBeenCalledWith(
888889
"https://api.example.com/v1/models",
889890
expect.objectContaining({
890891
headers: expect.objectContaining({
@@ -896,28 +897,43 @@ describe("getOpenAiModels", () => {
896897
})
897898

898899
it("should handle API errors gracefully", async () => {
899-
vi.mocked(axios.get).mockRejectedValueOnce(new Error("Network error"))
900+
mockFetch.mockRejectedValueOnce(new Error("Network error"))
901+
902+
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
903+
904+
expect(result).toEqual([])
905+
})
906+
907+
it("should handle non-ok response", async () => {
908+
mockFetch.mockResolvedValueOnce({
909+
ok: false,
910+
status: 401,
911+
})
900912

901913
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
902914

903915
expect(result).toEqual([])
904916
})
905917

906918
it("should handle malformed response data", async () => {
907-
vi.mocked(axios.get).mockResolvedValueOnce({ data: null })
919+
mockFetch.mockResolvedValueOnce({
920+
ok: true,
921+
json: async () => null,
922+
})
908923

909924
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
910925

911926
expect(result).toEqual([])
912927
})
913928

914929
it("should deduplicate model IDs", async () => {
915-
const mockResponse = {
916-
data: {
917-
data: [{ id: "gpt-4" }, { id: "gpt-4" }, { id: "gpt-3.5-turbo" }, { id: "gpt-4" }],
918-
},
930+
const mockResponseData = {
931+
data: [{ id: "gpt-4" }, { id: "gpt-4" }, { id: "gpt-3.5-turbo" }, { id: "gpt-4" }],
919932
}
920-
vi.mocked(axios.get).mockResolvedValueOnce(mockResponse)
933+
mockFetch.mockResolvedValueOnce({
934+
ok: true,
935+
json: async () => mockResponseData,
936+
})
921937

922938
const result = await getOpenAiModels("https://api.example.com/v1", "test-key")
923939

src/api/providers/lm-studio.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Anthropic } from "@anthropic-ai/sdk"
22
import OpenAI from "openai"
3-
import axios from "axios"
43

54
import { type ModelInfo, openAiModelInfoSaneDefaults, LMSTUDIO_DEFAULT_TEMPERATURE } from "@roo-code/types"
65

@@ -177,8 +176,20 @@ export async function getLmStudioModels(baseUrl = "http://localhost:1234") {
177176
return []
178177
}
179178

180-
const response = await axios.get(`${baseUrl}/v1/models`)
181-
const modelsArray = response.data?.data?.map((model: any) => model.id) || []
179+
// Use fetch instead of axios to respect VSCode's proxy settings
180+
const response = await fetch(`${baseUrl}/v1/models`, {
181+
method: "GET",
182+
headers: {
183+
"Content-Type": "application/json",
184+
},
185+
})
186+
187+
if (!response.ok) {
188+
return []
189+
}
190+
191+
const data = await response.json()
192+
const modelsArray = data?.data?.map((model: any) => model.id) || []
182193
return [...new Set<string>(modelsArray)]
183194
} catch (error) {
184195
return []

src/api/providers/openai.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Anthropic } from "@anthropic-ai/sdk"
22
import OpenAI, { AzureOpenAI } from "openai"
3-
import axios from "axios"
43

54
import {
65
type ModelInfo,
@@ -423,7 +422,6 @@ export async function getOpenAiModels(baseUrl?: string, apiKey?: string, openAiH
423422
return []
424423
}
425424

426-
const config: Record<string, any> = {}
427425
const headers: Record<string, string> = {
428426
...DEFAULT_HEADERS,
429427
...(openAiHeaders || {}),
@@ -433,12 +431,19 @@ export async function getOpenAiModels(baseUrl?: string, apiKey?: string, openAiH
433431
headers["Authorization"] = `Bearer ${apiKey}`
434432
}
435433

436-
if (Object.keys(headers).length > 0) {
437-
config["headers"] = headers
434+
// Use fetch instead of axios to respect VSCode's proxy settings
435+
// This matches how the OpenAI SDK makes requests internally
436+
const response = await fetch(`${trimmedBaseUrl}/models`, {
437+
method: "GET",
438+
headers: headers,
439+
})
440+
441+
if (!response.ok) {
442+
return []
438443
}
439444

440-
const response = await axios.get(`${trimmedBaseUrl}/models`, config)
441-
const modelsArray = response.data?.data?.map((model: any) => model.id) || []
445+
const data = await response.json()
446+
const modelsArray = data?.data?.map((model: any) => model.id) || []
442447
return [...new Set<string>(modelsArray)]
443448
} catch (error) {
444449
return []

0 commit comments

Comments
 (0)