fix: cap detail-plan latency and add LLM timeouts
This commit is contained in:
parent
598cdfd736
commit
b6a12ced9f
@ -14,6 +14,11 @@ import { inlineVisionImages } from "@/lib/ai/vision-inline";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const maxDuration = 180;
|
||||
const DETAIL_PLAN_INLINE_MAX_IMAGES = 4;
|
||||
const DETAIL_PLAN_INLINE_TIMEOUT_MS = 4000;
|
||||
const DETAIL_PLAN_INLINE_RETRIES = 1;
|
||||
const DETAIL_PLAN_MAIN_TIMEOUT_MS = 55000;
|
||||
const DETAIL_PLAN_PREFLIGHT_TIMEOUT_MS = 35000;
|
||||
|
||||
function isAbortError(error: unknown): boolean {
|
||||
const target = error as {
|
||||
@ -56,6 +61,19 @@ function isVolcanoResponsesPlanner(config: {
|
||||
return baseUrl.includes("ark.cn-beijing.volces.com") || /\/api\/v3(\/responses)?\/?$/.test(baseUrl);
|
||||
}
|
||||
|
||||
async function generateTextWithTimeout(params: any, timeoutMs: number) {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
try {
|
||||
return await generateText({
|
||||
...params,
|
||||
abortSignal: controller.signal,
|
||||
} as any);
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
type DetailPlanRequest = {
|
||||
prompt?: string;
|
||||
count?: number;
|
||||
@ -1465,7 +1483,7 @@ async function runDualDirectorPlanPreflight(params: {
|
||||
briefText,
|
||||
];
|
||||
|
||||
const { text } = await generateText({
|
||||
const { text } = await generateTextWithTimeout({
|
||||
model: params.model,
|
||||
messages: [
|
||||
{
|
||||
@ -1479,7 +1497,7 @@ async function runDualDirectorPlanPreflight(params: {
|
||||
: "你同时扮演高转化电商详情页的平面设计总监与营销总监。仅输出JSON。",
|
||||
temperature: 0.1,
|
||||
maxTokens: 2000,
|
||||
} as any);
|
||||
}, DETAIL_PLAN_PREFLIGHT_TIMEOUT_MS);
|
||||
|
||||
const parsed = extractJsonBlock<{
|
||||
pass?: unknown;
|
||||
@ -1661,10 +1679,10 @@ export async function POST(request: Request) {
|
||||
.map((url) => ({ type: "image" as const, image: url }));
|
||||
} else {
|
||||
const inlined = await inlineVisionImages(referenceImages, {
|
||||
maxImages: 6,
|
||||
maxImages: DETAIL_PLAN_INLINE_MAX_IMAGES,
|
||||
maxSide: 900,
|
||||
timeoutMs: 12000,
|
||||
retries: 2,
|
||||
timeoutMs: DETAIL_PLAN_INLINE_TIMEOUT_MS,
|
||||
retries: DETAIL_PLAN_INLINE_RETRIES,
|
||||
loggerPrefix: "[Canvas Detail Plan]",
|
||||
});
|
||||
imageInputs = inlined.inlineDataUrls.map((dataUrl) => ({
|
||||
@ -1676,7 +1694,7 @@ export async function POST(request: Request) {
|
||||
|
||||
let text = "";
|
||||
try {
|
||||
const result = await generateText({
|
||||
const result = await generateTextWithTimeout({
|
||||
model,
|
||||
messages: [
|
||||
{
|
||||
@ -1727,7 +1745,7 @@ export async function POST(request: Request) {
|
||||
`{"summary":"简述","globalStyle":{"designLanguage":"一句话风格方向","typography":{"fontFamily":"字体家族","title":"主标题规范","subtitle":"副标题规范","body":"正文规范"},"layout":{"grid":"栅格","safeArea":"安全区","rhythm":"留白节奏"},"palette":"配色规范"},"screens":[{"index":1,"screenType":"B","objective":"本屏目标","title":"首屏示例:甩甩杆,自拍不手抖(6-20字)","subtitle":"一句副标题","copy":"正文文案","layoutNote":"排版说明","useModel":false,"modelReason":"是否需要人物及原因","bundleShowcase":false,"bundleReason":"是否为搭配展示页及原因","fabe":{"b":"利益","f":"特征","a":"优势","e":"证据"},"visualPrompt":"用于生图的完整提示词,必须包含790x1300与统一字体排版"}]}`,
|
||||
temperature: 0.25,
|
||||
maxTokens: 1800,
|
||||
} as any);
|
||||
}, DETAIL_PLAN_MAIN_TIMEOUT_MS);
|
||||
text = String(result?.text || "");
|
||||
} catch (error) {
|
||||
const message = String((error as Error)?.message || "");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user