Skip to content

fix: suppress tool-call planning content#161

Merged
1bcMax merged 4 commits intoBlockRunAI:mainfrom
0xCheetah1:k2-tool-call-content-fix
Apr 24, 2026
Merged

fix: suppress tool-call planning content#161
1bcMax merged 4 commits intoBlockRunAI:mainfrom
0xCheetah1:k2-tool-call-content-fix

Conversation

@0xCheetah1
Copy link
Copy Markdown
Contributor

@0xCheetah1 0xCheetah1 commented Apr 24, 2026

Summary

  • suppress assistant content when the same response includes tool_calls, preventing provider planning text from being forwarded to chat surfaces
  • preserve tool_calls so tool execution still works normally
  • add a regression test for Kimi-style planning content alongside tool calls
  • type the plugin reload config used by the current fork baseline

Testing

  • Verified on VPS with moonshot/kimi-k2.6 through OpenClaw Telegram: old installer leaked planning text; patched branch returned clean final answers
  • npm run build on patched branch

Summary by CodeRabbit

  • Improvements

    • Assistant responses now suppress planning/thinking prose whenever tool calls are present, while preserving tool-call data.
    • Added support for an optional reload configuration (noopPrefixes) in plugin definitions.
  • Tests

    • Updated tests to cover tool-call response behavior and upstream server responses.
  • Documentation

    • Fixed markdown table formatting in several skill docs for consistent rendering.
  • Style

    • Minor code formatting tweaks with no functional changes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b2db58b1-0306-4dfb-afcf-3a38eae5d7c5

📥 Commits

Reviewing files that changed from the base of the PR and between 4a4c9ee and 6488348.

📒 Files selected for processing (6)
  • skills/clawrouter/SKILL.md
  • skills/imagegen/SKILL.md
  • src/index.ts
  • src/proxy.tool-forwarding.test.ts
  • src/proxy.ts
  • src/types.ts
✅ Files skipped from review due to trivial changes (4)
  • skills/clawrouter/SKILL.md
  • src/index.ts
  • src/types.ts
  • skills/imagegen/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/proxy.tool-forwarding.test.ts
  • src/proxy.ts

📝 Walkthrough

Walkthrough

Adds an optional reload.noopPrefixes to plugin types, reformats a small expression in src/index.ts, changes proxy streaming and non-streaming response handling to suppress assistant prose when tool_calls are present, refactors proxy tests to use a configurable upstream response, and updates two skill README tables' formatting.

Changes

Cohort / File(s) Summary
Plugin Types & small index tweak
src/types.ts, src/index.ts
Added optional reload?: { noopPrefixes?: string[] } to OpenClawPluginDefinition. Minor single-line reformat of toolWidth calculation in src/index.ts (no behavior change).
Proxy Response Handling
src/proxy.ts
Streaming and non-streaming response post-processing now detect per-choice tool_calls and suppress assistant prose by setting emitted delta.content / message.content to "" when non-empty tool calls exist; preserves structured tool_calls in output; conditional re-serialization applied.
Tool-forwarding Tests
src/proxy.tool-forwarding.test.ts
Refactored tests to use a mutable upstreamResponse reset in beforeEach; added test asserting proxy returns HTTP 200, preserves tool_calls, and suppresses assistant message.content when upstream signals finish_reason: "tool_calls".
Documentation formatting
skills/clawrouter/SKILL.md, skills/imagegen/SKILL.md
Adjusted Markdown table layouts and minor spacing in two SKILL.md files; only formatting changes, no content or behavioral changes.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Proxy
  participant Upstream
  Client->>Proxy: request
  Proxy->>Upstream: forward request
  Upstream-->>Proxy: response (choices with/without tool_calls)
  alt tool_calls present
    Proxy->>Proxy: for each choice, detect tool_calls → set content = ""
    Proxy-->>Client: stream tool_calls chunks + empty assistant content
  else no tool_calls
    Proxy->>Proxy: strip "thinking" tokens from content
    Proxy-->>Client: stream assistant content
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main change: suppressing planning/thinking content in tool-call responses. This aligns with the core objective stated in PR objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/proxy.tool-forwarding.test.ts (1)

