fix: improve error handling for streaming response in scene fusion

This commit is contained in:
Your Name 2026-01-22 19:41:58 +08:00
parent bec71cf195
commit da99fbdc93
2 changed files with 108 additions and 91 deletions

View File

@ -683,105 +683,115 @@ export async function POST(request: Request) {
async start(controller) {
const encoder = new TextEncoder();
// 发送初始消息
controller.enqueue(encoder.encode(JSON.stringify({ type: "start", total: processedScenes.length }) + "\n"));
let successCount = 0;
let failCount = 0;
for (let i = 0; i < processedScenes.length; i++) {
const scene = processedScenes[i];
try {
// 发送初始消息
controller.enqueue(encoder.encode(JSON.stringify({ type: "start", total: processedScenes.length }) + "\n"));
console.log(`[SceneFusion] ========== Processing scene ${i + 1}/${processedScenes.length} ==========`);
console.log(`[SceneFusion] Scene ID: ${scene.id}`);
console.log(`[SceneFusion] Product Image URL: ${productImageUrl.substring(0, 80)}...`);
console.log(`[SceneFusion] Scene Image URL: ${scene.imageUrl.substring(0, 80)}...`);
console.log(`[SceneFusion] Target Name: ${scene.targetName || '(not specified)'}`);
console.log(`[SceneFusion] Custom Prompt: ${scene.customPrompt || '(none)'}`);
console.log(`[SceneFusion] Provider Type: ${provider.type}`);
let successCount = 0;
let failCount = 0;
// 如果不是第一张图,等待5秒
if (i > 0) {
console.log(`[SceneFusion] Waiting 5 seconds before processing...`);
await new Promise((resolve) => setTimeout(resolve, 5000));
}
try {
// 使用新的通用处理函数,支持多种模型类型
const result = await processSceneWithProvider(
productImageUrl,
scene.imageUrl,
productName,
scene.targetName,
scene.customPrompt,
config.defaultPrompt,
{ type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey },
model.modelId,
aspectRatio,
imageSize
);
for (let i = 0; i < processedScenes.length; i++) {
const scene = processedScenes[i];
// 将分辨率等级用于数据库存储
const dbImageSize = imageSize || "2K";
console.log(`[SceneFusion] ========== Processing scene ${i + 1}/${processedScenes.length} ==========`);
console.log(`[SceneFusion] Scene ID: ${scene.id}`);
console.log(`[SceneFusion] Product Image URL: ${productImageUrl.substring(0, 80)}...`);
console.log(`[SceneFusion] Scene Image URL: ${scene.imageUrl.substring(0, 80)}...`);
console.log(`[SceneFusion] Target Name: ${scene.targetName || '(not specified)'}`);
console.log(`[SceneFusion] Custom Prompt: ${scene.customPrompt || '(none)'}`);
console.log(`[SceneFusion] Provider Type: ${provider.type}`);
// 保存到数据库(chatId 和 messageId 设为 null,因为场景裂变不关联聊天)
try {
await db.insert(generatedImage).values({
userId: session.user.id,
chatId: null, // 场景裂变不关联聊天
messageId: null, // 场景裂变不关联消息
thumbnailUrl: result.thumbnail,
originalUrl: result.original,
imageSize: dbImageSize,
prompt: `[场景裂变] ${result.prompt}`,
modelId: model.modelId,
});
console.log(`[SceneFusion] ✅ Saved image to gallery: ${result.original.substring(0, 80)}...`);
} catch (dbError) {
console.error("[SceneFusion] ❌ Failed to save to gallery:", dbError);
// 如果不是第一张图,等待5秒
if (i > 0) {
console.log(`[SceneFusion] Waiting 5 seconds before processing...`);
await new Promise((resolve) => setTimeout(resolve, 5000));
}
successCount++;
// 立即发送完成的结果给前端
controller.enqueue(encoder.encode(JSON.stringify({
type: "result",
data: {
id: scene.id,
status: "completed" as const,
result: result.thumbnail, // 返回缩略图用于预览
originalUrl: result.original, // 原图用于下载
},
}) + "\n"));
} catch (error) {
console.error(`[SceneFusion] Failed to process scene ${scene.id}:`, error);
failCount++;
// 立即发送失败的结果给前端
controller.enqueue(encoder.encode(JSON.stringify({
type: "result",
data: {
id: scene.id,
status: "failed" as const,
error: error instanceof Error ? error.message : "处理失败",
},
}) + "\n"));
try {
// 使用新的通用处理函数,支持多种模型类型
const result = await processSceneWithProvider(
productImageUrl,
scene.imageUrl,
productName,
scene.targetName,
scene.customPrompt,
config.defaultPrompt,
{ type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey },
model.modelId,
aspectRatio,
imageSize
);
// 将分辨率等级用于数据库存储
const dbImageSize = imageSize || "2K";
// 保存到数据库(chatId 和 messageId 设为 null,因为场景裂变不关联聊天)
try {
await db.insert(generatedImage).values({
userId: session.user.id,
chatId: null, // 场景裂变不关联聊天
messageId: null, // 场景裂变不关联消息
thumbnailUrl: result.thumbnail,
originalUrl: result.original,
imageSize: dbImageSize,
prompt: `[场景裂变] ${result.prompt}`,
modelId: model.modelId,
});
console.log(`[SceneFusion] ✅ Saved image to gallery: ${result.original.substring(0, 80)}...`);
} catch (dbError) {
console.error("[SceneFusion] ❌ Failed to save to gallery:", dbError);
}
successCount++;
// 立即发送完成的结果给前端
controller.enqueue(encoder.encode(JSON.stringify({
type: "result",
data: {
id: scene.id,
status: "completed" as const,
result: result.thumbnail, // 返回缩略图用于预览
originalUrl: result.original, // 原图用于下载
},
}) + "\n"));
} catch (error) {
console.error(`[SceneFusion] Failed to process scene ${scene.id}:`, error);
failCount++;
// 立即发送失败的结果给前端
controller.enqueue(encoder.encode(JSON.stringify({
type: "result",
data: {
id: scene.id,
status: "failed" as const,
error: error instanceof Error ? error.message : "处理失败",
},
}) + "\n"));
}
}
// 发送完成消息
console.log(`[SceneFusion] Completed: ${successCount} success, ${failCount} failed`);
controller.enqueue(encoder.encode(JSON.stringify({
type: "done",
summary: {
total: processedScenes.length,
success: successCount,
failed: failCount,
},
}) + "\n"));
} catch (error) {
console.error("[SceneFusion] Stream error:", error);
// 发送错误消息
controller.enqueue(encoder.encode(JSON.stringify({
type: "error",
error: error instanceof Error ? error.message : "处理失败",
}) + "\n"));
} finally {
controller.close();
}
// 发送完成消息
console.log(`[SceneFusion] Completed: ${successCount} success, ${failCount} failed`);
controller.enqueue(encoder.encode(JSON.stringify({
type: "done",
summary: {
total: processedScenes.length,
success: successCount,
failed: failCount,
},
}) + "\n"));
controller.close();
},
});

View File

@ -291,6 +291,13 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
} else {
toast.success(`批量处理完成:${summary.success} 张图片已生成`);
}
} else if (message.type === 'error') {
console.error('[SceneFusion Client] Stream error:', message.error);
toast.error(`处理失败:${message.error || '未知错误'}`);
// 将所有处理中的场景标记为失败
setScenes(prev => prev.map(s =>
s.status === 'processing' ? { ...s, status: 'failed', error: message.error || '处理失败' } : s
));
}
} catch (parseError) {
console.error('[SceneFusion Client] Failed to parse message:', parseError, 'Line:', line);