haixunMaster/lib/ai/generate-draft-image.ts

87 lines
2.6 KiB
TypeScript
Raw Normal View History

2026-06-21 12:50:31 +00:00
import { experimental_generateImage as generateImage, generateText } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
import { createXai } from "@ai-sdk/xai";
import type { ImageModelV1 } from "@ai-sdk/provider";
import type { ProviderApiKeys } from "./keys";
import { resolveApiKey } from "./keys";
import { getModel } from "./provider";
import { sanitizePromptText } from "@/lib/utils";
function getImageModel(aiProvider: string, apiKeys: ProviderApiKeys): ImageModelV1 {
const xaiKey = resolveApiKey("xai", apiKeys);
const openaiKey = resolveApiKey("openai", apiKeys);
if (aiProvider === "openai" && openaiKey) {
return createOpenAI({ apiKey: openaiKey }).image("dall-e-3");
}
if (xaiKey) {
return createXai({ apiKey: xaiKey }).image("grok-2-image");
}
if (openaiKey) {
return createOpenAI({ apiKey: openaiKey }).image("dall-e-3");
}
throw new Error("請在設定頁填入 xAI 或 OpenAI API key 以使用 AI 生圖");
}
async function buildImagePrompt(input: {
text: string;
hook?: string | null;
imageBrief?: string | null;
angle?: string | null;
aiProvider: string;
aiModel: string;
apiKeys: ProviderApiKeys;
}): Promise<string> {
const model = getModel(input.aiProvider, input.aiModel, input.apiKeys);
const context = sanitizePromptText(
[
input.angle ? `切角:${input.angle}` : "",
input.hook ? `開頭:${input.hook}` : "",
input.imageBrief ? `配圖說明:${input.imageBrief}` : "",
`貼文:${input.text}`,
]
.filter(Boolean)
.join("\n")
);
const { text } = await generateText({
model,
system:
"You write concise English prompts for social media image generation. Output only the prompt, no markdown.",
prompt: `Create one image generation prompt for a Threads post visual.
Requirements:
- Warm, modern, mobile-friendly composition
- No real celebrity faces or logos
- Minimal readable text on image (prefer no text)
- Match the mood of this Traditional Chinese post
${context}`,
});
const prompt = text.trim();
if (!prompt) {
throw new Error("無法產生繪圖提示詞");
}
return prompt.slice(0, 1200);
}
export async function generateDraftImage(input: {
text: string;
hook?: string | null;
imageBrief?: string | null;
angle?: string | null;
aiProvider: string;
aiModel: string;
apiKeys: ProviderApiKeys;
}) {
const prompt = await buildImagePrompt(input);
const model = getImageModel(input.aiProvider, input.apiKeys);
const { image } = await generateImage({
model,
prompt,
aspectRatio: "1:1",
});
return { image, prompt };
}