server: disable tool calls during compaction requests#591
Open
ober wants to merge 5 commits into
Open
Conversation
When the Responses API input history ends with a "compaction" or "context_compaction" bookkeeping item, the model was still emitting DSML tool calls as part of its summary output. These tool calls were returned as raw text in the response content, causing breakage in clients like opencode that expect structured tool_calls or plain text. Track whether the last input item was a compaction type and disable has_tools in that case, so the model generates a plain text summary without attempting tool invocations. Fixes bogus DSML appearing in compaction/summary responses.
The previous fix only set has_tools=false when the Responses input ended with a compaction item, but still rendered the full tool schemas and DSML usage instructions into the prompt. The model, primed by both the schemas and a history saturated with DSML stanzas, kept emitting tool calls as its "summary" — and with has_tools off nothing parsed them, so the raw DSML leaked into the response text and became the stored compaction summary, corrupting the compacted context. Drop active_tool_schemas as well for compaction requests, matching the tool-less chat completions path: no schemas, no DSML instructions, plain text summary. Also fix last_was_compaction latching: it was set by any bookkeeping item in the input and never cleared, so a compaction item anywhere in history would disable tools for the whole request. Now only the last input item decides, as the original commit intended. Verified live: compaction-last request renders 14 prompt tokens with no TOOLS flag; the same request with a trailing user message renders 334 tokens with TOOLS enabled.
The previous three compaction fixes only patched the Responses API (last_was_compaction bookkeeping in parse_responses_input). opencode talks to ds4 through @ai-sdk/openai-compatible, which posts compaction requests to /v1/chat/completions with the full tool schemas attached — so none of the earlier handling ever fired, and long sessions kept stalling: primed by the schemas and a DSML-saturated history, the model emitted |DSML|tool_calls stanzas as its "summary", corrupting the compacted context every time auto-compaction ran. Detect opencode's compaction request in parse_chat_request by its distinctive summarization markers (the anchored-summary system prompt, <previous-summary>, or the create/update anchored-summary user instruction) and apply the Responses-path treatment: drop tool schemas, sanitize the history (strip stored DSML, prepend the plain-text instruction, drop structured tool_calls), and set r->compaction so the output-side DSML strip still catches anything that leaks through. Regression test covers the system-prompt marker, the previous-summary update marker, and a negative case that merely mentions summarizing.
sanitize_messages_for_compaction unconditionally xstrdup'd m->reasoning, but xstrdup is NULL-unsafe and chat-completions messages carry reasoning == NULL unless the client sends the field — the first opencode compaction request after the chat-path detection segfaulted the server. Only the Responses parser always populates reasoning, which is why the original live verification missed it. Guard the copy and add a regression test that sanitizes a message with no reasoning field.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
server: disable tool calls during compaction requests
Problem
When opencode (or other Responses API clients) sends a compaction request to summarize a session, the ds4 server was allowing the model to emit DSML tool calls as part of its summary output. These tool calls were returned as raw text in the response content instead of being converted to structured tool_calls, causing breakage in clients that expect either proper JSON tool calls or plain text.
Observed Behavior
During session compaction, the model would output raw DSML like:
This raw DSML text was included in the assistant response content, which:
Root Cause
The Responses API parser (parse_responses_input) correctly identifies "compaction" and "context_compaction" items as bookkeeping entries in the input history. However, this information was not propagated to the request handler, so has_tools remained true even when the request was clearly a compaction/summary operation.
With has_tools = true, the model DSML tool call output was processed by the tool parser. When the parser failed (or the DSML was incomplete), the raw DSML was returned as assistant text content.
Solution
Track whether the last item in the Responses API input history is a compaction type ("compaction" or "context_compaction"), and disable tool calls (has_tools = false) for such requests.
Changes
parse_responses_input: Added a bool *last_was_compaction out-parameter that is set to true when a compaction bookkeeping item is encountered in the input history.
parse_responses_request: Passes the last_was_compaction flag and uses it to disable tools:
Test updates: Updated existing test calls to parse_responses_input to pass NULL for the new parameter.
Impact
Testing
Files Changed