Skip to content

Commit b5233d0

Browse files
7418claude
andcommitted
fix: Image Agent 模式下图片不回显、不调用生成工具的问题
三个连锁 Bug 的根因修复: 1. route.ts — 图片 base64 数据保留 - 之前:文件存盘后一律清空 base64 data(包括图片) - 现在:只对非图片文件清空 data,图片始终保留 base64 - 确保 SDK 收到有效的 image content block 用于 vision 2. types/index.ts — ClaudeStreamOptions 增加 imageAgentMode 标记 - 新增 imageAgentMode?: boolean 字段 - 由 route.ts 根据 systemPromptAppend 是否存在来设置 3. claude-client.ts — buildFinalPrompt 在 imageAgentMode 下跳过文件路径引用 - imageAgentMode=true 时不添加 [User attached image: /path] 文本 - 防止 Claude 看到路径后调用 analyze_image 等内置工具 - Claude 仅通过 vision(base64 content blocks)看到图片 - 遵循 IMAGE_AGENT_SYSTEM_PROMPT 输出 image-gen-request 块 - 普通模式行为不变,仍保留路径引用供 skill/工具使用 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d13a039 commit b5233d0

File tree

3 files changed

+19
-9
lines changed

3 files changed

+19
-9
lines changed

src/app/api/chat/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export async function POST(request: NextRequest) {
149149
name: f.name,
150150
type: f.type,
151151
size: f.size,
152-
data: meta?.filePath ? '' : f.data, // Skip base64 data if file is already saved to disk
152+
data: (meta?.filePath && !f.type.startsWith('image/')) ? '' : f.data, // Keep base64 for images (needed for vision); clear for non-images (read from disk)
153153
filePath: meta?.filePath,
154154
};
155155
})
@@ -189,6 +189,7 @@ export async function POST(request: NextRequest) {
189189
abortController,
190190
permissionMode,
191191
files: fileAttachments,
192+
imageAgentMode: !!systemPromptAppend,
192193
toolTimeoutSeconds: toolTimeout || 300,
193194
provider: resolvedProvider,
194195
conversationHistory: historyMsgs,

src/lib/claude-client.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
279279
toolTimeoutSeconds = 0,
280280
conversationHistory,
281281
onRuntimeStatusChange,
282+
imageAgentMode,
282283
} = options;
283284

284285
return new ReadableStream<string>({
@@ -612,14 +613,21 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
612613
}
613614

614615
if (imageFiles.length > 0) {
615-
// Append image disk paths to the text prompt so Claude knows where
616-
// the files are on disk (enables skills to reference them by path).
617-
const workDir = workingDirectory || os.homedir();
618-
const imagePaths = getUploadedFilePaths(imageFiles, workDir);
619-
const imageReferences = imagePaths
620-
.map((p, i) => `[User attached image: ${p} (${imageFiles[i].name})]`)
621-
.join('\n');
622-
const textWithImageRefs = `${imageReferences}\n\n${textPrompt}`;
616+
// In imageAgentMode, skip file path references so Claude doesn't
617+
// try to use built-in tools to analyze images from disk. It will
618+
// see the images via vision (base64 content blocks) and follow the
619+
// IMAGE_AGENT_SYSTEM_PROMPT to output image-gen-request blocks.
620+
// In normal mode, append disk paths so skills can reference them.
621+
const textWithImageRefs = imageAgentMode
622+
? textPrompt
623+
: (() => {
624+
const workDir = workingDirectory || os.homedir();
625+
const imagePaths = getUploadedFilePaths(imageFiles, workDir);
626+
const imageReferences = imagePaths
627+
.map((p, i) => `[User attached image: ${p} (${imageFiles[i].name})]`)
628+
.join('\n');
629+
return `${imageReferences}\n\n${textPrompt}`;
630+
})();
623631

624632
const contentBlocks: Array<
625633
| { type: 'image'; source: { type: 'base64'; media_type: string; data: string } }

src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ export interface ClaudeStreamOptions {
644644
abortController?: AbortController;
645645
permissionMode?: string;
646646
files?: FileAttachment[];
647+
imageAgentMode?: boolean;
647648
toolTimeoutSeconds?: number;
648649
provider?: ApiProvider;
649650
/** Recent conversation history from DB — used as fallback context when SDK resume is unavailable or fails */

0 commit comments

Comments
 (0)