fix: support all image providers for canvas text edit
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-04-28 15:23:05 +08:00
parent 7bf3363211
commit 7d51b980b9

View File

@ -2,7 +2,18 @@ import { NextResponse } from "next/server";
import { z } from "zod";
import { auth } from "@/app/(auth)/auth";
import { getEnabledAiModelsWithProvider } from "@/lib/db/queries";
import { createGeminiImage } from "@/lib/ai/providers/gemini-image";
import { createHuankeGemini } from "@/lib/ai/providers/huanke-gemini";
import { createAlibabaImage } from "@/lib/ai/providers/alibaba-image";
import { createBflImage } from "@/lib/ai/providers/bfl-image";
import { createRuxaImage } from "@/lib/ai/providers/ruxa-image";
import { createFalImage } from "@/lib/ai/providers/fal-image";
import { createOpenAIImage } from "@/lib/ai/providers/openai-image";
import { createMidjourney } from "@/lib/ai/providers/midjourney";
import { createRunway } from "@/lib/ai/providers/runway";
import { createKrea } from "@/lib/ai/providers/krea";
import { createVolcanoVision } from "@/lib/ai/providers/volcano-vision";
import { isImageGenerationModelDescriptor } from "@/lib/ai/model-classification";
export const runtime = "nodejs";
export const maxDuration = 120;
@ -19,9 +30,35 @@ const requestSchema = z.object({
items: z.array(textItemSchema).min(1, "至少需要一条文字修改"),
});
const SUPPORTED_TEXT_EDIT_PROVIDER_TYPES = new Set([
"gemini",
"grsai-gpt-image",
"huanke-gemini",
"alibaba-image",
"bfl-image",
"ruxa-image",
"fal-image",
"openai-image",
"midjourney",
"runway",
"krea",
"volcano-vision",
]);
function pickTextEditModel(models: Awaited<ReturnType<typeof getEnabledAiModelsWithProvider>>) {
const candidates = models
.filter((item) => item.model.supportsImageGen && item.provider.type === "volcano-vision")
.filter(
(item) =>
isImageGenerationModelDescriptor({
providerType: item.provider.type,
modelId: item.model.modelId,
capabilities: item.model.capabilities,
supportsImageGen: item.model.supportsImageGen,
}) &&
item.model.isEnabled &&
item.provider.isEnabled &&
SUPPORTED_TEXT_EDIT_PROVIDER_TYPES.has(item.provider.type),
)
.sort((a, b) => {
const aScore =
(a.model.isDefaultTextEditModel ? 100 : 0) +
@ -39,6 +76,41 @@ function pickTextEditModel(models: Awaited<ReturnType<typeof getEnabledAiModelsW
return candidates[0];
}
function createTextEditProvider(
provider: Awaited<ReturnType<typeof getEnabledAiModelsWithProvider>>[number]["provider"],
) {
const baseURL = provider.baseUrl;
const apiKey = provider.apiKey;
switch (provider.type) {
case "gemini":
case "grsai-gpt-image":
return createGeminiImage({ baseURL, apiKey });
case "huanke-gemini":
return createHuankeGemini({ baseURL, apiKey });
case "alibaba-image":
return createAlibabaImage({ baseURL, apiKey });
case "bfl-image":
return createBflImage({ baseURL, apiKey });
case "ruxa-image":
return createRuxaImage({ baseURL, apiKey });
case "fal-image":
return createFalImage({ baseURL, apiKey });
case "openai-image":
return createOpenAIImage({ baseURL, apiKey });
case "midjourney":
return createMidjourney({ baseURL, apiKey });
case "runway":
return createRunway({ baseURL, apiKey });
case "krea":
return createKrea({ baseURL, apiKey });
case "volcano-vision":
return createVolcanoVision({ baseURL, apiKey });
default:
throw new Error(`当前文字编辑暂不支持服务商类型:${provider.type}`);
}
}
function parseImageFromMarkdown(markdown: string): { originalUrl: string; thumbnailUrl: string } {
const strictMatch = markdown.match(/!\[thumb\|\|\|([^|]+)\|\|\|[^\]]*\]\(([^)]+)\)/);
if (strictMatch) {
@ -156,7 +228,7 @@ export async function POST(request: Request) {
return NextResponse.json(
{
success: false,
error: "未配置可用的文字编辑生图模型,请在系统设置-AI 模型中启用一个火山生图模型并设为默认文字编辑生图模型",
error: "未配置可用的文字编辑生图模型,请在系统设置-AI 模型中启用一个支持图片生成的模型并设为默认文字编辑生图模型",
},
{ status: 500 },
);
@ -171,10 +243,7 @@ export async function POST(request: Request) {
isDefaultTextEditModel: selected.model.isDefaultTextEditModel,
});
const provider = createVolcanoVision({
baseURL: selected.provider.baseUrl,
apiKey: selected.provider.apiKey,
});
const provider = createTextEditProvider(selected.provider);
const prompt = buildTextEditPrompt(replacements, deletions);