mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-06 02:47:56 +00:00
Merge 9869b06b9e into 2bb826039b
This commit is contained in:
commit
c56db83e0d
3 changed files with 63 additions and 25 deletions
|
|
@ -15,9 +15,9 @@ describe("Ollama Fetcher", () => {
|
||||||
describe("parseOllamaModel", () => {
|
describe("parseOllamaModel", () => {
|
||||||
it("should correctly parse Ollama model info", () => {
|
it("should correctly parse Ollama model info", () => {
|
||||||
const modelData = ollamaModelsData["qwen3-2to16:latest"]
|
const modelData = ollamaModelsData["qwen3-2to16:latest"]
|
||||||
const parsedModel = parseOllamaModel(modelData)
|
const { modelInfo } = parseOllamaModel(modelData)
|
||||||
|
|
||||||
expect(parsedModel).toEqual({
|
expect(modelInfo).toEqual({
|
||||||
maxTokens: 40960,
|
maxTokens: 40960,
|
||||||
contextWindow: 40960,
|
contextWindow: 40960,
|
||||||
supportsImages: false,
|
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,
|
maxTokens: 40960,
|
||||||
contextWindow: 40960,
|
contextWindow: 40960,
|
||||||
supportsImages: false,
|
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 = {
|
const modelDataWithoutTools = {
|
||||||
...ollamaModelsData["qwen3-2to16:latest"],
|
...ollamaModelsData["qwen3-2to16:latest"],
|
||||||
capabilities: ["completion"], // No "tools" capability
|
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)
|
// 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'", () => {
|
it("should return model info when capabilities includes 'tools'", () => {
|
||||||
|
|
@ -72,22 +74,25 @@ describe("Ollama Fetcher", () => {
|
||||||
capabilities: ["completion", "tools"], // Has "tools" capability
|
capabilities: ["completion", "tools"], // Has "tools" capability
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedModel = parseOllamaModel(modelDataWithTools as any)
|
const { modelInfo, filteredReason } = parseOllamaModel(modelDataWithTools as any)
|
||||||
|
|
||||||
expect(parsedModel).not.toBeNull()
|
expect(modelInfo).not.toBeNull()
|
||||||
expect(parsedModel!.contextWindow).toBeGreaterThan(0)
|
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 = {
|
const modelDataWithoutCapabilities = {
|
||||||
...ollamaModelsData["qwen3-2to16:latest"],
|
...ollamaModelsData["qwen3-2to16:latest"],
|
||||||
capabilities: undefined, // No capabilities array
|
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
|
// 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", () => {
|
it("should return null when model has vision but no tools capability", () => {
|
||||||
|
|
@ -96,10 +101,10 @@ describe("Ollama Fetcher", () => {
|
||||||
capabilities: ["completion", "vision"],
|
capabilities: ["completion", "vision"],
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedModel = parseOllamaModel(modelDataWithVision as any)
|
const { modelInfo } = parseOllamaModel(modelDataWithVision as any)
|
||||||
|
|
||||||
// No "tools" capability means filtered out
|
// 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", () => {
|
it("should return model with both vision and tools when both capabilities present", () => {
|
||||||
|
|
@ -108,11 +113,11 @@ describe("Ollama Fetcher", () => {
|
||||||
capabilities: ["completion", "vision", "tools"],
|
capabilities: ["completion", "vision", "tools"],
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedModel = parseOllamaModel(modelDataWithBoth as any)
|
const { modelInfo } = parseOllamaModel(modelDataWithBoth as any)
|
||||||
|
|
||||||
expect(parsedModel).not.toBeNull()
|
expect(modelInfo).not.toBeNull()
|
||||||
expect(parsedModel!.supportsImages).toBe(true)
|
expect(modelInfo!.supportsImages).toBe(true)
|
||||||
expect(parsedModel!.contextWindow).toBeGreaterThan(0)
|
expect(modelInfo!.contextWindow).toBeGreaterThan(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -177,7 +182,7 @@ describe("Ollama Fetcher", () => {
|
||||||
expect(Object.keys(result).length).toBe(1)
|
expect(Object.keys(result).length).toBe(1)
|
||||||
expect(result[modelName]).toBeDefined()
|
expect(result[modelName]).toBeDefined()
|
||||||
|
|
||||||
const expectedParsedDetails = parseOllamaModel(mockApiShowResponse as any)
|
const { modelInfo: expectedParsedDetails } = parseOllamaModel(mockApiShowResponse as any)
|
||||||
expect(result[modelName]).toEqual(expectedParsedDetails)
|
expect(result[modelName]).toEqual(expectedParsedDetails)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,10 @@ type OllamaModelsResponse = z.infer<typeof OllamaModelsResponseSchema>
|
||||||
|
|
||||||
type OllamaModelInfoResponse = z.infer<typeof OllamaModelInfoResponseSchema>
|
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 contextKey = Object.keys(rawModel.model_info).find((k) => k.includes("context_length"))
|
||||||
const contextWindow =
|
const contextWindow =
|
||||||
contextKey && typeof rawModel.model_info[contextKey] === "number" ? rawModel.model_info[contextKey] : undefined
|
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.
|
// Filter out models that don't support tools. Models without tool capability won't work.
|
||||||
const supportsTools = rawModel.capabilities?.includes("tools") ?? false
|
const supportsTools = rawModel.capabilities?.includes("tools") ?? false
|
||||||
if (!supportsTools) {
|
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, {
|
const modelInfo: ModelInfo = Object.assign({}, ollamaDefaultModelInfo, {
|
||||||
|
|
@ -56,7 +62,7 @@ export const parseOllamaModel = (rawModel: OllamaModelInfoResponse): ModelInfo |
|
||||||
maxTokens: contextWindow || ollamaDefaultModelInfo.contextWindow,
|
maxTokens: contextWindow || ollamaDefaultModelInfo.contextWindow,
|
||||||
})
|
})
|
||||||
|
|
||||||
return modelInfo
|
return { modelInfo }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOllamaModels(
|
export async function getOllamaModels(
|
||||||
|
|
@ -84,6 +90,8 @@ export async function getOllamaModels(
|
||||||
let modelInfoPromises = []
|
let modelInfoPromises = []
|
||||||
|
|
||||||
if (parsedResponse.success) {
|
if (parsedResponse.success) {
|
||||||
|
const filteredModels: string[] = []
|
||||||
|
|
||||||
for (const ollamaModel of parsedResponse.data.models) {
|
for (const ollamaModel of parsedResponse.data.models) {
|
||||||
modelInfoPromises.push(
|
modelInfoPromises.push(
|
||||||
axios
|
axios
|
||||||
|
|
@ -95,16 +103,28 @@ export async function getOllamaModels(
|
||||||
{ headers },
|
{ headers },
|
||||||
)
|
)
|
||||||
.then((ollamaModelInfo) => {
|
.then((ollamaModelInfo) => {
|
||||||
const modelInfo = parseOllamaModel(ollamaModelInfo.data)
|
const { modelInfo, filteredReason } = parseOllamaModel(
|
||||||
|
ollamaModelInfo.data,
|
||||||
|
ollamaModel.name,
|
||||||
|
)
|
||||||
// Only include models that support native tools
|
// Only include models that support native tools
|
||||||
if (modelInfo) {
|
if (modelInfo) {
|
||||||
models[ollamaModel.name] = modelInfo
|
models[ollamaModel.name] = modelInfo
|
||||||
|
} else if (filteredReason) {
|
||||||
|
filteredModels.push(filteredReason)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(modelInfoPromises)
|
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 {
|
} else {
|
||||||
console.error(`Error parsing Ollama models response: ${JSON.stringify(parsedResponse.error, null, 2)}`)
|
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,
|
metadata?: ApiHandlerCreateMessageMetadata,
|
||||||
): ApiStream {
|
): ApiStream {
|
||||||
const client = this.ensureClient()
|
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")
|
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[] = [
|
const ollamaMessages: Message[] = [
|
||||||
{ role: "system", content: systemPrompt },
|
{ role: "system", content: systemPrompt },
|
||||||
...convertToOllamaMessages(messages),
|
...convertToOllamaMessages(messages),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue