diff --git a/app/api/canvas/3d/tasks/[taskId]/route.ts b/app/api/canvas/3d/tasks/[taskId]/route.ts index e85c9ca25..cb9af5f55 100644 --- a/app/api/canvas/3d/tasks/[taskId]/route.ts +++ b/app/api/canvas/3d/tasks/[taskId]/route.ts @@ -17,7 +17,6 @@ import { resolveTripoProviderConfigForTaskId, } from "@/lib/tripo/server"; import type { TripoTaskData } from "@/lib/tripo/types"; -import { getConfiguredVip98kTaskStatus } from "@/lib/ai/vip98k-runtime"; export const runtime = "nodejs"; export const maxDuration = 120; @@ -320,111 +319,6 @@ export async function GET(request: Request, { params }: RouteParams) { const { searchParams } = new URL(request.url); const preferredProviderId = searchParams.get("providerId"); - if (normalizedTaskId.startsWith("vip98k:")) { - const [, encodedProviderId, ...remoteTaskParts] = normalizedTaskId.split(":"); - const remoteTaskId = remoteTaskParts.join(":"); - if (!encodedProviderId || !remoteTaskId) { - return NextResponse.json( - { success: false, error: "VIP98K 3D taskId 格式无效" }, - { status: 400 }, - ); - } - const result = await getConfiguredVip98kTaskStatus({ - taskId: remoteTaskId, - preferredProviderId: encodedProviderId, - }); - const modelUrl = - result.outputUrls.find((url) => - /\.(?:glb|gltf|fbx|obj|stl|3mf|usdz|ply|zip)(?:[?#].*)?$/i.test(url), - ) || result.outputUrls[0] || null; - const previewImageUrl = - result.outputUrls.find((url) => - /\.(?:png|jpe?g|webp|gif)(?:[?#].*)?$/i.test(url), - ) || null; - const stableModelUrl = await archiveTripoModelIfNeeded({ - taskId: remoteTaskId, - cacheKey: `vip98k:${remoteTaskId}:model`, - sourceUrl: modelUrl, - filenamePrefix: `vip98k-model-${remoteTaskId}`, - fallbackExtension: "glb", - }); - const stablePreviewImageUrl = await archiveTripoModelIfNeeded({ - taskId: remoteTaskId, - cacheKey: `vip98k:${remoteTaskId}:preview`, - sourceUrl: previewImageUrl, - filenamePrefix: `vip98k-preview-${remoteTaskId}`, - fallbackExtension: "jpg", - }); - const errorMessage = result.failed ? "VIP98K 3D 任务执行失败" : null; - const refundedPoints = result.failed - ? await refundModel3DPointsForFailedTask({ - userId: session.user.id, - taskId: normalizedTaskId, - errorMessage: errorMessage || "VIP98K 3D task failed", - }) - : 0; - return NextResponse.json({ - success: true, - data: { - taskId: normalizedTaskId, - type: "model", - status: result.failed ? "failed" : result.done ? "success" : result.status || "running", - progress: result.done ? 100 : null, - output: result.payload, - modelUrl: stableModelUrl, - previewImageUrl: stablePreviewImageUrl, - assetPayload: { - providerId: result.providerId, - providerName: result.providerName, - storageArchivedAt: stableModelUrl || stablePreviewImageUrl ? new Date().toISOString() : null, - }, - errorCode: result.failed ? "VIP98K_TASK_FAILED" : null, - errorMessage, - refundedPoints, - }, - }); - } - - if (normalizedTaskId.startsWith("vip98k-direct:")) { - const [, encodedProviderId, encodedUrls] = normalizedTaskId.split(":"); - if (!encodedProviderId || !encodedUrls) { - return NextResponse.json( - { success: false, error: "VIP98K 3D direct taskId 格式无效" }, - { status: 400 }, - ); - } - const outputUrls = JSON.parse( - Buffer.from(encodedUrls, "base64url").toString("utf8"), - ) as string[]; - const modelUrl = - outputUrls.find((url) => - /\.(?:glb|gltf|fbx|obj|stl|3mf|usdz|ply|zip)(?:[?#].*)?$/i.test(url), - ) || outputUrls[0] || null; - const previewImageUrl = - outputUrls.find((url) => /\.(?:png|jpe?g|webp|gif)(?:[?#].*)?$/i.test(url)) || - null; - return NextResponse.json({ - success: true, - data: { - taskId: normalizedTaskId, - type: "model", - status: "success", - progress: 100, - output: { urls: outputUrls }, - modelUrl, - previewImageUrl, - assetPayload: { - providerId: encodedProviderId, - providerName: "VIP98K", - storageArchivedAt: null, - }, - errorCode: null, - errorMessage: null, - refundedPoints: 0, - }, - }); - } - const config = preferredProviderId ? await getTripoProviderConfig(preferredProviderId) : await resolveTripoProviderConfigForTaskId(normalizedTaskId); diff --git a/app/api/canvas/3d/tasks/route.ts b/app/api/canvas/3d/tasks/route.ts index 40ba8db01..ec3479a19 100644 --- a/app/api/canvas/3d/tasks/route.ts +++ b/app/api/canvas/3d/tasks/route.ts @@ -14,10 +14,6 @@ import { resolveTripoProviderConfigForTaskId, } from "@/lib/tripo/server"; import { TripoApiError, tripoRequest } from "@/lib/tripo/client"; -import { - getVip98kRuntimeProvider, - submitConfiguredVip98kTask, -} from "@/lib/ai/vip98k-runtime"; export const runtime = "nodejs"; export const maxDuration = 60; @@ -120,13 +116,6 @@ function assignMapped( } } -const VIP98K_TEXT_TO_3D_MODEL = "tencent/hunyuan3d-pro/text-to-3d"; -const VIP98K_IMAGE_TO_3D_MODEL = "tencent/hunyuan3d-pro/image-to-3d"; - -function encodeVip98kDirectResult(outputUrls: string[]) { - return Buffer.from(JSON.stringify(outputUrls), "utf8").toString("base64url"); -} - export async function POST(request: Request) { try { const session = await auth(); @@ -426,73 +415,6 @@ export async function POST(request: Request) { } const preferredProviderId = readOptionalTrimmedText(body.providerId); - let vip98kProvider: Awaited> | null = null; - try { - vip98kProvider = await getVip98kRuntimeProvider(preferredProviderId); - } catch { - vip98kProvider = null; - } - - if (vip98kProvider) { - if (taskType !== "text_to_model" && taskType !== "image_to_model") { - return NextResponse.json( - { - success: false, - error: "VIP98K 当前仅支持文生 3D 和图生 3D;细化、动画和格式转换暂不支持", - }, - { status: 400 }, - ); - } - - const inputFile = body.file && typeof body.file === "object" ? body.file : null; - const imageUrl = String(body.imageUrl || inputFile?.url || "").trim(); - if (taskType === "image_to_model" && !imageUrl) { - return NextResponse.json( - { success: false, error: "VIP98K 图生 3D 缺少可访问的图片地址" }, - { status: 400 }, - ); - } - const vipModelId = - taskType === "image_to_model" - ? VIP98K_IMAGE_TO_3D_MODEL - : VIP98K_TEXT_TO_3D_MODEL; - const submission = await submitConfiguredVip98kTask({ - preferredProviderId: vip98kProvider.id, - modelId: vipModelId, - kind: "model-3d", - prompt: String(body.prompt || "Generate a production-ready 3D model"), - imageUrls: imageUrl ? [imageUrl] : [], - }); - if (!submission.taskId && submission.outputUrls.length === 0) { - throw new Error("VIP98K 3D 任务未返回 taskId 或模型地址"); - } - const taskId = submission.taskId - ? `vip98k:${vip98kProvider.id}:${submission.taskId}` - : `vip98k-direct:${vip98kProvider.id}:${encodeVip98kDirectResult(submission.outputUrls)}`; - - if (pointsCharge.chargeable) { - await pointsCharge.consume({ - note: `3D模型任务消耗积分(${billableTaskLabel} / ${taskId})`, - }).catch((error) => { - logger.error("VIP98K points consume failed", error, { - taskId, - userId: session.user.id, - }); - }); - } - - return NextResponse.json({ - success: true, - data: { - taskId, - status: submission.outputUrls.length > 0 ? "success" : "queued", - providerId: vip98kProvider.id, - providerName: vip98kProvider.name, - raw: submission.payload, - }, - }); - } - const dependencyTaskId = taskType === "refine_model" ? String(body.draftModelTaskId || "").trim() diff --git a/app/api/canvas/3d/upload-image/route.ts b/app/api/canvas/3d/upload-image/route.ts index 1a65ad438..42bdd6f68 100644 --- a/app/api/canvas/3d/upload-image/route.ts +++ b/app/api/canvas/3d/upload-image/route.ts @@ -2,8 +2,6 @@ import { NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { getTripoProviderConfig } from "@/lib/tripo/server"; import { TripoApiError, tripoRequest } from "@/lib/tripo/client"; -import { getVip98kRuntimeProvider } from "@/lib/ai/vip98k-runtime"; -import { uploadFile } from "@/lib/storage/service"; export const runtime = "nodejs"; export const maxDuration = 60; @@ -53,26 +51,7 @@ export async function POST(request: Request) { ); } - let config: Awaited> | null = null; - try { - config = await getTripoProviderConfig(providerId); - } catch { - const vip98k = await getVip98kRuntimeProvider(providerId); - const extension = file.type === "image/png" ? "png" : file.type === "image/webp" ? "webp" : "jpg"; - const uploaded = await uploadFile( - `vip98k-3d-input-${Date.now()}-${crypto.randomUUID()}.${extension}`, - await file.arrayBuffer(), - ); - return NextResponse.json({ - success: true, - data: { - imageToken: null, - file: { type: extension, url: uploaded.url }, - providerId: vip98k.id, - providerName: vip98k.name, - }, - }); - } + const config = await getTripoProviderConfig(providerId); const uploadPaths = ["/upload/sts", "/upload"]; let lastError: unknown = null; diff --git a/scripts/consolidate-ai-providers.ts b/scripts/consolidate-ai-providers.ts index 781a3236c..dc67f193c 100644 --- a/scripts/consolidate-ai-providers.ts +++ b/scripts/consolidate-ai-providers.ts @@ -12,7 +12,12 @@ import { dotenv.config({ path: ".env.local" }); dotenv.config(); -const KEEP_PROVIDER_TYPES = new Set(["vip98k", "fal-image", "fal-video"]); +const KEEP_PROVIDER_TYPES = new Set([ + "vip98k", + "fal-image", + "fal-video", + "tripo-3d", +]); const REQUIRED_CAPABILITY_MODELS = [ "nano-banana-2", "remove-background",