From 7d51b980b91bce0e5fd69603fc798024e8b73aae Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 28 Apr 2026 15:23:05 +0800 Subject: [PATCH] fix: support all image providers for canvas text edit --- app/api/canvas/text-edit/route.ts | 81 ++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/app/api/canvas/text-edit/route.ts b/app/api/canvas/text-edit/route.ts index 1ffb68119..489538eaa 100644 --- a/app/api/canvas/text-edit/route.ts +++ b/app/api/canvas/text-edit/route.ts @@ -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>) { 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>[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);