diff --git a/core/llm/llms/Ollama.test.ts b/core/llm/llms/Ollama.test.ts index 78d69dd157a..2a5bb269a03 100644 --- a/core/llm/llms/Ollama.test.ts +++ b/core/llm/llms/Ollama.test.ts @@ -223,4 +223,80 @@ describe("Ollama", () => { expect(result[1].role).toBe("tool"); }); }); + + describe("_streamChat tools preservation", () => { + let ollama: Ollama; + + beforeEach(() => { + ollama = createOllama(); + (ollama as any).ensureModelInfo = jest.fn().mockResolvedValue(undefined); + (ollama as any)._getModel = jest.fn().mockReturnValue("test-model"); + (ollama as any)._getModelFileParams = jest.fn().mockReturnValue({}); + (ollama as any).getEndpoint = jest + .fn() + .mockReturnValue("http://localhost:11434/api/chat"); + }); + + it("should include tools even when the last message role is 'tool'", async () => { + let capturedBody: any = null; + (ollama as any).fetch = jest + .fn() + .mockImplementation((url: string, init: any) => { + capturedBody = JSON.parse(init.body); + return Promise.resolve({ + ok: true, + body: { + getReader: () => ({ + read: () => Promise.resolve({ done: true, value: undefined }), + }), + }, + }); + }); + + const messages: ChatMessage[] = [ + { role: "user", content: "What is the weather?" }, + { + role: "assistant", + content: "", + toolCalls: [ + { + id: "1", + type: "function", + function: { name: "get_weather", arguments: "{}" }, + }, + ], + }, + { role: "tool", content: "Sunny", toolCallId: "1" }, + ]; + + const options = { + tools: [ + { + type: "function" as const, + function: { + name: "get_weather", + description: "Get weather", + parameters: {}, + }, + }, + ], + }; + + const generator = (ollama as any)._streamChat( + messages, + new AbortController().signal, + options, + ); + try { + for await (const _ of generator) { + } + } catch { + // Stream reading may fail on dummy response, but request body was already captured + } + + expect(capturedBody).not.toBeNull(); + expect(capturedBody.tools).toHaveLength(1); + expect(capturedBody.tools[0].function.name).toBe("get_weather"); + }); + }); }); diff --git a/core/llm/llms/Ollama.ts b/core/llm/llms/Ollama.ts index 4bcd9fb1e0f..a2d78c47f04 100644 --- a/core/llm/llms/Ollama.ts +++ b/core/llm/llms/Ollama.ts @@ -511,7 +511,7 @@ class Ollama extends BaseLLM implements ModelInstaller { stream: options.stream, // format: options.format, // Not currently in base completion options }; - if (options.tools?.length && ollamaMessages.at(-1)?.role === "user") { + if (options.tools?.length) { chatOptions.tools = options.tools.map((tool) => ({ type: "function", function: {