fix: scope /approve-/deny direct-send confirmations to native-streaming adapters; drop leftover debug log; refresh WeCom docs
- The direct adapter.send() confirmation path in /approve and /deny is only needed on native-streaming platforms (WeCom) where the reply stream is already finalized; other platforms keep the return-text contract (fixes 4 approve/deny regression tests, guarded with 'is not True' against MagicMock auto-attributes). - Remove a stray [DEBUG] logger.info left in _deliver_media_from_response. - website/docs wecom.md: replace the 'does not stream' notes with the native msgtype:stream behavior and document the stream keepalive extra keys.
This commit is contained in:
@@ -22886,7 +22886,6 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
||||
asked to deliver (#20834). Only ``MEDIA:`` directives — the explicit
|
||||
attachment contract — trigger post-stream uploads.
|
||||
"""
|
||||
logger.info("[DEBUG] _deliver_media_from_response called: response=%r chat=%s", response[:100], event.source.chat_id)
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote as _quote
|
||||
|
||||
|
||||
@@ -5886,6 +5886,14 @@ class GatewaySlashCommandsMixin:
|
||||
logger.info("User approved %d dangerous command(s) via /approve (%s)", count, choice)
|
||||
plural = "plural" if count > 1 else "singular"
|
||||
confirmation_text = t(f"gateway.approve.{choice}_{plural}", count=count)
|
||||
# Native-streaming adapters (WeCom msgtype:"stream") need the
|
||||
# confirmation sent directly with control-lane metadata so it lands
|
||||
# via a reliable proactive send instead of the (already-finalized)
|
||||
# reply stream. Every other platform keeps the normal contract:
|
||||
# else: return the text and let the gateway deliver it.
|
||||
# (`is not True` — mock adapters auto-create truthy attributes.)
|
||||
if getattr(_adapter, "SUPPORTS_NATIVE_STREAMING", False) is not True:
|
||||
return confirmation_text
|
||||
if _adapter:
|
||||
try:
|
||||
await _adapter.send(
|
||||
@@ -5971,6 +5979,12 @@ class GatewaySlashCommandsMixin:
|
||||
else:
|
||||
confirmation_text = t("gateway.deny.denied_singular")
|
||||
|
||||
# Same native-streaming carve-out as /approve above: only WeCom-style
|
||||
# native-stream adapters take the direct control-lane send; everyone
|
||||
# else returns the text for normal gateway delivery.
|
||||
# (`is not True` — mock adapters auto-create truthy attributes.)
|
||||
if getattr(_adapter, "SUPPORTS_NATIVE_STREAMING", False) is not True:
|
||||
return confirmation_text
|
||||
if _adapter:
|
||||
try:
|
||||
await _adapter.send(
|
||||
|
||||
@@ -1014,8 +1014,6 @@ def _ensure_hermes_home_managed(home: Path):
|
||||
|
||||
from hermes_cli.config_defaults import DEFAULT_CONFIG, OPTIONAL_ENV_VARS # noqa: F401
|
||||
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Config Migration System
|
||||
# =============================================================================
|
||||
|
||||
@@ -96,10 +96,12 @@ hermes gateway
|
||||
- **Auto-reconnect** — exponential backoff on connection drops
|
||||
|
||||
:::note Streaming and typing indicators
|
||||
The WeCom adapter delivers each response as a single complete message — it does
|
||||
**not** stream responses token-by-token, and it does **not** show a typing
|
||||
indicator. "Reply correlation" (below) only threads a response to its inbound
|
||||
request; it is not live streaming.
|
||||
The WeCom adapter streams responses natively over WeCom's `msgtype: "stream"`
|
||||
protocol: the client shows a thinking/typing bubble as soon as a turn starts,
|
||||
and the reply renders token-by-token in a single bubble as the model
|
||||
generates it. Tool-call progress is folded into the same bubble. Native
|
||||
streaming is enabled by default (`display.platforms.wecom.streaming: true` in
|
||||
`config.yaml`); set it to `false` to restore single-shot delivery.
|
||||
:::
|
||||
|
||||
## Configuration Options
|
||||
@@ -116,6 +118,9 @@ Set these in `config.yaml` under `platforms.wecom.extra`:
|
||||
| `allow_from` | `[]` | User IDs allowed for DMs (when dm_policy=allowlist) |
|
||||
| `group_allow_from` | `[]` | Group IDs allowed (when group_policy=allowlist) |
|
||||
| `groups` | `{}` | Per-group configuration (see below) |
|
||||
| `stream_keepalive_enabled` | `false` | Send periodic keepalive frames to refresh WeCom's ~6-minute reply-stream window on long turns |
|
||||
| `stream_keepalive_interval_seconds` | `120` | Keepalive frame cadence when enabled |
|
||||
| `stream_safe_duration_seconds` | `330` | Stream age after which finalize prefers the reliable proactive send |
|
||||
|
||||
## Access Policies
|
||||
|
||||
@@ -237,7 +242,7 @@ Files exceeding the absolute 20 MB limit are rejected with an informational mess
|
||||
|
||||
When the bot receives a message via the WeCom callback, the adapter remembers the inbound request ID. If a response is sent while the request context is still active, the adapter uses WeCom's reply-mode (`aibot_respond_msg`) to correlate the response directly to the inbound message. This provides a more natural conversation experience in the WeCom client.
|
||||
|
||||
The full response is delivered as a single message — the adapter does not stream tokens incrementally. If the inbound request context has expired or is unavailable, the adapter falls back to proactive message sending via `aibot_send_msg`.
|
||||
When a native reply stream is active, the response streams incrementally through reply-mode `msgtype: "stream"` frames. If the inbound request context has expired or is unavailable (or a stream frame fails), the adapter falls back to proactive message sending via `aibot_send_msg`.
|
||||
|
||||
Reply-mode also works for media: uploaded media can be sent as a reply to the originating message.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user