diff --git a/lib/ai/prompt-optimizer.ts b/lib/ai/prompt-optimizer.ts index 703212827..d9c399dd5 100644 --- a/lib/ai/prompt-optimizer.ts +++ b/lib/ai/prompt-optimizer.ts @@ -299,6 +299,30 @@ const IMAGE_GEN_OPTIMIZER_SYSTEM_PROMPT = `你是一个专业的图片生成提 - 细节:一句话补充风格/材质/光线/构图 `; +function parseOptimizerJsonCandidate(raw: string): { + optimizedPrompt?: string; + negativePrompt?: string; + suggestedParams?: { + aspectRatio?: string; + imageSize?: string; + imageCount?: number; + promptExtend?: boolean; + }; +} | null { + const normalized = raw + .trim() + .replace(/\uFEFF/g, "") + .replace(/[“”]/g, "\"") + .replace(/[‘’]/g, "'") + .replace(/,\s*([}\]])/g, "$1"); + if (!normalized) return null; + try { + return JSON.parse(normalized); + } catch { + return null; + } +} + function parseOptimizerJson(responseText: string): { optimizedPrompt?: string; negativePrompt?: string; @@ -309,13 +333,40 @@ function parseOptimizerJson(responseText: string): { promptExtend?: boolean; }; } | null { - const jsonMatch = responseText.match(/\{[\s\S]*\}/); - if (!jsonMatch) return null; - try { - return JSON.parse(jsonMatch[0]); - } catch (error) { - return null; + if (!responseText || typeof responseText !== "string") return null; + + const fencedBlocks = Array.from( + responseText.matchAll(/```(?:json)?\s*([\s\S]*?)```/gi) + ).map((match) => match[1]?.trim() || ""); + + for (const block of fencedBlocks) { + const parsed = parseOptimizerJsonCandidate(block); + if (parsed) return parsed; } + + const text = responseText.trim(); + const starts: number[] = []; + for (let i = 0; i < text.length; i++) { + if (text[i] === "{") starts.push(i); + } + + for (const start of starts) { + let depth = 0; + for (let end = start; end < text.length; end++) { + if (text[end] === "{") depth += 1; + if (text[end] === "}") { + depth -= 1; + if (depth === 0) { + const candidate = text.slice(start, end + 1); + const parsed = parseOptimizerJsonCandidate(candidate); + if (parsed) return parsed; + break; + } + } + } + } + + return null; } function sanitizeSuggestedParams(input?: { @@ -590,7 +641,10 @@ export async function optimizePromptWithLLM(params: { } // 解析失败,回退到模板优化 - console.log("[Prompt Optimizer LLM] Parse failed, falling back to template"); + console.log( + "[Prompt Optimizer LLM] Parse failed, falling back to template. raw=", + responseText.slice(0, 320) + ); return optimizePrompt({ originalPrompt, intent, subIntent }); } catch (error) {