Skip to content

feat: add tool_call_format config for compact tool call display in IM channels #7436

Description

@hrygo

Feature Request: Add tool_call_format config to control tool call display format

Problem

Currently, tool call messages in channels (Feishu, DingTalk, etc.) always render with the full format:

🔧 **execute_shell_command**

{"command": "cd ~/.qwenpaw && python3 -c "import json; ...""}

This is 3 lines per tool call, which becomes very noisy in IM channels where users want a lightweight progress trace — "I want to know you're alive, not see every argument."

The existing config fields (show_tool_calls, show_tool_details, tool_call_max_length) only control visibility and truncation, not the format itself. There's no way to get a compact single-line output like 🔧 **execute_shell_command** without patching source code.

Proposed Solution

Add a new config field tool_call_format to ChannelDisplayConfig:

@dataclass
class ChannelDisplayConfig:
    show_tool_details: bool = True
    show_thinking: bool = True
    show_tool_calls: bool = True
    show_tool_results: bool = True
    tool_call_max_length: int = 200
    tool_result_max_length: int = 500
    tool_call_format: str = "full"  # NEW: "full" | "name_only" | "compact"

Format modes:

Mode Output Use case
"full" (default) 🔧 **name** + code fence + args Current behavior, no breaking change
"name_only" 🔧 **name** (single line, no args) IM channels: lightweight progress trace
"compact" 🔧 **name**: {first 80 chars} Balance between info and brevity

Implementation sketch (in renderer.py):

def _fmt_tool_call(
    name: str,
    args_preview: str,
    style: RenderStyle,
) -> str:
    fmt = getattr(style.display_config, "tool_call_format", "full")

    if fmt == "name_only":
        if style.use_emoji:
            return f"🔧 **{name}**"
        return f"**{name}**"

    if fmt == "compact":
        truncated = args_preview[:80] + ("..." if len(args_preview) > 80 else "")
        if style.supports_markdown and style.use_emoji:
            return f"🔧 **{name}**: `{truncated}`"
        if style.supports_markdown:
            return f"**{name}**: `{truncated}`"
        return f"{name}: {truncated}"

    # Default: "full" (current behavior)
    if style.supports_markdown and style.use_emoji:
        return f"🔧 **{name}**\n```\n{args_preview}\n```"
    if style.supports_markdown:
        return f"**{name}**\n```\n{args_preview}\n```"
    if style.supports_code_fence:
        return f"{name}\n```\n{args_preview}\n```"
    return f"{name}: {args_preview}"

Why not just show_tool_details=false?

I tried that approach. When show_tool_details=false, args_preview becomes "...", but the code fence is still emitted:

🔧 **execute_shell_command**

...

That's still 3 lines with no information gain. The user wants just the name — a single line per tool call.

Config usage

Per-channel config (e.g. agent.jsonchannels.feishu):

{
  "channels": {
    "feishu": {
      "tool_call_format": "name_only"
    }
  }
}

Impact

  • Backward compatible: Default is "full" (current behavior unchanged)
  • Per-channel: Each channel can choose its own format
  • No global side effects: Unlike show_tool_details=false which affects all channels

Context

  • QwenPaw version: v2.1.0 (installed via pip)
  • Affected file: src/qwenpaw/app/channels/renderer.py, function _fmt_tool_call
  • Related config: ChannelDisplayConfig in same file
  • User scenario: Feishu (Lark) channel where tool call noise degrades mobile UX

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status
      Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions