mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-12 05:51:44 +08:00
115 lines
2.8 KiB
TypeScript
115 lines
2.8 KiB
TypeScript
import type { CliArgs } from "../types";
|
|
|
|
export function getDefaultModel(): string {
|
|
return process.env.OPENAI_IMAGE_MODEL || "gpt-image-1.5";
|
|
}
|
|
|
|
function parseAspectRatio(ar: string): { width: number; height: number } | null {
|
|
const match = ar.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
|
|
if (!match) return null;
|
|
const w = parseFloat(match[1]!);
|
|
const h = parseFloat(match[2]!);
|
|
if (w <= 0 || h <= 0) return null;
|
|
return { width: w, height: h };
|
|
}
|
|
|
|
type SizeMapping = {
|
|
square: string;
|
|
landscape: string;
|
|
portrait: string;
|
|
};
|
|
|
|
function getOpenAISize(
|
|
model: string,
|
|
ar: string | null,
|
|
quality: CliArgs["quality"]
|
|
): string {
|
|
const isDalle3 = model.includes("dall-e-3");
|
|
const isDalle2 = model.includes("dall-e-2");
|
|
|
|
if (isDalle2) {
|
|
return "1024x1024";
|
|
}
|
|
|
|
const sizes: SizeMapping = isDalle3
|
|
? {
|
|
square: "1024x1024",
|
|
landscape: "1792x1024",
|
|
portrait: "1024x1792",
|
|
}
|
|
: {
|
|
square: "1024x1024",
|
|
landscape: "1536x1024",
|
|
portrait: "1024x1536",
|
|
};
|
|
|
|
if (!ar) return sizes.square;
|
|
|
|
const parsed = parseAspectRatio(ar);
|
|
if (!parsed) return sizes.square;
|
|
|
|
const ratio = parsed.width / parsed.height;
|
|
|
|
if (Math.abs(ratio - 1) < 0.1) return sizes.square;
|
|
if (ratio > 1.5) return sizes.landscape;
|
|
if (ratio < 0.67) return sizes.portrait;
|
|
return sizes.square;
|
|
}
|
|
|
|
export async function generateImage(
|
|
prompt: string,
|
|
model: string,
|
|
args: CliArgs
|
|
): Promise<Uint8Array> {
|
|
const baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
|
|
const apiKey = process.env.OPENAI_API_KEY;
|
|
|
|
if (!apiKey) throw new Error("OPENAI_API_KEY is required");
|
|
|
|
if (args.referenceImages.length > 0) {
|
|
console.error("Warning: Reference images not supported with OpenAI, ignoring.");
|
|
}
|
|
|
|
const size = args.size || getOpenAISize(model, args.aspectRatio, args.quality);
|
|
|
|
const body: Record<string, any> = {
|
|
model,
|
|
prompt,
|
|
size,
|
|
};
|
|
|
|
if (model.includes("dall-e-3")) {
|
|
body.quality = args.quality === "2k" ? "hd" : "standard";
|
|
}
|
|
|
|
const res = await fetch(`${baseURL}/images/generations`, {
|
|
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(`OpenAI API error: ${err}`);
|
|
}
|
|
|
|
const result = (await res.json()) as { data: Array<{ url?: string; b64_json?: string }> };
|
|
const img = result.data[0];
|
|
|
|
if (img?.b64_json) {
|
|
return Uint8Array.from(Buffer.from(img.b64_json, "base64"));
|
|
}
|
|
|
|
if (img?.url) {
|
|
const imgRes = await fetch(img.url);
|
|
if (!imgRes.ok) throw new Error("Failed to download image");
|
|
const buf = await imgRes.arrayBuffer();
|
|
return new Uint8Array(buf);
|
|
}
|
|
|
|
throw new Error("No image in response");
|
|
}
|