Browse Source

Honor requested image resolution for Seedream

Seedream 5.0 rejects the previous default 1024x1024 size when the user selects high resolution output. The NewApiWG image endpoint now derives a provider pixel size from the visible resolution and aspect-ratio controls unless the model schema supplied an explicit size.

Constraint: Keep the fix server-side and preserve explicit model size parameters when provided.

Rejected: Change the composer resolution UI for one model | the UI already sends the intended 4K and 16:9 values; the loss happened in provider payload mapping.

Confidence: high

Scope-risk: narrow

Directive: Do not default OpenAI-compatible image endpoints to 1024x1024 when resolution/imageSize is present.

Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts

Tested: npm run test:run -- src/app/api/generate/__tests__/route.test.ts

Tested: npm run build

Tested: git diff --check

Not-tested: Live Seedream 5.0 paid generation call.
feature/canvas-chatbot-copilot
jiajia 2 months ago
parent
commit
428a57c795
  1. 23
      src/app/api/generate/providers/__tests__/newapiwg.test.ts
  2. 51
      src/app/api/generate/providers/newapiwg.ts

23
src/app/api/generate/providers/__tests__/newapiwg.test.ts

@ -375,6 +375,29 @@ describe("NewApiWG generation payloads", () => {
expect(capturedBody!.image).toBe("data:image/png;base64,input");
});
it("maps Seedream image resolution and aspect ratio to provider pixel size", async () => {
const result = await generateWithNewApiWG("test-key", "https://newapi.example/v1", makeInput({
model: {
id: "apiyi_seedream5.0",
name: "APIYI Seedream 5.0",
description: null,
provider: "newapiwg",
capabilities: ["text-to-image", "image-to-image"],
},
prompt: "draw a landscape",
parameters: {
resolution: "4K",
imageSize: "4K",
aspectRatio: "16:9",
aspect_ratio: "16:9",
},
}));
expect(result.success).toBe(true);
expect(capturedBody!.model).toBe("apiyi_seedream5.0");
expect(capturedBody!.size).toBe("3840x2160");
});
it("blocks cached Doubao chat models before calling media generation endpoints", async () => {
const result = await generateWithNewApiWG("test-key", "https://newapi.example/v1", makeInput({
model: {

51
src/app/api/generate/providers/newapiwg.ts

@ -421,6 +421,13 @@ const NEWAPIWG_VIDEO_AUDIO_INPUTS = new Set([
"reference_audio_urls",
]);
const NEWAPIWG_IMAGE_RESOLUTION_PIXELS: Record<string, number> = {
"512": 512 * 512,
"1K": 1024 * 1024,
"2K": 2560 * 1440,
"4K": 3840 * 2160,
};
function asStringArray(value: string | string[] | undefined): string[] {
if (typeof value === "string") return value.length > 0 ? [value] : [];
if (Array.isArray(value)) return value.filter((item) => typeof item === "string" && item.length > 0);
@ -471,6 +478,48 @@ function buildNewApiWGImageInputs(input: GenerationInput): Record<string, string
return { image: input.images.length === 1 ? input.images[0] : input.images };
}
function getStringParameter(parameters: Record<string, unknown> | undefined, key: string): string | null {
const value = parameters?.[key];
return typeof value === "string" && value.length > 0 ? value : null;
}
function parseAspectRatio(value: string | null): { width: number; height: number } {
const match = value?.match(/^(\d+):(\d+)$/);
if (!match) return { width: 1, height: 1 };
const width = Number(match[1]);
const height = Number(match[2]);
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
return { width: 1, height: 1 };
}
return { width, height };
}
function ceilToMultiple(value: number, multiple: number): number {
return Math.ceil(value / multiple) * multiple;
}
function resolveNewApiWGImageSize(parameters: Record<string, unknown> | undefined): string {
const explicitSize = getStringParameter(parameters, "size");
if (explicitSize) return explicitSize;
const requestedSize =
getStringParameter(parameters, "imageSize") ??
getStringParameter(parameters, "resolution");
if (!requestedSize) return "1024x1024";
if (/^\d+x\d+$/i.test(requestedSize)) return requestedSize;
const targetPixels = NEWAPIWG_IMAGE_RESOLUTION_PIXELS[requestedSize];
if (!targetPixels) return "1024x1024";
const ratio = parseAspectRatio(
getStringParameter(parameters, "aspectRatio") ??
getStringParameter(parameters, "aspect_ratio")
);
const width = ceilToMultiple(Math.sqrt(targetPixels * ratio.width / ratio.height), 8);
const height = ceilToMultiple(Math.sqrt(targetPixels * ratio.height / ratio.width), 8);
return `${width}x${height}`;
}
function isImagenGenerationModel(modelId: string): boolean {
return /(^|[\/_\-.])imagen(\d|[\/_\-.]|$)/i.test(modelId);
}
@ -800,7 +849,7 @@ export async function generateWithNewApiWG(
}
const endpoint = `${normalizedBaseUrl}/${isVideo ? "video" : "images"}/generations`;
const imageSize = typeof input.parameters?.size === "string" ? input.parameters.size : "1024x1024";
const imageSize = resolveNewApiWGImageSize(input.parameters);
const rawDynamicInputs = dynamicInputsWithoutPrompt(input);
const normalizedVideoInputs = isVideo
? normalizeNewApiWGVideoInputs(input, rawDynamicInputs)

Loading…
Cancel
Save