package vip.mate.channel; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import vip.mate.channel.model.ChannelSessionEntity; import vip.mate.workspace.conversation.model.MessageContentPart; import java.util.List; import java.util.Set; /** * Forward async-task results (image / video / music / 3D) generated by tool * pipelines to the IM channel that originated the conversation. *
* Without this dispatcher, completion bytes only land in * {@code mate_message} + a Web SSE broadcast — IM users (WeCom / DingTalk / * Feishu / Telegram / etc.) see nothing arrive in their chat client because * the tool pipeline doesn't know about channel adapters. This dispatcher * closes that loop: look up the conversation's bound channel session, get * the live adapter from {@link ChannelManager}, and call * {@link ChannelAdapter#sendContentParts} so the same bytes ride the * channel-native attachment protocol. *
* Web / webchat conversations are intentionally skipped because their SSE
* stream already carries the result; double-dispatching would render the
* image twice.
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AsyncTaskMediaDispatcher {
private final ChannelSessionStore channelSessionStore;
private final ChannelManager channelManager;
/**
* Channel types that handle their own UX via SSE (no IM forward needed).
* Everything not in this set is treated as an IM channel and gets the
* generated parts pushed via the adapter.
*/
private static final Set
* Best-effort: missing session, missing adapter, or adapter exception
* are all logged at debug/warn and never propagate. The caller has
* already persisted the message to {@code mate_message} and broadcast
* to Web SSE before invoking this — IM forwarding is additive.
*
* @param conversationId the conversation id used by the agent (e.g.
* {@code wecom:XuZhanFu}, {@code dingtalk:cid_xxx},
* {@code conv_xxx} for Web)
* @param parts assistant content parts to dispatch (typically a
* single image / video / audio / file part)
*/
public void forwardToImIfBound(String conversationId, List