118-179: Add a streaming-path companion regression test.

Line [118]-[179] correctly covers non-streaming behavior. Add a stream: true case that asserts empty delta.content with preserved tool_calls so SSE behavior is also locked in.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/proxy.tool-forwarding.test.ts` around lines 118 - 179, Add a companion
streaming test that mirrors the non-streaming case: create a new it(...) e.g.
"suppresses assistant content when upstream returns tool_calls (streaming)", set
upstreamResponse the same way, POST to `${proxy.baseUrl}/v1/chat/completions`
with stream: true and same messages/tools, consume the SSE response and assert
that the streamed assistant delta events contain an empty delta.content and
include the tool_calls payload (preserved) — i.e. verify each relevant SSE
chunk's parsed delta has content === "" and tool_calls length === 1 (and that
the final finish event is handled); reuse the same identifiers
(upstreamResponse, proxy.baseUrl, endpoint /v1/chat/completions) so the test
structure matches the existing non-streaming test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/proxy.tool-forwarding.test.ts`:
- Around line 118-179: Add a companion streaming test that mirrors the
non-streaming case: create a new it(...) e.g. "suppresses assistant content when
upstream returns tool_calls (streaming)", set upstreamResponse the same way,
POST to `${proxy.baseUrl}/v1/chat/completions` with stream: true and same
messages/tools, consume the SSE response and assert that the streamed assistant
delta events contain an empty delta.content and include the tool_calls payload
(preserved) — i.e. verify each relevant SSE chunk's parsed delta has content ===
"" and tool_calls length === 1 (and that the final finish event is handled);
reuse the same identifiers (upstreamResponse, proxy.baseUrl, endpoint
/v1/chat/completions) so the test structure matches the existing non-streaming
test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 43046a9b-b7f2-4132-b6f7-779a7cebc116

📥 Commits

Reviewing files that changed from the base of the PR and between 3c00b45 and 926a610.

📒 Files selected for processing (4)
  • src/index.ts
  • src/proxy.tool-forwarding.test.ts
  • src/proxy.ts
  • src/types.ts

@0xCheetah1 0xCheetah1 force-pushed the k2-tool-call-content-fix branch from 4a4c9ee to 6488348 Compare April 24, 2026 12:11
@1bcMax 1bcMax merged commit 4e63409 into BlockRunAI:main Apr 24, 2026
4 checks passed
1bcMax added a commit that referenced this pull request Apr 24, 2026
…sion

PR #161 fixed Kimi planning-prose leakage in both non-streaming and SSE paths,
but only the non-streaming path had a test. The SSE emission path is what
OpenClaw Telegram actually uses in production.

Asserts no delta.content chunk leaks the planning text, a tool_calls delta
chunk is emitted, and finish_reason is "tool_calls".

Uses a distinct user message to bypass the proxy dedup cache, which hashes
the request body without accounting for the stream flag — a cached
non-streaming response would otherwise be returned as application/json.
1bcMax added a commit that referenced this pull request Apr 24, 2026
…ream cache isolation

- fix: suppress tool-call planning prose when upstream returns content alongside tool_calls (thanks @0xCheetah1, #161)
- fix: declare mcp.servers.blockrun as noop reload prefix to stop OpenClaw gateway restart loop
- fix: dedup + response cache now keyed on original stream intent; SSE and JSON responses no longer collide
- test: SSE streaming regression test + dedup isolation regression test
1bcMax pushed a commit that referenced this pull request Apr 24, 2026
Thanks @0xCheetah1 — follow-up to #161.

Some providers (observed on Moonshot Kimi K2.6 via OpenClaw Telegram) mark a
turn with finish_reason: "tool_calls" without exposing the tool_calls array
at the same inspection point, so the #161 gate (tool_calls.length > 0) let
planning prose slip through. Broadens the gate to
`endsWithToolCalls || toolCalls.length > 0` in both the non-streaming JSON
path and the SSE emission path.

Rebased onto v0.12.165's dedup/cache-isolation fix — changes auto-merged;
the second "style: format" commit was dropped (prettier already applied
upstream). The original PR branch's dedup-test conflict was resolved by
keeping both the dedup-isolation test and the new finish_reason test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants