Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit 9869b06

Browse files
committed
fix(ollama): add logging and diagnostics for debugging Ollama connection issues
- Add logging when models are filtered out due to missing tool support - Add warning when selected model is not in the tool-capable models list - Update parseOllamaModel to return filteredReason for better diagnostics - Log request info (model ID, base URL) when starting Ollama requests This helps diagnose issues like #11049 where Ollama models appear unresponsive. The logging output will show: - Which models are being filtered and why - When a model may not support native tool calling - Request details for debugging connectivity issues
1 parent fe722da commit 9869b06

3 files changed

Lines changed: 63 additions & 25 deletions

File tree

src/api/providers/fetchers/__tests__/ollama.test.ts

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ describe("Ollama Fetcher", () => {
1515
describe("parseOllamaModel", () => {
1616
it("should correctly parse Ollama model info", () => {
1717
const modelData = ollamaModelsData["qwen3-2to16:latest"]
18-
const parsedModel = parseOllamaModel(modelData)
18+
const { modelInfo } = parseOllamaModel(modelData)
1919

20-
expect(parsedModel).toEqual({
20+
expect(modelInfo).toEqual({
2121
maxTokens: 40960,
2222
contextWindow: 40960,
2323
supportsImages: false,
@@ -39,9 +39,9 @@ describe("Ollama Fetcher", () => {
3939
},
4040
}
4141

42-
const parsedModel = parseOllamaModel(modelDataWithNullFamilies as any)
42+
const { modelInfo } = parseOllamaModel(modelDataWithNullFamilies as any)
4343

44-
expect(parsedModel).toEqual({
44+
expect(modelInfo).toEqual({
4545
maxTokens: 40960,
4646
contextWindow: 40960,
4747
supportsImages: false,
@@ -54,16 +54,18 @@ describe("Ollama Fetcher", () => {
5454
})
5555
})
5656

57-
it("should return null when capabilities does not include 'tools'", () => {
57+
it("should return null with reason when capabilities does not include 'tools'", () => {
5858
const modelDataWithoutTools = {
5959
...ollamaModelsData["qwen3-2to16:latest"],
6060
capabilities: ["completion"], // No "tools" capability
6161
}
6262

63-
const parsedModel = parseOllamaModel(modelDataWithoutTools as any)
63+
const { modelInfo, filteredReason } = parseOllamaModel(modelDataWithoutTools as any, "test-model")
6464

6565
// Models without tools capability are filtered out (return null)
66-
expect(parsedModel).toBeNull()
66+
expect(modelInfo).toBeNull()
67+
expect(filteredReason).toContain("test-model")
68+
expect(filteredReason).toContain("do not include 'tools'")
6769
})
6870

6971
it("should return model info when capabilities includes 'tools'", () => {
@@ -72,22 +74,25 @@ describe("Ollama Fetcher", () => {
7274
capabilities: ["completion", "tools"], // Has "tools" capability
7375
}
7476

75-
const parsedModel = parseOllamaModel(modelDataWithTools as any)
77+
const { modelInfo, filteredReason } = parseOllamaModel(modelDataWithTools as any)
7678

77-
expect(parsedModel).not.toBeNull()
78-
expect(parsedModel!.contextWindow).toBeGreaterThan(0)
79+
expect(modelInfo).not.toBeNull()
80+
expect(modelInfo!.contextWindow).toBeGreaterThan(0)
81+
expect(filteredReason).toBeUndefined()
7982
})
8083

81-
it("should return null when capabilities is undefined (no tool support)", () => {
84+
it("should return null with reason when capabilities is undefined (no tool support)", () => {
8285
const modelDataWithoutCapabilities = {
8386
...ollamaModelsData["qwen3-2to16:latest"],
8487
capabilities: undefined, // No capabilities array
8588
}
8689

87-
const parsedModel = parseOllamaModel(modelDataWithoutCapabilities as any)
90+
const { modelInfo, filteredReason } = parseOllamaModel(modelDataWithoutCapabilities as any, "test-model")
8891

8992
// Models without explicit tools capability are filtered out
90-
expect(parsedModel).toBeNull()
93+
expect(modelInfo).toBeNull()
94+
expect(filteredReason).toContain("test-model")
95+
expect(filteredReason).toContain("no capabilities reported")
9196
})
9297

9398
it("should return null when model has vision but no tools capability", () => {
@@ -96,10 +101,10 @@ describe("Ollama Fetcher", () => {
96101
capabilities: ["completion", "vision"],
97102
}
98103

99-
const parsedModel = parseOllamaModel(modelDataWithVision as any)
104+
const { modelInfo } = parseOllamaModel(modelDataWithVision as any)
100105

101106
// No "tools" capability means filtered out
102-
expect(parsedModel).toBeNull()
107+
expect(modelInfo).toBeNull()
103108
})
104109

105110
it("should return model with both vision and tools when both capabilities present", () => {
@@ -108,11 +113,11 @@ describe("Ollama Fetcher", () => {
108113
capabilities: ["completion", "vision", "tools"],
109114
}
110115

111-
const parsedModel = parseOllamaModel(modelDataWithBoth as any)
116+
const { modelInfo } = parseOllamaModel(modelDataWithBoth as any)
112117

113-
expect(parsedModel).not.toBeNull()
114-
expect(parsedModel!.supportsImages).toBe(true)
115-
expect(parsedModel!.contextWindow).toBeGreaterThan(0)
118+
expect(modelInfo).not.toBeNull()
119+
expect(modelInfo!.supportsImages).toBe(true)
120+
expect(modelInfo!.contextWindow).toBeGreaterThan(0)
116121
})
117122
})
118123

@@ -177,7 +182,7 @@ describe("Ollama Fetcher", () => {
177182
expect(Object.keys(result).length).toBe(1)
178183
expect(result[modelName]).toBeDefined()
179184

180-
const expectedParsedDetails = parseOllamaModel(mockApiShowResponse as any)
185+
const { modelInfo: expectedParsedDetails } = parseOllamaModel(mockApiShowResponse as any)
181186
expect(result[modelName]).toEqual(expectedParsedDetails)
182187
})
183188

src/api/providers/fetchers/ollama.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,21 @@ type OllamaModelsResponse = z.infer<typeof OllamaModelsResponseSchema>
3737

3838
type OllamaModelInfoResponse = z.infer<typeof OllamaModelInfoResponseSchema>
3939

40-
export const parseOllamaModel = (rawModel: OllamaModelInfoResponse): ModelInfo | null => {
40+
export const parseOllamaModel = (
41+
rawModel: OllamaModelInfoResponse,
42+
modelName?: string,
43+
): { modelInfo: ModelInfo | null; filteredReason?: string } => {
4144
const contextKey = Object.keys(rawModel.model_info).find((k) => k.includes("context_length"))
4245
const contextWindow =
4346
contextKey && typeof rawModel.model_info[contextKey] === "number" ? rawModel.model_info[contextKey] : undefined
4447

4548
// Filter out models that don't support tools. Models without tool capability won't work.
4649
const supportsTools = rawModel.capabilities?.includes("tools") ?? false
4750
if (!supportsTools) {
48-
return null
51+
const reason = rawModel.capabilities
52+
? `Model '${modelName || "unknown"}' capabilities (${rawModel.capabilities.join(", ")}) do not include 'tools'`
53+
: `Model '${modelName || "unknown"}' has no capabilities reported (Ollama may need to be updated)`
54+
return { modelInfo: null, filteredReason: reason }
4955
}
5056

5157
const modelInfo: ModelInfo = Object.assign({}, ollamaDefaultModelInfo, {
@@ -56,7 +62,7 @@ export const parseOllamaModel = (rawModel: OllamaModelInfoResponse): ModelInfo |
5662
maxTokens: contextWindow || ollamaDefaultModelInfo.contextWindow,
5763
})
5864

59-
return modelInfo
65+
return { modelInfo }
6066
}
6167

6268
export async function getOllamaModels(
@@ -84,6 +90,8 @@ export async function getOllamaModels(
8490
let modelInfoPromises = []
8591

8692
if (parsedResponse.success) {
93+
const filteredModels: string[] = []
94+
8795
for (const ollamaModel of parsedResponse.data.models) {
8896
modelInfoPromises.push(
8997
axios
@@ -95,16 +103,28 @@ export async function getOllamaModels(
95103
{ headers },
96104
)
97105
.then((ollamaModelInfo) => {
98-
const modelInfo = parseOllamaModel(ollamaModelInfo.data)
106+
const { modelInfo, filteredReason } = parseOllamaModel(
107+
ollamaModelInfo.data,
108+
ollamaModel.name,
109+
)
99110
// Only include models that support native tools
100111
if (modelInfo) {
101112
models[ollamaModel.name] = modelInfo
113+
} else if (filteredReason) {
114+
filteredModels.push(filteredReason)
102115
}
103116
}),
104117
)
105118
}
106119

107120
await Promise.all(modelInfoPromises)
121+
122+
// Log filtered models to help users understand why models aren't appearing
123+
if (filteredModels.length > 0) {
124+
console.warn(
125+
`[Ollama] ${filteredModels.length} model(s) filtered out due to missing tool support:\n${filteredModels.join("\n")}`,
126+
)
127+
}
108128
} else {
109129
console.error(`Error parsing Ollama models response: ${JSON.stringify(parsedResponse.error, null, 2)}`)
110130
}

src/api/providers/native-ollama.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,22 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
206206
metadata?: ApiHandlerCreateMessageMetadata,
207207
): ApiStream {
208208
const client = this.ensureClient()
209-
const { id: modelId } = await this.fetchModel()
209+
const { id: modelId, info: modelInfo } = await this.fetchModel()
210210
const useR1Format = modelId.toLowerCase().includes("deepseek-r1")
211211

212+
// Log request info for debugging
213+
const baseUrl = this.options.ollamaBaseUrl || "http://localhost:11434"
214+
console.log(`[Ollama] Starting request to model '${modelId}' at ${baseUrl}`)
215+
216+
// Warn if the model is not in the fetched models list (may indicate missing tool support)
217+
if (!this.models[modelId]) {
218+
console.warn(
219+
`[Ollama] Warning: Model '${modelId}' was not found in the list of tool-capable models. ` +
220+
`This may indicate the model does not support native tool calling. ` +
221+
`Check if your Ollama version reports capabilities by running: ollama show ${modelId}`,
222+
)
223+
}
224+
212225
const ollamaMessages: Message[] = [
213226
{ role: "system", content: systemPrompt },
214227
...convertToOllamaMessages(messages),

0 commit comments

Comments
 (0)