fix(zulip): edit_message accepts **kwargs, returns SendResult; use POST for edits
CI / validate (pull_request) Failing after 2s

The Gateway streaming interface calls edit_message() with finalize=True
and expects SendResult. Fixed:
1. Added **kwargs to edit_message() signature — silently ignores finalize
2. Changed return type from bool to SendResult for Gateway compatibility
3. Switched from PATCH to POST /api/v1/messages/{id} — Zulip docs use
   POST for message editing (PATCH returns 405 Method Not Allowed)
4. Same fixes applied to delete_message() and send()

Discovered during Tanko Gen 4 testing.
This commit is contained in:
Abiba (pi)
2026-06-27 14:18:42 +00:00
parent 48bc66b42f
commit 870f64d8a9
+37 -9
View File
@@ -853,12 +853,15 @@ class ZulipAdapter(BasePlatformAdapter):
content: str, content: str,
reply_to: Optional[str] = None, reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None, metadata: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> SendResult: ) -> SendResult:
"""Send a message to a Zulip chat. """Send a message to a Zulip chat.
For DMs, chat_id is the user_id. For streams, format is For DMs, chat_id is the user_id. For streams, format is
"stream_name:topic". Sends a placeholder first if this is the "stream_name:topic". Sends a placeholder first if this is the
first message in a turn, then edits it on subsequent calls. first message in a turn, then edits it on subsequent calls.
Accepts **kwargs for Hermes Gateway compatibility.
""" """
# Determine target type and IDs # Determine target type and IDs
is_dm = not chat_id or ":" not in chat_id is_dm = not chat_id or ":" not in chat_id
@@ -925,19 +928,42 @@ class ZulipAdapter(BasePlatformAdapter):
message_id: str, message_id: str,
content: str, content: str,
metadata: Optional[Dict[str, Any]] = None, metadata: Optional[Dict[str, Any]] = None,
) -> bool: **kwargs: Any,
"""Edit a previously sent Zulip message (for placeholder replacement).""" ) -> SendResult:
"""Edit a previously sent Zulip message (for placeholder replacement).
Returns SendResult for Hermes Gateway streaming interface compatibility.
Accepts **kwargs (e.g., finalize=True) — extra kwargs are silently ignored.
Note: Zulip uses POST /api/v1/messages/{message_id} for editing.
"""
truncated = _truncate(content) truncated = _truncate(content)
payload = { payload = {
"message_id": int(message_id),
"content": truncated, "content": truncated,
} }
try: try:
resp, _ = await self._api_call("PATCH", "/api/v1/messages", data=payload) resp, status = await self._api_call(
return resp is not None "POST", f"/api/v1/messages/{int(message_id)}",
data=payload,
)
if resp:
self._health_stats["send_count"] += 1
return SendResult(
success=True,
message_id=str(resp.get("id", message_id)),
)
self._health_stats["send_errors"] += 1
return SendResult(
success=False,
error=f"Edit failed (status={status})",
)
except Exception as e: except Exception as e:
logger.warning("[%s] Edit message %s error: %s", self.name, message_id, e) logger.warning("[%s] Edit message %s error: %s", self.name, message_id, e)
return False self._health_stats["send_errors"] += 1
return SendResult(
success=False,
error=str(e),
)
async def send_typing(self, chat_id: str, metadata: Optional[Dict] = None) -> None: async def send_typing(self, chat_id: str, metadata: Optional[Dict] = None) -> None:
"""Send typing indicator to a Zulip chat.""" """Send typing indicator to a Zulip chat."""
@@ -975,12 +1001,14 @@ class ZulipAdapter(BasePlatformAdapter):
return {"name": chat_id, "type": "dm"} return {"name": chat_id, "type": "dm"}
async def delete_message( async def delete_message(
self, chat_id: str, message_id: str, metadata: Optional[Dict] = None self, chat_id: str, message_id: str,
) -> bool: metadata: Optional[Dict] = None,
**kwargs: Any,
) -> SendResult:
"""Delete a message via Zulip API.""" """Delete a message via Zulip API."""
# Zulip doesn't support message deletion via API for bots # Zulip doesn't support message deletion via API for bots
# Send an empty edit instead # Send an empty edit instead
return await self.edit_message(chat_id, message_id, "*deleted*") return await self.edit_message(chat_id, message_id, "*deleted*", **kwargs)
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Gen 2: Self-test diagnostics # Gen 2: Self-test diagnostics