Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 1 commit
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
85 changes: 84 additions & 1 deletion packages/types/src/__tests__/provider-settings.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,87 @@
import { getApiProtocol } from "../provider-settings.js"
import { getApiProtocol, providerSettingsSchema } from "../provider-settings.js"

describe("Ollama Settings Schema", () => {
it("should accept valid ollamaRequestTimeout", () => {
const result = providerSettingsSchema.safeParse({
ollamaRequestTimeout: 3600000,
})
expect(result.success).toBe(true)
})

it("should reject ollamaRequestTimeout below minimum", () => {
const result = providerSettingsSchema.safeParse({
ollamaRequestTimeout: 500,
})
expect(result.success).toBe(false)
})

it("should reject ollamaRequestTimeout above maximum", () => {
const result = providerSettingsSchema.safeParse({
ollamaRequestTimeout: 8000000,
})
expect(result.success).toBe(false)
})

it("should accept valid ollamaModelDiscoveryTimeout", () => {
const result = providerSettingsSchema.safeParse({
ollamaModelDiscoveryTimeout: 10000,
})
expect(result.success).toBe(true)
})

it("should reject ollamaModelDiscoveryTimeout above maximum", () => {
const result = providerSettingsSchema.safeParse({
ollamaModelDiscoveryTimeout: 700000,
})
expect(result.success).toBe(false)
})

it("should accept valid ollamaMaxRetries", () => {
const result = providerSettingsSchema.safeParse({
ollamaMaxRetries: 3,
})
expect(result.success).toBe(true)
})

it("should reject ollamaMaxRetries above maximum", () => {
const result = providerSettingsSchema.safeParse({
ollamaMaxRetries: 15,
})
expect(result.success).toBe(false)
})

it("should accept valid ollamaRetryDelay", () => {
const result = providerSettingsSchema.safeParse({
ollamaRetryDelay: 2000,
})
expect(result.success).toBe(true)
})

it("should reject ollamaRetryDelay below minimum", () => {
const result = providerSettingsSchema.safeParse({
ollamaRetryDelay: 50,
})
expect(result.success).toBe(false)
})

it("should accept ollamaEnableLogging boolean", () => {
const result = providerSettingsSchema.safeParse({
ollamaEnableLogging: true,
})
expect(result.success).toBe(true)
})

it("should accept all optional fields together", () => {
const result = providerSettingsSchema.safeParse({
ollamaRequestTimeout: 3600000,
ollamaModelDiscoveryTimeout: 10000,
ollamaMaxRetries: 2,
ollamaRetryDelay: 1000,
ollamaEnableLogging: true,
})
expect(result.success).toBe(true)
})
})

describe("getApiProtocol", () => {
describe("Anthropic-style providers", () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ const ollamaSchema = baseProviderSettingsSchema.extend({
ollamaBaseUrl: z.string().optional(),
ollamaApiKey: z.string().optional(),
ollamaNumCtx: z.number().int().min(128).optional(),
ollamaRequestTimeout: z.number().int().min(1000).max(7200000).optional(),
ollamaModelDiscoveryTimeout: z.number().int().min(1000).max(600000).optional(),
ollamaMaxRetries: z.number().int().min(0).max(10).optional(),
ollamaRetryDelay: z.number().int().min(100).max(10000).optional(),
ollamaEnableLogging: z.boolean().optional(),
})

const vsCodeLmSchema = baseProviderSettingsSchema.extend({
Expand Down
16 changes: 16 additions & 0 deletions packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export interface ExtensionMessage {
| "routerModels"
| "openAiModels"
| "ollamaModels"
| "ollamaConnectionTestResult"
| "ollamaModelsRefreshResult"
| "lmStudioModels"
| "vsCodeLmModels"
| "huggingFaceModels"
Expand Down Expand Up @@ -124,6 +126,18 @@ export interface ExtensionMessage {
routerModels?: RouterModels
openAiModels?: string[]
ollamaModels?: ModelRecord
ollamaModelsWithTools?: Array<{
name: string
contextWindow: number
size?: number
quantizationLevel?: string
family?: string
supportsImages: boolean
modelInfo: ModelRecord[string]
}>
modelsWithoutTools?: string[]
message?: string
durationMs?: number
lmStudioModels?: ModelRecord
vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[]
huggingFaceModels?: Array<{
Expand Down Expand Up @@ -393,6 +407,8 @@ export interface WebviewMessage {
| "requestRouterModels"
| "requestOpenAiModels"
| "requestOllamaModels"
| "testOllamaConnection"
| "refreshOllamaModels"
| "requestLmStudioModels"
| "requestRooModels"
| "requestRooCreditBalance"
Expand Down
101 changes: 101 additions & 0 deletions src/api/providers/fetchers/__tests__/ollama-axios-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { describe, it, expect, vi, beforeEach } from "vitest"
import axios from "axios"
import { createOllamaAxiosInstance } from "../ollama"
import type { AxiosInstance, AxiosError } from "axios"

vi.mock("axios")

const mockAxiosInstance = {
interceptors: {
request: { use: vi.fn() },
response: { use: vi.fn() },
},
get: vi.fn(),
post: vi.fn(),
} as unknown as AxiosInstance

describe("createOllamaAxiosInstance", () => {
beforeEach(() => {
vi.clearAllMocks()
vi.mocked(axios.create).mockReturnValue(mockAxiosInstance)
})

it("should create instance with default configuration", () => {
const instance = createOllamaAxiosInstance()
expect(instance).toBeDefined()
expect(axios.create).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: "http://localhost:11434",
timeout: 3600000,
}),
)
})

it("should create instance with custom baseUrl", () => {
createOllamaAxiosInstance({ baseUrl: "http://custom:11434" })
expect(axios.create).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: "http://custom:11434",
}),
)
})

it("should include Authorization header when apiKey provided", () => {
createOllamaAxiosInstance({ apiKey: "test-key" })
expect(axios.create).toHaveBeenCalledWith(
expect.objectContaining({
headers: {
Authorization: "Bearer test-key",
},
}),
)
})

it("should not include Authorization header when apiKey not provided", () => {
createOllamaAxiosInstance()
expect(axios.create).toHaveBeenCalledWith(
expect.objectContaining({
headers: {},
}),
)
})

it("should set up retry interceptor when retries > 0", () => {
createOllamaAxiosInstance({ retries: 2, retryDelay: 1000 })
expect(mockAxiosInstance.interceptors.response.use).toHaveBeenCalled()
})

it("should not set up retry interceptor when retries = 0", () => {
createOllamaAxiosInstance({ retries: 0 })
expect(mockAxiosInstance.interceptors.response.use).not.toHaveBeenCalled()
})

it("should set up logging interceptor when enableLogging is true", () => {
createOllamaAxiosInstance({ enableLogging: true })
expect(mockAxiosInstance.interceptors.request.use).toHaveBeenCalled()
expect(mockAxiosInstance.interceptors.response.use).toHaveBeenCalled()
})

it("should not set up logging interceptor when enableLogging is false", () => {
createOllamaAxiosInstance({ enableLogging: false })
expect(mockAxiosInstance.interceptors.request.use).not.toHaveBeenCalled()
})

it("should use custom timeout", () => {
createOllamaAxiosInstance({ timeout: 5000 })
expect(axios.create).toHaveBeenCalledWith(
expect.objectContaining({
timeout: 5000,
}),
)
})

it("should set timeout error message", () => {
createOllamaAxiosInstance({ timeout: 10000 })
expect(axios.create).toHaveBeenCalledWith(
expect.objectContaining({
timeoutErrorMessage: "Ollama request timed out after 10000ms",
}),
)
})
})
Loading