diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx index 703991a8a..6bad710d9 100644 --- a/app/(admin)/settings/admin-settings-client.tsx +++ b/app/(admin)/settings/admin-settings-client.tsx @@ -435,7 +435,7 @@ const AVAILABLE_TOOLS = [ const PROVIDER_TYPES = [ { value: "alibaba", label: "阿里云通义千问" }, { value: "alibaba-image", label: "阿里生图模型" }, // 🆕 千问图片生成/编辑 - { value: "ruxa-image", label: "RUXA 生图模型" }, + { value: "ruxa-image", label: "RUXA生图" }, { value: "fal-image", label: "FAL 生图模型" }, { value: "tripo-3d", label: "Tripo3D 模型生成" }, { value: "zhipu", label: "智普官方 4.7" }, diff --git a/app/api/admin/ai-providers/route.ts b/app/api/admin/ai-providers/route.ts index 9b2c1bf5b..ac9ddca71 100644 --- a/app/api/admin/ai-providers/route.ts +++ b/app/api/admin/ai-providers/route.ts @@ -53,6 +53,7 @@ const ZHIPU_BASE_URL = "https://open.bigmodel.cn/api/paas/v4"; const VOLCANO_MULTIMODAL_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"; const DOUBAO_VIDEO_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"; const RUXA_VIDEO_BASE_URL = "https://api.ruxa.ai"; +const RUXA_IMAGE_BASE_URL = "https://api.ruxa.ai"; const TRIPO_3D_BASE_URL = "https://api.tripo3d.ai/v2/openapi"; function normalizeProviderTypeForStorage( @@ -162,6 +163,8 @@ export async function POST(request: Request) { ? data.baseUrl || DOUBAO_VIDEO_BASE_URL : data.type === "ruxa-video" ? data.baseUrl || RUXA_VIDEO_BASE_URL + : data.type === "ruxa-image" + ? data.baseUrl || RUXA_IMAGE_BASE_URL : data.type === "tripo-3d" ? data.baseUrl || TRIPO_3D_BASE_URL : data.baseUrl, diff --git a/lib/ai/providers/ruxa-image.ts b/lib/ai/providers/ruxa-image.ts index dac1de838..ef1ddaeed 100644 --- a/lib/ai/providers/ruxa-image.ts +++ b/lib/ai/providers/ruxa-image.ts @@ -20,7 +20,7 @@ interface UploadedImage { original: string; } -const DEFAULT_ASPECT_RATIO = "auto"; +const DEFAULT_SIZE = "1:1"; const DEFAULT_RESOLUTION = "1k"; const DEFAULT_OUTPUT_FORMAT = "png"; const POLL_INTERVAL_MS = 2000; @@ -50,14 +50,14 @@ function joinApiPath(baseURL: string, path: string) { } function normalizeAspectRatio(aspectRatio?: string) { - if (!aspectRatio) return DEFAULT_ASPECT_RATIO; + if (!aspectRatio) return DEFAULT_SIZE; const normalized = aspectRatio.trim().toLowerCase(); - if (normalized === "auto") return "auto"; + if (normalized === "auto") return DEFAULT_SIZE; const ratio = aspectRatio.trim(); - if (["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"].includes(ratio)) { + if (["1:1", "16:9", "9:16", "4:3", "3:4", "2:3", "3:2"].includes(ratio)) { return ratio; } - return DEFAULT_ASPECT_RATIO; + return DEFAULT_SIZE; } function normalizeResolution(imageSize?: string) { @@ -67,10 +67,11 @@ function normalizeResolution(imageSize?: string) { return DEFAULT_RESOLUTION; } -function normalizeOutputFormat(outputFormat?: string): "png" | "jpg" { +function normalizeOutputFormat(outputFormat?: string): "png" | "jpg" | "webp" { if (!outputFormat) return DEFAULT_OUTPUT_FORMAT; const normalized = outputFormat.trim().toLowerCase(); if (normalized === "jpg" || normalized === "jpeg") return "jpg"; + if (normalized === "webp") return "webp"; return "png"; } @@ -168,10 +169,6 @@ function extractPromptAndImages(messages: Array<{ role: string; content: any }>) throw new Error("No prompt found in user message"); } - if (imageUrls.length === 0) { - throw new Error("RUXA 图片编辑模型需要至少 1 张输入图片"); - } - return { prompt, imageUrls }; } @@ -361,18 +358,25 @@ export function createRuxaImage({ apiKey, baseURL }: RuxaImageConfig) { const aspectRatio = normalizeAspectRatio(options.imageParams?.aspectRatio); const resolution = normalizeResolution(options.imageParams?.imageSize); const outputFormat = normalizeOutputFormat(options.imageParams?.outputFormat); - const activeModelId = options.overrideModelId || modelId; + const activeModelId = options.overrideModelId || modelId || "gpt-image-1.5"; const createEndpoint = joinApiPath(baseURL, "/api/v1/tasks/create"); const callbackUrl = process.env.RUXA_CALLBACK_URL?.trim(); + const input: Record = { + prompt, + size: aspectRatio, + aspect_ratio: aspectRatio, + resolution, + output_format: outputFormat, + }; + if (imageUrls[0]) { + input.image = imageUrls[0]; + } + if (imageUrls.length > 1) { + input.image_input = imageUrls; + } const requestBody = { - input: { - prompt, - image_input: imageUrls, - aspect_ratio: aspectRatio, - resolution, - output_format: outputFormat, - }, + input, model: activeModelId, ...(callbackUrl ? { callBackUrl: callbackUrl } : {}), };