feat(channels): enable tool hints by default
This commit is contained in:
@@ -618,7 +618,7 @@ async def send(self, msg: OutboundMessage) -> None:
|
|||||||
await self._send_message(msg.chat_id, msg.content, media=msg.media)
|
await self._send_message(msg.chat_id, msg.content, media=msg.media)
|
||||||
```
|
```
|
||||||
|
|
||||||
Tool hints are off by default for most channels. Users can enable them globally or per channel:
|
Tool hints are on by default. Users can disable them globally or per channel:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -626,7 +626,7 @@ Tool hints are off by default for most channels. Users can enable them globally
|
|||||||
"sendToolHints": true,
|
"sendToolHints": true,
|
||||||
"webhook": {
|
"webhook": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"sendToolHints": true
|
"sendToolHints": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1555,7 +1555,7 @@ Global settings that apply to all channels. Configure under the `channels` secti
|
|||||||
{
|
{
|
||||||
"channels": {
|
"channels": {
|
||||||
"sendProgress": true,
|
"sendProgress": true,
|
||||||
"sendToolHints": false,
|
"sendToolHints": true,
|
||||||
"extractDocumentText": true,
|
"extractDocumentText": true,
|
||||||
"sendMaxRetries": 3,
|
"sendMaxRetries": 3,
|
||||||
"telegram": {
|
"telegram": {
|
||||||
@@ -1568,7 +1568,7 @@ Global settings that apply to all channels. Configure under the `channels` secti
|
|||||||
| Setting | Default | Description |
|
| Setting | Default | Description |
|
||||||
|---------|---------|-------------|
|
|---------|---------|-------------|
|
||||||
| `sendProgress` | `true` | Stream agent's text progress to the channel |
|
| `sendProgress` | `true` | Stream agent's text progress to the channel |
|
||||||
| `sendToolHints` | `false` | Stream tool-call hints (e.g. `read_file("…")`) |
|
| `sendToolHints` | `true` | Stream tool-call hints (e.g. `read_file("…")`) |
|
||||||
| `showReasoning` | `true` | Allow channels to surface model reasoning/thinking content (DeepSeek-R1 `reasoning_content`, Anthropic `thinking_blocks`, inline `<think>` tags). Reasoning flows as a dedicated stream with `_reasoning_delta` / `_reasoning_end` markers — channels override `send_reasoning_delta` / `send_reasoning_end` to render in-place updates. Even with `true`, channels without those overrides stay no-op silently. Currently surfaced on CLI and WebSocket/WebUI (italic shimmer header, auto-collapses after the stream ends); Telegram / Slack / Discord / Feishu / WeChat / Matrix / Mattermost keep the base no-op until their bubble UI is adapted. Independent of `sendProgress`. |
|
| `showReasoning` | `true` | Allow channels to surface model reasoning/thinking content (DeepSeek-R1 `reasoning_content`, Anthropic `thinking_blocks`, inline `<think>` tags). Reasoning flows as a dedicated stream with `_reasoning_delta` / `_reasoning_end` markers — channels override `send_reasoning_delta` / `send_reasoning_end` to render in-place updates. Even with `true`, channels without those overrides stay no-op silently. Currently surfaced on CLI and WebSocket/WebUI (italic shimmer header, auto-collapses after the stream ends); Telegram / Slack / Discord / Feishu / WeChat / Matrix / Mattermost keep the base no-op until their bubble UI is adapted. Independent of `sendProgress`. |
|
||||||
| `extractDocumentText` | `true` | Extract supported document/text attachments into the model prompt. PDF, DOCX, XLSX, and PPTX readers are included in the standard installation. Set to `false` to keep document content out of the prompt and include attachment path references instead. |
|
| `extractDocumentText` | `true` | Extract supported document/text attachments into the model prompt. PDF, DOCX, XLSX, and PPTX readers are included in the standard installation. Set to `false` to keep document content out of the prompt and include attachment path references instead. |
|
||||||
| `sendMaxRetries` | `3` | Max delivery attempts per outbound message, including the initial send (0-10 configured, minimum 1 actual attempt) |
|
| `sendMaxRetries` | `3` | Max delivery attempts per outbound message, including the initial send (0-10 configured, minimum 1 actual attempt) |
|
||||||
@@ -1581,10 +1581,11 @@ Global settings that apply to all channels. Configure under the `channels` secti
|
|||||||
{
|
{
|
||||||
"channels": {
|
"channels": {
|
||||||
"sendProgress": true,
|
"sendProgress": true,
|
||||||
"sendToolHints": false,
|
"sendToolHints": true,
|
||||||
"telegram": {
|
"telegram": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"sendProgress": false
|
"sendProgress": false,
|
||||||
|
"sendToolHints": false
|
||||||
},
|
},
|
||||||
"websocket": {
|
"websocket": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class BaseChannel(ABC):
|
|||||||
name: str = "base"
|
name: str = "base"
|
||||||
display_name: str = "Base"
|
display_name: str = "Base"
|
||||||
send_progress: bool = True
|
send_progress: bool = True
|
||||||
send_tool_hints: bool = False
|
send_tool_hints: bool = True
|
||||||
show_reasoning: bool = True
|
show_reasoning: bool = True
|
||||||
|
|
||||||
def __init__(self, config: Any, bus: MessageBus):
|
def __init__(self, config: Any, bus: MessageBus):
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class MattermostConfig(Base):
|
|||||||
react_emoji: str = "eyes"
|
react_emoji: str = "eyes"
|
||||||
done_emoji: str = "white_check_mark"
|
done_emoji: str = "white_check_mark"
|
||||||
send_progress: bool = True
|
send_progress: bool = True
|
||||||
send_tool_hints: bool = False
|
send_tool_hints: bool = True
|
||||||
dm: MattermostDMConfig = Field(default_factory=MattermostDMConfig)
|
dm: MattermostDMConfig = Field(default_factory=MattermostDMConfig)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ def test_config_defaults():
|
|||||||
assert config.token == ""
|
assert config.token == ""
|
||||||
assert config.streaming is True
|
assert config.streaming is True
|
||||||
assert config.streaming_max_chars == 16000
|
assert config.streaming_max_chars == 16000
|
||||||
|
assert config.send_tool_hints is True
|
||||||
assert config.dm.enabled is True
|
assert config.dm.enabled is True
|
||||||
assert config.dm.policy == "open"
|
assert config.dm.policy == "open"
|
||||||
assert config.reply_in_thread is True
|
assert config.reply_in_thread is True
|
||||||
@@ -131,6 +132,7 @@ def test_config_camelcase_aliases():
|
|||||||
"allowFromMatchMode": "username",
|
"allowFromMatchMode": "username",
|
||||||
"streamingMaxChars": 8000,
|
"streamingMaxChars": 8000,
|
||||||
"replyInThread": False,
|
"replyInThread": False,
|
||||||
|
"sendToolHints": False,
|
||||||
}
|
}
|
||||||
config = MattermostConfig.model_validate(raw)
|
config = MattermostConfig.model_validate(raw)
|
||||||
assert config.server_url == "https://mm.example.com"
|
assert config.server_url == "https://mm.example.com"
|
||||||
@@ -138,11 +140,13 @@ def test_config_camelcase_aliases():
|
|||||||
assert config.allow_from_match_mode == "username"
|
assert config.allow_from_match_mode == "username"
|
||||||
assert config.streaming_max_chars == 8000
|
assert config.streaming_max_chars == 8000
|
||||||
assert config.reply_in_thread is False
|
assert config.reply_in_thread is False
|
||||||
|
assert config.send_tool_hints is False
|
||||||
|
|
||||||
|
|
||||||
def test_config_default_config_classmethod():
|
def test_config_default_config_classmethod():
|
||||||
d = MattermostChannel.default_config()
|
d = MattermostChannel.default_config()
|
||||||
assert d["enabled"] is False
|
assert d["enabled"] is False
|
||||||
|
assert d["sendToolHints"] is True
|
||||||
assert d["serverUrl"] == ""
|
assert d["serverUrl"] == ""
|
||||||
assert d["token"] == ""
|
assert d["token"] == ""
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class ChannelsConfig(Base):
|
|||||||
model_config = ConfigDict(extra="allow")
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
send_progress: bool = True # stream agent's text progress to the channel
|
send_progress: bool = True # stream agent's text progress to the channel
|
||||||
send_tool_hints: bool = False # stream tool-call hints (e.g. read_file("…"))
|
send_tool_hints: bool = True # stream tool-call hints (e.g. read_file("…"))
|
||||||
show_reasoning: bool = True # surface model reasoning when channel implements it
|
show_reasoning: bool = True # surface model reasoning when channel implements it
|
||||||
extract_document_text: bool = True # extract text from document attachments before sending to the model
|
extract_document_text: bool = True # extract text from document attachments before sending to the model
|
||||||
send_max_retries: int = Field(default=3, ge=0, le=10) # Max delivery attempts (initial send included)
|
send_max_retries: int = Field(default=3, ge=0, le=10) # Max delivery attempts (initial send included)
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ def bus():
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def manager(config, bus):
|
def manager(config, bus):
|
||||||
manager = ChannelManager(config, bus)
|
manager = ChannelManager(config, bus)
|
||||||
manager.channels["mock"] = MockChannel({}, bus)
|
manager.channels["mock"] = manager._build_channel("mock", MockChannel, {})
|
||||||
return manager
|
return manager
|
||||||
|
|
||||||
|
|
||||||
@@ -284,14 +284,17 @@ class TestProgressFiltering:
|
|||||||
|
|
||||||
def test_progress_visibility_uses_global_defaults(self, manager):
|
def test_progress_visibility_uses_global_defaults(self, manager):
|
||||||
assert manager._should_send_progress("mock", tool_hint=False) is True
|
assert manager._should_send_progress("mock", tool_hint=False) is True
|
||||||
assert manager._should_send_progress("mock", tool_hint=True) is False
|
assert manager._should_send_progress("mock", tool_hint=True) is True
|
||||||
|
|
||||||
def test_progress_visibility_uses_channel_overrides(self, manager):
|
def test_progress_visibility_uses_channel_overrides(self, manager, bus):
|
||||||
manager.channels["mock"].send_progress = False
|
manager.channels["mock"] = manager._build_channel(
|
||||||
manager.channels["mock"].send_tool_hints = True
|
"mock",
|
||||||
|
MockChannel,
|
||||||
|
{"sendProgress": False, "sendToolHints": False},
|
||||||
|
)
|
||||||
|
|
||||||
assert manager._should_send_progress("mock", tool_hint=False) is False
|
assert manager._should_send_progress("mock", tool_hint=False) is False
|
||||||
assert manager._should_send_progress("mock", tool_hint=True) is True
|
assert manager._should_send_progress("mock", tool_hint=True) is False
|
||||||
|
|
||||||
def test_progress_visibility_returns_false_for_missing_channel(self, manager):
|
def test_progress_visibility_returns_false_for_missing_channel(self, manager):
|
||||||
assert manager._should_send_progress("nonexistent", tool_hint=False) is False
|
assert manager._should_send_progress("nonexistent", tool_hint=False) is False
|
||||||
|
|||||||
@@ -269,9 +269,12 @@ def test_channels_config_has_no_per_channel_fields():
|
|||||||
cfg = ChannelsConfig()
|
cfg = ChannelsConfig()
|
||||||
assert not hasattr(cfg, "telegram")
|
assert not hasattr(cfg, "telegram")
|
||||||
assert cfg.send_progress is True
|
assert cfg.send_progress is True
|
||||||
assert cfg.send_tool_hints is False
|
assert cfg.send_tool_hints is True
|
||||||
assert cfg.extract_document_text is True
|
assert cfg.extract_document_text is True
|
||||||
|
|
||||||
|
opted_out = ChannelsConfig.model_validate({"sendToolHints": False})
|
||||||
|
assert opted_out.send_tool_hints is False
|
||||||
|
|
||||||
|
|
||||||
def test_channels_config_extract_document_text_accepts_camel_alias():
|
def test_channels_config_extract_document_text_accepts_camel_alias():
|
||||||
cfg = ChannelsConfig.model_validate({"extractDocumentText": False})
|
cfg = ChannelsConfig.model_validate({"extractDocumentText": False})
|
||||||
|
|||||||
Reference in New Issue
Block a user