feat: add qwen dashscope api compatibility with model routing
This commit is contained in:
parent
12208621c9
commit
7a8a8ffd27
|
|
@ -45,6 +45,14 @@ const STANDARD_SIZES_2K: [number, number][] = [
|
|||
[2048, 2048],
|
||||
];
|
||||
|
||||
const QWEN_SIZES: [number, number][] = [
|
||||
[1664, 928],
|
||||
[1472, 1104],
|
||||
[1328, 1328],
|
||||
[1104, 1472],
|
||||
[928, 1664],
|
||||
];
|
||||
|
||||
function getSizeFromAspectRatio(ar: string | null, quality: CliArgs["quality"]): string {
|
||||
const is2k = quality === "2k";
|
||||
const defaultSize = is2k ? "1536*1536" : "1024*1024";
|
||||
|
|
@ -75,21 +83,40 @@ function normalizeSize(size: string): string {
|
|||
return size.replace("x", "*");
|
||||
}
|
||||
|
||||
export async function generateImage(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) throw new Error("DASHSCOPE_API_KEY is required");
|
||||
function isQwenModel(model: string): boolean {
|
||||
return model.startsWith("qwen-image-");
|
||||
}
|
||||
|
||||
if (args.referenceImages.length > 0) {
|
||||
throw new Error(
|
||||
"Reference images are not supported with DashScope provider in baoyu-image-gen. Use --provider google with a Gemini multimodal model."
|
||||
);
|
||||
function getQwenSizeFromAspectRatio(ar: string | null): string {
|
||||
const defaultSize = "1328*1328";
|
||||
|
||||
if (!ar) return defaultSize;
|
||||
|
||||
const parsed = parseAspectRatio(ar);
|
||||
if (!parsed) return defaultSize;
|
||||
|
||||
const targetRatio = parsed.width / parsed.height;
|
||||
|
||||
let best = defaultSize;
|
||||
let bestDiff = Infinity;
|
||||
|
||||
for (const [w, h] of QWEN_SIZES) {
|
||||
const diff = Math.abs(w / h - targetRatio);
|
||||
if (diff < bestDiff) {
|
||||
bestDiff = diff;
|
||||
best = `${w}*${h}`;
|
||||
}
|
||||
}
|
||||
|
||||
const size = args.size ? normalizeSize(args.size) : getSizeFromAspectRatio(args.aspectRatio, args.quality);
|
||||
return best;
|
||||
}
|
||||
|
||||
async function generateWithQwenApi(
|
||||
prompt: string,
|
||||
model: string,
|
||||
size: string,
|
||||
apiKey: string
|
||||
): Promise<Uint8Array> {
|
||||
const url = `${getBaseUrl()}/api/v1/services/aigc/multimodal-generation/generation`;
|
||||
|
||||
const body = {
|
||||
|
|
@ -104,11 +131,12 @@ export async function generateImage(
|
|||
},
|
||||
parameters: {
|
||||
prompt_extend: false,
|
||||
watermark: false,
|
||||
size,
|
||||
},
|
||||
};
|
||||
|
||||
console.log(`Generating image with DashScope (${model})...`, { size });
|
||||
console.log(`Generating image with DashScope Qwen API (${model})...`, { size });
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
|
|
@ -163,3 +191,106 @@ export async function generateImage(
|
|||
|
||||
return Uint8Array.from(Buffer.from(imageData, "base64"));
|
||||
}
|
||||
|
||||
async function generateWithLegacyApi(
|
||||
prompt: string,
|
||||
model: string,
|
||||
size: string,
|
||||
apiKey: string
|
||||
): Promise<Uint8Array> {
|
||||
const url = `${getBaseUrl()}/api/v1/services/aigc/multimodal-generation/generation`;
|
||||
|
||||
const body = {
|
||||
model,
|
||||
input: {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [{ text: prompt }],
|
||||
},
|
||||
],
|
||||
},
|
||||
parameters: {
|
||||
prompt_extend: false,
|
||||
size,
|
||||
},
|
||||
};
|
||||
|
||||
console.log(`Generating image with DashScope Legacy API (${model})...`, { size });
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.text();
|
||||
throw new Error(`DashScope API error (${res.status}): ${err}`);
|
||||
}
|
||||
|
||||
const result = await res.json() as {
|
||||
output?: {
|
||||
result_image?: string;
|
||||
choices?: Array<{
|
||||
message?: {
|
||||
content?: Array<{ image?: string }>;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
|
||||
let imageData: string | null = null;
|
||||
|
||||
if (result.output?.result_image) {
|
||||
imageData = result.output.result_image;
|
||||
} else if (result.output?.choices?.[0]?.message?.content) {
|
||||
const content = result.output.choices[0].message.content;
|
||||
for (const item of content) {
|
||||
if (item.image) {
|
||||
imageData = item.image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!imageData) {
|
||||
console.error("Response:", JSON.stringify(result, null, 2));
|
||||
throw new Error("No image in response");
|
||||
}
|
||||
|
||||
if (imageData.startsWith("http://") || imageData.startsWith("https://")) {
|
||||
const imgRes = await fetch(imageData);
|
||||
if (!imgRes.ok) throw new Error("Failed to download image");
|
||||
const buf = await imgRes.arrayBuffer();
|
||||
return new Uint8Array(buf);
|
||||
}
|
||||
|
||||
return Uint8Array.from(Buffer.from(imageData, "base64"));
|
||||
}
|
||||
|
||||
export async function generateImage(
|
||||
prompt: string,
|
||||
model: string,
|
||||
args: CliArgs
|
||||
): Promise<Uint8Array> {
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) throw new Error("DASHSCOPE_API_KEY is required");
|
||||
|
||||
if (args.referenceImages.length > 0) {
|
||||
throw new Error(
|
||||
"Reference images are not supported with DashScope provider in baoyu-image-gen. Use --provider google with a Gemini multimodal model."
|
||||
);
|
||||
}
|
||||
|
||||
if (isQwenModel(model)) {
|
||||
const size = args.size ? normalizeSize(args.size) : getQwenSizeFromAspectRatio(args.aspectRatio);
|
||||
return generateWithQwenApi(prompt, model, size, apiKey);
|
||||
} else {
|
||||
const size = args.size ? normalizeSize(args.size) : getSizeFromAspectRatio(args.aspectRatio, args.quality);
|
||||
return generateWithLegacyApi(prompt, model, size, apiKey);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue