fix(channels): preserve Feishu clarification thread continuity (#3285)

* fix(channels): preserve Feishu clarification thread continuity

* fix(channels): address Feishu clarification review feedback

---------

Co-authored-by: zzp1221 <zzp1221@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
kia
2026-05-31 22:43:07 +08:00
committed by GitHub
parent 79cc227917
commit 46ddc346ad
5 changed files with 685 additions and 16 deletions
+61 -4
View File
@@ -15,7 +15,14 @@ import httpx
from langgraph_sdk.errors import ConflictError
from app.channels.commands import KNOWN_CHANNEL_COMMANDS
from app.channels.message_bus import InboundMessage, InboundMessageType, MessageBus, OutboundMessage, ResolvedAttachment
from app.channels.message_bus import (
PENDING_CLARIFICATION_METADATA_KEY,
InboundMessage,
InboundMessageType,
MessageBus,
OutboundMessage,
ResolvedAttachment,
)
from app.channels.store import ChannelStore
from app.gateway.csrf_middleware import CSRF_COOKIE_NAME, CSRF_HEADER_NAME, generate_csrf_token
from app.gateway.internal_auth import create_internal_auth_headers
@@ -202,6 +209,54 @@ def _extract_response_text(result: dict | list) -> str:
return ""
def _messages_from_result(result: dict | list) -> list[Any]:
if isinstance(result, list):
return result
if isinstance(result, dict):
messages = result.get("messages", [])
if isinstance(messages, list):
return messages
return []
def _current_turn_messages(result: dict | list) -> list[dict[str, Any]]:
messages = _messages_from_result(result)
current_turn: list[dict[str, Any]] = []
for msg in reversed(messages):
if not isinstance(msg, dict):
continue
if msg.get("type") == "human":
break
current_turn.append(msg)
current_turn.reverse()
return current_turn
def _has_current_turn_clarification(result: dict | list) -> bool:
"""Return True only when the current turn's final result is clarification."""
for msg in reversed(_current_turn_messages(result)):
msg_type = msg.get("type")
if msg_type == "tool":
return msg.get("name") == "ask_clarification"
if msg_type == "ai":
content = msg.get("content")
if isinstance(content, str):
if content:
return False
elif content:
return False
if msg.get("tool_calls"):
return False
return False
def _response_metadata(base_metadata: dict[str, Any], *, pending_clarification: bool = False) -> dict[str, Any]:
metadata = _slim_metadata(base_metadata)
if pending_clarification:
metadata[PENDING_CLARIFICATION_METADATA_KEY] = True
return metadata
def _extract_text_content(content: Any) -> str:
"""Extract text from a streaming payload content field."""
if isinstance(content, str):
@@ -806,6 +861,7 @@ class ChannelManager:
raise
response_text = _extract_response_text(result)
pending_clarification = _has_current_turn_clarification(result)
artifacts = _extract_artifacts(result)
logger.info(
@@ -831,7 +887,7 @@ class ChannelManager:
artifacts=artifacts,
attachments=attachments,
thread_ts=msg.thread_ts,
metadata=_slim_metadata(msg.metadata),
metadata=_response_metadata(msg.metadata, pending_clarification=pending_clarification),
)
logger.info("[Manager] publishing outbound message to bus: channel=%s, chat_id=%s", msg.channel_name, msg.chat_id)
await self.bus.publish_outbound(outbound)
@@ -893,7 +949,7 @@ class ChannelManager:
text=latest_text,
is_final=False,
thread_ts=msg.thread_ts,
metadata=_slim_metadata(msg.metadata),
metadata=_response_metadata(msg.metadata),
)
)
last_published_text = latest_text
@@ -907,6 +963,7 @@ class ChannelManager:
finally:
result = last_values if last_values is not None else {"messages": [{"type": "ai", "content": latest_text}]}
response_text = _extract_response_text(result)
pending_clarification = _has_current_turn_clarification(result)
artifacts = _extract_artifacts(result)
response_text, attachments = _prepare_artifact_delivery(thread_id, response_text, artifacts)
@@ -938,7 +995,7 @@ class ChannelManager:
attachments=attachments,
is_final=True,
thread_ts=msg.thread_ts,
metadata=_slim_metadata(msg.metadata),
metadata=_response_metadata(msg.metadata, pending_clarification=pending_clarification),
)
)