From b0f5dadbe653d90221b334875b577bb0c942d5b9 Mon Sep 17 00:00:00 2001 From: matevip Date: Sat, 2 May 2026 15:40:17 +0800 Subject: [PATCH] fix(feishu): default-on image download for vision pipelines --- .../channel/feishu/FeishuChannelAdapter.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/channel/feishu/FeishuChannelAdapter.java b/mateclaw-server/src/main/java/vip/mate/channel/feishu/FeishuChannelAdapter.java index f9a44412..602251bf 100644 --- a/mateclaw-server/src/main/java/vip/mate/channel/feishu/FeishuChannelAdapter.java +++ b/mateclaw-server/src/main/java/vip/mate/channel/feishu/FeishuChannelAdapter.java @@ -916,9 +916,17 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter { case "image" -> { String imageKey = (String) contentObj.get("image_key"); if (imageKey != null) { - String localPath = maybeDownloadResource(messageId, imageKey, "image", null); + // Images need bytes for vision: the Feishu CDN URL + // requires tenant_access_token, so a downstream vision + // tool that only sees image_key cannot fetch the + // image. Default-on for images (separate from the + // file/audio/video gate) so vision works out of the + // box; admins can opt out with feishu_image_download_enabled=false. + String localPath = maybeDownloadImage(messageId, imageKey); MessageContentPart part = MessageContentPart.image(imageKey, null); - if (localPath != null) part.setPath(localPath); + if (localPath != null) { + part.setPath(localPath); + } parts.add(part); } yield "[图片]"; @@ -1068,7 +1076,9 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter { case "img" -> { String imageKey = (String) element.get("image_key"); if (imageKey != null) { - String localPath = mediaDownload ? maybeDownloadResource(messageId, imageKey, "image", null) : null; + // Same reasoning as the standalone image case: vision + // pipelines need bytes; image_key alone is opaque. + String localPath = maybeDownloadImage(messageId, imageKey); MessageContentPart imgPart = MessageContentPart.image(imageKey, null); if (localPath != null) imgPart.setPath(localPath); parts.add(imgPart); @@ -1117,6 +1127,22 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter { return downloadResource(messageId, fileKey, type, fileNameHint); } + /** + * Image-specific download: default ON so the vision/STT pipeline + * downstream actually has bytes to analyze. Without the local file, + * vision providers see only an opaque {@code image_key} and the + * Feishu CDN URL needs tenant_access_token to fetch — neither of + * which the model can resolve. Admins who want to suppress image + * downloads (e.g. tighter privacy, no disk usage) can set + * {@code feishu_image_download_enabled=false} on the channel config. + */ + private String maybeDownloadImage(String messageId, String imageKey) { + if (!getConfigBoolean("feishu_image_download_enabled", true)) { + return null; + } + return downloadResource(messageId, imageKey, "image", null); + } + /** * 下载飞书消息资源(图片/文件)到本地 *