mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
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
This commit is contained in:
parent
fe722dad23
commit
9869b06b9e
3 changed files with 63 additions and 25 deletions
|
|
@ -15,9 +15,9 @@ describe("Ollama Fetcher", () => {
|
|||
describe("parseOllamaModel", () => {
|
||||
it("should correctly parse Ollama model info", () => {
|
||||
const modelData = ollamaModelsData["qwen3-2to16:latest"]
|
||||
const parsedModel = parseOllamaModel(modelData)
|
||||
const { modelInfo } = parseOllamaModel(modelData)
|
||||
|
||||
expect(parsedModel).toEqual({
|
||||
expect(modelInfo).toEqual({
|
||||
maxTokens: 40960,
|
||||
contextWindow: 40960,
|
||||
supportsImages: false,
|
||||
|
|
@ -39,9 +39,9 @@ describe("Ollama Fetcher", () => {
|
|||
},
|
||||
}
|
||||
|
||||
const parsedModel = parseOllamaModel(modelDataWithNullFamilies as any)
|
||||
const { modelInfo } = parseOllamaModel(modelDataWithNullFamilies as any)
|
||||
|
||||
expect(parsedModel).toEqual({
|
||||
expect(modelInfo).toEqual({
|
||||
maxTokens: 40960,
|
||||
contextWindow: 40960,
|
||||
supportsImages: false,
|
||||
|
|
@ -54,16 +54,18 @@ describe("Ollama Fetcher", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("should return null when capabilities does not include 'tools'", () => {
|
||||
it("should return null with reason when capabilities does not include 'tools'", () => {
|
||||
const modelDataWithoutTools = {
|
||||
...ollamaModelsData["qwen3-2to16:latest"],
|
||||
capabilities: ["completion"], // No "tools" capability
|
||||
}
|
||||
|
||||
const parsedModel = parseOllamaModel(modelDataWithoutTools as any)
|
||||
const { modelInfo, filteredReason } = parseOllamaModel(modelDataWithoutTools as any, "test-model")
|
||||
|
||||
// Models without tools capability are filtered out (return null)
|
||||
expect(parsedModel).toBeNull()
|
||||
expect(modelInfo).toBeNull()
|
||||
expect(filteredReason).toContain("test-model")
|
||||
expect(filteredReason).toContain("do not include 'tools'")
|
||||
})
|
||||
|
||||
it("should return model info when capabilities includes 'tools'", () => {
|
||||
|
|
@ -72,22 +74,25 @@ describe("Ollama Fetcher", () => {
|
|||
capabilities: ["completion", "tools"], // Has "tools" capability
|
||||
}
|
||||
|
||||
const parsedModel = parseOllamaModel(modelDataWithTools as any)
|
||||
const { modelInfo, filteredReason } = parseOllamaModel(modelDataWithTools as any)
|
||||
|
||||
expect(parsedModel).not.toBeNull()
|
||||
expect(parsedModel!.contextWindow).toBeGreaterThan(0)
|
||||
expect(modelInfo).not.toBeNull()
|
||||
expect(modelInfo!.contextWindow).toBeGreaterThan(0)
|
||||
expect(filteredReason).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should return null when capabilities is undefined (no tool support)", () => {
|
||||
it("should return null with reason when capabilities is undefined (no tool support)", () => {
|
||||
const modelDataWithoutCapabilities = {
|
||||
...ollamaModelsData["qwen3-2to16:latest"],
|
||||
capabilities: undefined, // No capabilities array
|
||||
}
|
||||
|
||||
const parsedModel = parseOllamaModel(modelDataWithoutCapabilities as any)
|
||||
const { modelInfo, filteredReason } = parseOllamaModel(modelDataWithoutCapabilities as any, "test-model")
|
||||
|
||||
// Models without explicit tools capability are filtered out
|
||||
expect(parsedModel).toBeNull()
|
||||
expect(modelInfo).toBeNull()
|
||||
expect(filteredReason).toContain("test-model")
|
||||
expect(filteredReason).toContain("no capabilities reported")
|
||||
})
|
||||
|
||||
it("should return null when model has vision but no tools capability", () => {
|
||||
|
|
@ -96,10 +101,10 @@ describe("Ollama Fetcher", () => {
|
|||
capabilities: ["completion", "vision"],
|
||||
}
|
||||
|
||||
const parsedModel = parseOllamaModel(modelDataWithVision as any)
|
||||
const { modelInfo } = parseOllamaModel(modelDataWithVision as any)
|
||||
|
||||
// No "tools" capability means filtered out
|
||||
expect(parsedModel).toBeNull()
|
||||
expect(modelInfo).toBeNull()
|
||||
})
|
||||
|
||||
it("should return model with both vision and tools when both capabilities present", () => {
|
||||
|
|
@ -108,11 +113,11 @@ describe("Ollama Fetcher", () => {
|
|||
capabilities: ["completion", "vision", "tools"],
|
||||
}
|
||||
|
||||
const parsedModel = parseOllamaModel(modelDataWithBoth as any)
|
||||
const { modelInfo } = parseOllamaModel(modelDataWithBoth as any)
|
||||
|
||||
expect(parsedModel).not.toBeNull()
|
||||
expect(parsedModel!.supportsImages).toBe(true)
|
||||
expect(parsedModel!.contextWindow).toBeGreaterThan(0)
|
||||
expect(modelInfo).not.toBeNull()
|
||||
expect(modelInfo!.supportsImages).toBe(true)
|
||||
expect(modelInfo!.contextWindow).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -177,7 +182,7 @@ describe("Ollama Fetcher", () => {
|
|||
expect(Object.keys(result).length).toBe(1)
|
||||
expect(result[modelName]).toBeDefined()
|
||||
|
||||
const expectedParsedDetails = parseOllamaModel(mockApiShowResponse as any)
|
||||
const { modelInfo: expectedParsedDetails } = parseOllamaModel(mockApiShowResponse as any)
|
||||
expect(result[modelName]).toEqual(expectedParsedDetails)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,10 @@ type OllamaModelsResponse = z.infer<typeof OllamaModelsResponseSchema>
|
|||
|
||||
type OllamaModelInfoResponse = z.infer<typeof OllamaModelInfoResponseSchema>
|
||||
|
||||
export const parseOllamaModel = (rawModel: OllamaModelInfoResponse): ModelInfo | null => {
|
||||
export const parseOllamaModel = (
|
||||
rawModel: OllamaModelInfoResponse,
|
||||
modelName?: string,
|
||||
): { modelInfo: ModelInfo | null; filteredReason?: string } => {
|
||||
const contextKey = Object.keys(rawModel.model_info).find((k) => k.includes("context_length"))
|
||||
const contextWindow =
|
||||
contextKey && typeof rawModel.model_info[contextKey] === "number" ? rawModel.model_info[contextKey] : undefined
|
||||
|
|
@ -45,7 +48,10 @@ export const parseOllamaModel = (rawModel: OllamaModelInfoResponse): ModelInfo |
|
|||
// Filter out models that don't support tools. Models without tool capability won't work.
|
||||
const supportsTools = rawModel.capabilities?.includes("tools") ?? false
|
||||
if (!supportsTools) {
|
||||
return null
|
||||
const reason = rawModel.capabilities
|
||||
? `Model '${modelName || "unknown"}' capabilities (${rawModel.capabilities.join(", ")}) do not include 'tools'`
|
||||
: `Model '${modelName || "unknown"}' has no capabilities reported (Ollama may need to be updated)`
|
||||
return { modelInfo: null, filteredReason: reason }
|
||||
}
|
||||
|
||||
const modelInfo: ModelInfo = Object.assign({}, ollamaDefaultModelInfo, {
|
||||
|
|
@ -56,7 +62,7 @@ export const parseOllamaModel = (rawModel: OllamaModelInfoResponse): ModelInfo |
|
|||
maxTokens: contextWindow || ollamaDefaultModelInfo.contextWindow,
|
||||
})
|
||||
|
||||
return modelInfo
|
||||
return { modelInfo }
|
||||
}
|
||||
|
||||
export async function getOllamaModels(
|
||||
|
|
@ -84,6 +90,8 @@ export async function getOllamaModels(
|
|||
let modelInfoPromises = []
|
||||
|
||||
if (parsedResponse.success) {
|
||||
const filteredModels: string[] = []
|
||||
|
||||
for (const ollamaModel of parsedResponse.data.models) {
|
||||
modelInfoPromises.push(
|
||||
axios
|
||||
|
|
@ -95,16 +103,28 @@ export async function getOllamaModels(
|
|||
{ headers },
|
||||
)
|
||||
.then((ollamaModelInfo) => {
|
||||
const modelInfo = parseOllamaModel(ollamaModelInfo.data)
|
||||
const { modelInfo, filteredReason } = parseOllamaModel(
|
||||
ollamaModelInfo.data,
|
||||
ollamaModel.name,
|
||||
)
|
||||
// Only include models that support native tools
|
||||
if (modelInfo) {
|
||||
models[ollamaModel.name] = modelInfo
|
||||
} else if (filteredReason) {
|
||||
filteredModels.push(filteredReason)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
await Promise.all(modelInfoPromises)
|
||||
|
||||
// Log filtered models to help users understand why models aren't appearing
|
||||
if (filteredModels.length > 0) {
|
||||
console.warn(
|
||||
`[Ollama] ${filteredModels.length} model(s) filtered out due to missing tool support:\n${filteredModels.join("\n")}`,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
console.error(`Error parsing Ollama models response: ${JSON.stringify(parsedResponse.error, null, 2)}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,9 +206,22 @@ export class NativeOllamaHandler extends BaseProvider implements SingleCompletio
|
|||
metadata?: ApiHandlerCreateMessageMetadata,
|
||||
): ApiStream {
|
||||
const client = this.ensureClient()
|
||||
const { id: modelId } = await this.fetchModel()
|
||||
const { id: modelId, info: modelInfo } = await this.fetchModel()
|
||||
const useR1Format = modelId.toLowerCase().includes("deepseek-r1")
|
||||
|
||||
// Log request info for debugging
|
||||
const baseUrl = this.options.ollamaBaseUrl || "http://localhost:11434"
|
||||
console.log(`[Ollama] Starting request to model '${modelId}' at ${baseUrl}`)
|
||||
|
||||
// Warn if the model is not in the fetched models list (may indicate missing tool support)
|
||||
if (!this.models[modelId]) {
|
||||
console.warn(
|
||||
`[Ollama] Warning: Model '${modelId}' was not found in the list of tool-capable models. ` +
|
||||
`This may indicate the model does not support native tool calling. ` +
|
||||
`Check if your Ollama version reports capabilities by running: ollama show ${modelId}`,
|
||||
)
|
||||
}
|
||||
|
||||
const ollamaMessages: Message[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
...convertToOllamaMessages(messages),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue