From d35935200f5e1a28833dad7205d6777f48fe1ac1 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Wed, 4 Mar 2026 08:53:33 +0100 Subject: [PATCH] fix(coding-agent): avoid compaction reasoning for non-reasoning models (#1793) --- .../src/core/compaction/compaction.ts | 6 +- .../test/compaction-summary-reasoning.test.ts | 78 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 packages/coding-agent/test/compaction-summary-reasoning.test.ts diff --git a/packages/coding-agent/src/core/compaction/compaction.ts b/packages/coding-agent/src/core/compaction/compaction.ts index d59ff0ef..4703de6b 100644 --- a/packages/coding-agent/src/core/compaction/compaction.ts +++ b/packages/coding-agent/src/core/compaction/compaction.ts @@ -554,10 +554,14 @@ export async function generateSummary( }, ]; + const completionOptions = model.reasoning + ? { maxTokens, signal, apiKey, reasoning: "high" as const } + : { maxTokens, signal, apiKey }; + const response = await completeSimple( model, { systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages }, - { maxTokens, signal, apiKey, reasoning: "high" }, + completionOptions, ); if (response.stopReason === "error") { diff --git a/packages/coding-agent/test/compaction-summary-reasoning.test.ts b/packages/coding-agent/test/compaction-summary-reasoning.test.ts new file mode 100644 index 00000000..3e8fe66c --- /dev/null +++ b/packages/coding-agent/test/compaction-summary-reasoning.test.ts @@ -0,0 +1,78 @@ +import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import type { AssistantMessage, Model } from "@mariozechner/pi-ai"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { generateSummary } from "../src/core/compaction/index.js"; + +const { completeSimpleMock } = vi.hoisted(() => ({ + completeSimpleMock: vi.fn(), +})); + +vi.mock("@mariozechner/pi-ai", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + completeSimple: completeSimpleMock, + }; +}); + +function createModel(reasoning: boolean): Model<"anthropic-messages"> { + return { + id: reasoning ? "reasoning-model" : "non-reasoning-model", + name: reasoning ? "Reasoning Model" : "Non-reasoning Model", + api: "anthropic-messages", + provider: "anthropic", + baseUrl: "https://api.anthropic.com", + reasoning, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 8192, + }; +} + +const mockSummaryResponse: AssistantMessage = { + role: "assistant", + content: [{ type: "text", text: "## Goal\nTest summary" }], + api: "anthropic-messages", + provider: "anthropic", + model: "claude-sonnet-4-5", + usage: { + input: 10, + output: 10, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 20, + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, + }, + stopReason: "stop", + timestamp: Date.now(), +}; + +const messages: AgentMessage[] = [{ role: "user", content: "Summarize this.", timestamp: Date.now() }]; + +describe("generateSummary reasoning options", () => { + beforeEach(() => { + completeSimpleMock.mockReset(); + completeSimpleMock.mockResolvedValue(mockSummaryResponse); + }); + + it("sets reasoning=high for reasoning-capable models", async () => { + await generateSummary(messages, createModel(true), 2000, "test-key"); + + expect(completeSimpleMock).toHaveBeenCalledTimes(1); + expect(completeSimpleMock.mock.calls[0][2]).toMatchObject({ + reasoning: "high", + apiKey: "test-key", + }); + }); + + it("does not set reasoning for non-reasoning models", async () => { + await generateSummary(messages, createModel(false), 2000, "test-key"); + + expect(completeSimpleMock).toHaveBeenCalledTimes(1); + expect(completeSimpleMock.mock.calls[0][2]).toMatchObject({ + apiKey: "test-key", + }); + expect(completeSimpleMock.mock.calls[0][2]).not.toHaveProperty("reasoning"); + }); +});