feat(ollama): add native /api/chat provider for streaming + tool calling (#11853)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 0a723f98e6
Co-authored-by: BrokenFinger98 <115936166+BrokenFinger98@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
This commit is contained in:
Sunwoo Yu
2026-02-14 09:20:42 +09:00
committed by GitHub
parent 5378583da1
commit 11702290ff
9 changed files with 760 additions and 75 deletions

View File

@@ -29,25 +29,20 @@ describe("Ollama provider", () => {
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
const providers = await resolveImplicitProviders({ agentDir });
// Ollama requires explicit configuration via OLLAMA_API_KEY env var or profile
expect(providers?.ollama).toBeUndefined();
});
it("should disable streaming by default for Ollama models", async () => {
it("should use native ollama api type", async () => {
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
process.env.OLLAMA_API_KEY = "test-key";
try {
const providers = await resolveImplicitProviders({ agentDir });
// Provider should be defined with OLLAMA_API_KEY set
expect(providers?.ollama).toBeDefined();
expect(providers?.ollama?.apiKey).toBe("OLLAMA_API_KEY");
// Note: discoverOllamaModels() returns empty array in test environments (VITEST env var check)
// so we can't test the actual model discovery here. The streaming: false setting
// is applied in the model mapping within discoverOllamaModels().
// The configuration structure itself is validated by TypeScript and the Zod schema.
expect(providers?.ollama?.api).toBe("ollama");
expect(providers?.ollama?.baseUrl).toBe("http://127.0.0.1:11434");
} finally {
delete process.env.OLLAMA_API_KEY;
}
@@ -69,15 +64,14 @@ describe("Ollama provider", () => {
},
});
expect(providers?.ollama?.baseUrl).toBe("http://192.168.20.14:11434/v1");
// Native API strips /v1 suffix via resolveOllamaApiBase()
expect(providers?.ollama?.baseUrl).toBe("http://192.168.20.14:11434");
} finally {
delete process.env.OLLAMA_API_KEY;
}
});
it("should have correct model structure with streaming disabled (unit test)", () => {
// This test directly verifies the model configuration structure
// since discoverOllamaModels() returns empty array in test mode
it("should have correct model structure without streaming override", () => {
const mockOllamaModel = {
id: "llama3.3:latest",
name: "llama3.3:latest",
@@ -86,13 +80,9 @@ describe("Ollama provider", () => {
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 8192,
params: {
streaming: false,
},
};
// Verify the model structure matches what discoverOllamaModels() would return
expect(mockOllamaModel.params?.streaming).toBe(false);
expect(mockOllamaModel.params).toHaveProperty("streaming");
// Native Ollama provider does not need streaming: false workaround
expect(mockOllamaModel).not.toHaveProperty("params");
});
});