fix: forward canvas detail imageSize and harden ruxa task parsing
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-24 20:59:58 +08:00
parent 365174ffc5
commit d89e8590a9
4 changed files with 58 additions and 16 deletions

View File

@ -26,6 +26,10 @@ function toGenerateImagePayload(body: GenerateImageRequest): CanvasGenerateImage
typeof body.aspectRatio === "string" && body.aspectRatio.trim()
? body.aspectRatio.trim()
: "1:1",
imageSize:
typeof body.imageSize === "string" && body.imageSize.trim()
? body.imageSize.trim()
: undefined,
imageCount:
typeof body.imageCount === "number" && Number.isFinite(body.imageCount)
? body.imageCount

View File

@ -343,6 +343,7 @@ type CanvasImageGenerationRequest = {
prompt: string;
model?: string;
aspectRatio?: string;
imageSize?: string;
imageCount?: number;
negativePrompt?: string;
referenceImage?: string;
@ -4162,6 +4163,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
prompt: promptOverride,
model: modelForGeneration,
aspectRatio: "9:16",
imageSize: imageParams.imageSize,
imageCount: 1,
negativePrompt: imageParams.negativePrompt,
referenceImage: productAttachments[0]?.url,
@ -4421,6 +4423,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
prompt: reviewPrompt,
model: modelForGeneration,
aspectRatio: "1:1",
imageSize: imageParams.imageSize,
imageCount: DETAIL_REVIEW_IMAGE_COUNT,
negativePrompt: imageParams.negativePrompt,
referenceImage: productAttachments[0]?.url,
@ -4770,6 +4773,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
prompt: reviewPrompt,
model: modelForGeneration,
aspectRatio: "1:1",
imageSize: imageParams.imageSize,
imageCount: DETAIL_REVIEW_IMAGE_COUNT,
negativePrompt: imageParams.negativePrompt,
referenceImage: productAttachments[0]?.url,

View File

@ -75,13 +75,21 @@ function normalizeOutputFormat(outputFormat?: string): "png" | "jpg" {
}
function extractTaskId(payload: any): string | undefined {
if (typeof payload === "string" && payload.trim()) {
return payload.trim();
}
if (typeof payload?.data === "string" && payload.data.trim()) {
return payload.data.trim();
}
return (
payload?.taskId ||
payload?.task_id ||
payload?.id ||
payload?.task?.id ||
payload?.data?.taskId ||
payload?.data?.task_id ||
payload?.data?.id
payload?.data?.id ||
payload?.data?.task?.id
);
}
@ -110,6 +118,15 @@ function extractErrorMessage(payload: any): string {
);
}
function compactPayloadSnippet(payload: any): string {
try {
const json = JSON.stringify(payload);
return json.length > 320 ? `${json.slice(0, 320)}...` : json;
} catch {
return String(payload || "");
}
}
function extractPromptAndImages(messages: Array<{ role: string; content: any }>) {
const lastUserMessage = messages.filter((message) => message.role === "user").pop();
if (!lastUserMessage) {
@ -347,34 +364,46 @@ export function createRuxaImage({ apiKey, baseURL }: RuxaImageConfig) {
const activeModelId = options.overrideModelId || modelId;
const createEndpoint = joinApiPath(baseURL, "/api/v1/tasks/create");
const callbackUrl = process.env.RUXA_CALLBACK_URL?.trim();
const requestBody = {
input: {
prompt,
image_input: imageUrls,
aspect_ratio: aspectRatio,
resolution,
output_format: outputFormat,
},
model: activeModelId,
...(callbackUrl ? { callBackUrl: callbackUrl } : {}),
};
const createResponse = await fetch(createEndpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
input: {
prompt,
image_input: imageUrls,
aspect_ratio: aspectRatio,
resolution,
output_format: outputFormat,
},
model: activeModelId,
}),
body: JSON.stringify(requestBody),
});
const createPayload = await createResponse.json();
const createRawText = await createResponse.text();
let createPayload: any = null;
try {
createPayload = JSON.parse(createRawText);
} catch {
createPayload = createRawText;
}
if (!createResponse.ok) {
throw new Error(extractErrorMessage(createPayload));
throw new Error(
`${extractErrorMessage(createPayload)} (HTTP ${createResponse.status})`
);
}
let outputUrls = filterSourceImages(collectImageUrls(createPayload), imageUrls);
if (outputUrls.length === 0) {
const taskId = extractTaskId(createPayload);
if (!taskId) {
throw new Error("RUXA 创建任务成功,但未返回 taskId");
throw new Error(
`RUXA 创建任务成功,但未返回 taskId,响应片段: ${compactPayloadSnippet(createPayload)}`
);
}
outputUrls = await waitForTaskResult(baseURL, apiKey, taskId, imageUrls);
}

View File

@ -12,6 +12,7 @@ export interface CanvasGenerateImageRequest {
negativePrompt?: string;
model?: string;
aspectRatio?: string;
imageSize?: string;
imageCount?: number;
referenceImage?: string;
referenceImages?: string[];
@ -80,6 +81,10 @@ export async function generateCanvasImage(
typeof request.aspectRatio === "string" && request.aspectRatio.trim()
? request.aspectRatio.trim()
: "1:1";
const imageSize =
typeof request.imageSize === "string" && request.imageSize.trim()
? request.imageSize.trim()
: "2K";
const imageCount =
typeof request.imageCount === "number" && Number.isFinite(request.imageCount)
? Math.max(1, Math.min(8, Math.floor(request.imageCount)))
@ -180,7 +185,7 @@ export async function generateCanvasImage(
const imageParams = {
aspectRatio,
imageSize: "2K",
imageSize,
imageCount,
negativePrompt,
};