diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx
index b55835116..87ce33728 100644
--- a/app/(admin)/settings/admin-settings-client.tsx
+++ b/app/(admin)/settings/admin-settings-client.tsx
@@ -1337,6 +1337,7 @@ export function AdminSettingsClient() {
const [enhanceProConfig, setEnhanceProConfig] = useState(
defaultEnhanceProKreaConfig()
);
+ const [enhanceProApiKeysDraft, setEnhanceProApiKeysDraft] = useState("");
const [enhanceProPresetEditorMode, setEnhanceProPresetEditorMode] = useState<
"simple" | "advanced"
>("simple");
@@ -1449,6 +1450,10 @@ export function AdminSettingsClient() {
const [threeDModelAliasConfig, setThreeDModelAliasConfig] = useState(
defaultThreeDModelAliasConfig()
);
+ const [tripoProviderSearch, setTripoProviderSearch] = useState("");
+ const [tripoProviderBusyId, setTripoProviderBusyId] = useState<
+ string | null
+ >(null);
const [worldlabsProviderSearch, setWorldlabsProviderSearch] = useState("");
const [worldlabsProviderBusyId, setWorldlabsProviderBusyId] = useState<
string | null
@@ -1564,8 +1569,17 @@ export function AdminSettingsClient() {
setEnhanceProConfig(
normalizeEnhanceProKreaConfig(enhanceProConfigData.setting?.value)
);
+ setEnhanceProApiKeysDraft("");
}, [enhanceProConfigData]);
+ const enhanceProApiKeysDraftCount = useMemo(
+ () =>
+ normalizeEnhanceProKreaConfig({
+ apiKeys: enhanceProApiKeysDraft,
+ }).apiKeys.length,
+ [enhanceProApiKeysDraft]
+ );
+
useEffect(() => {
if (enhanceProConfig.presets.length === 0) {
if (selectedEnhanceProPresetId) {
@@ -1976,6 +1990,28 @@ export function AdminSettingsClient() {
);
}, [threeDModelAliasConfigData]);
+ const tripoProviders = useMemo(
+ () =>
+ (providersData?.providers || []).filter(
+ (provider) => provider.type === "tripo-3d"
+ ),
+ [providersData?.providers]
+ );
+ const filteredTripoProviders = useMemo(() => {
+ const keyword = tripoProviderSearch.trim().toLowerCase();
+ if (!keyword) return tripoProviders;
+
+ return tripoProviders.filter((provider) =>
+ [provider.name, provider.apiKey, provider.baseUrl]
+ .filter(Boolean)
+ .some((value) => value.toLowerCase().includes(keyword))
+ );
+ }, [tripoProviderSearch, tripoProviders]);
+ const enabledTripoProviderCount = useMemo(
+ () => tripoProviders.filter((provider) => provider.isEnabled).length,
+ [tripoProviders]
+ );
+
const worldlabsProviders = useMemo(
() =>
(providersData?.providers || []).filter(
@@ -3138,13 +3174,28 @@ export function AdminSettingsClient() {
const handleSaveEnhanceProConfig = async () => {
try {
+ const hasEnhanceProApiKeysDraft =
+ enhanceProApiKeysDraft.trim().length > 0;
+ const normalizedConfig = normalizeEnhanceProKreaConfig({
+ ...enhanceProConfig,
+ providerName:
+ enhanceProConfig.providerName.trim() || "Krea Topaz Generative",
+ baseUrl:
+ enhanceProConfig.baseUrl.trim() ||
+ ENHANCE_PRO_KREA_DEFAULT_BASE_URL,
+ apiKey: enhanceProConfig.apiKey.trim(),
+ apiKeys: hasEnhanceProApiKeysDraft
+ ? enhanceProApiKeysDraft
+ : enhanceProConfig.apiKeys,
+ });
+
if (enhanceProConfig.enabled) {
- if (!enhanceProConfig.baseUrl.trim()) {
+ if (!normalizedConfig.baseUrl) {
toast.error("请输入 Krea API 地址");
return;
}
- if (!enhanceProConfig.apiKey.trim()) {
- toast.error("请输入 Krea API Key");
+ if (!normalizedConfig.apiKey && normalizedConfig.apiKeys.length === 0) {
+ toast.error("请输入 Krea API Key 或 Key 池");
return;
}
}
@@ -3154,22 +3205,7 @@ export function AdminSettingsClient() {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
key: ENHANCE_PRO_KREA_SETTING_KEY,
- value: {
- enabled: enhanceProConfig.enabled,
- providerName:
- enhanceProConfig.providerName.trim() || "Krea Topaz Generative",
- baseUrl:
- enhanceProConfig.baseUrl.trim() ||
- ENHANCE_PRO_KREA_DEFAULT_BASE_URL,
- apiKey: enhanceProConfig.apiKey.trim(),
- defaultModel: enhanceProConfig.defaultModel,
- defaultOutputFormat: enhanceProConfig.defaultOutputFormat,
- allowBatchSize: enhanceProConfig.allowBatchSize,
- maxBatchSize: enhanceProConfig.maxBatchSize,
- maxOutputEdge: enhanceProConfig.maxOutputEdge,
- enableAdvancedControls: enhanceProConfig.enableAdvancedControls,
- presets: enhanceProConfig.presets,
- },
+ value: normalizedConfig,
}),
});
@@ -4322,6 +4358,82 @@ export function AdminSettingsClient() {
}
};
+ const handleAddTripo3dPoolKey = async () => {
+ try {
+ const trimmedName = tripo3dConfig.name.trim() || "Tripo3D Key";
+ const trimmedBaseUrl =
+ tripo3dConfig.baseUrl.trim() || getProviderDefaultBaseUrl("tripo-3d");
+ const trimmedApiKey = tripo3dConfig.apiKey.trim();
+
+ if (!trimmedApiKey) {
+ toast.error("请填写要加入 Key 池的 Tripo3D API Key");
+ return;
+ }
+
+ const response = await fetch("/api/admin/ai-providers", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ name: trimmedName,
+ type: "tripo-3d",
+ baseUrl: trimmedBaseUrl,
+ apiKey: trimmedApiKey,
+ isEnabled: tripo3dConfig.isEnabled,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("添加失败");
+ }
+
+ toast.success("Tripo3D Key 已加入池");
+ setTripo3dConfig((prev) => ({ ...prev, apiKey: "" }));
+ mutateProviders();
+ } catch (error) {
+ toast.error("添加 Tripo3D Key 失败");
+ }
+ };
+
+ const handleEditTripoPoolProvider = (provider: AiProvider) => {
+ setTripo3dConfig({
+ providerId: provider.id,
+ name: provider.name || "Tripo3D",
+ baseUrl: provider.baseUrl || getProviderDefaultBaseUrl("tripo-3d"),
+ apiKey: "",
+ isEnabled: provider.isEnabled,
+ });
+ };
+
+ const handleToggleTripoPoolProvider = async (
+ provider: AiProvider,
+ isEnabled: boolean
+ ) => {
+ setTripoProviderBusyId(provider.id);
+ try {
+ const response = await fetch("/api/admin/ai-providers", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ id: provider.id,
+ isEnabled,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("更新状态失败");
+ }
+
+ toast.success(
+ `已${isEnabled ? "启用" : "停用"} ${provider.name || "Tripo3D Key"}`
+ );
+ mutateProviders();
+ } catch (error) {
+ toast.error("更新 Tripo3D Key 状态失败");
+ } finally {
+ setTripoProviderBusyId(null);
+ }
+ };
+
const handleSaveWorldlabs3dConfig = async () => {
try {
const trimmedName = worldlabs3dConfig.name.trim() || "World Labs";
@@ -13244,6 +13356,101 @@ export function AdminSettingsClient() {
}
/>
+
+
+
+
Tripo3D Key 池
+
+ 当前共 {tripoProviders.length} 个 Key,已启用{" "}
+ {enabledTripoProviderCount} 个。生成任务会轮询使用可用 Key。
+
+
+
+
+ setTripoProviderSearch(e.target.value)}
+ />
+
+
+
+
+ {filteredTripoProviders.length ? (
+ filteredTripoProviders.map((provider) => (
+
+
+
+
{provider.name}
+
+ {provider.isEnabled ? "已启用" : "已禁用"}
+
+
+
+ Key: {provider.apiKey}
+
+
+ API: {provider.baseUrl}
+
+
+ 创建时间: {formatBeijingTime(provider.createdAt)}
+
+
+
+
+
+
+ void handleToggleTripoPoolProvider(
+ provider,
+ checked
+ )
+ }
+ />
+
+ {provider.isEnabled ? "启用中" : "已停用"}
+
+
+
+
+
+ ))
+ ) : (
+
+ {tripoProviders.length
+ ? "没有匹配到符合筛选条件的 Tripo3D Key。"
+ : "当前还没有导入任何 Tripo3D Key。"}
+
+ )}
+
+
@@ -13263,9 +13470,14 @@ export function AdminSettingsClient() {
-
+
+
+
+
@@ -14595,6 +14807,52 @@ export function AdminSettingsClient() {
/>
+
+
diff --git a/app/api/admin/settings/route.ts b/app/api/admin/settings/route.ts
index e2e683f5d..9dabc5a44 100644
--- a/app/api/admin/settings/route.ts
+++ b/app/api/admin/settings/route.ts
@@ -252,8 +252,8 @@ export async function POST(request: Request) {
if (!normalizedValue.baseUrl) {
return new ChatSDKError("bad_request:api", "请填写 Krea API 地址").toResponse();
}
- if (!normalizedValue.apiKey) {
- return new ChatSDKError("bad_request:api", "请填写 Krea API Key").toResponse();
+ if (!normalizedValue.apiKey && normalizedValue.apiKeys.length === 0) {
+ return new ChatSDKError("bad_request:api", "请填写 Krea API Key 或 Key 池").toResponse();
}
}
diff --git a/app/api/canvas/3d/tasks/[taskId]/route.ts b/app/api/canvas/3d/tasks/[taskId]/route.ts
index 6410d662c..0aa9e9e1e 100644
--- a/app/api/canvas/3d/tasks/[taskId]/route.ts
+++ b/app/api/canvas/3d/tasks/[taskId]/route.ts
@@ -140,7 +140,7 @@ interface RouteParams {
params: Promise<{ taskId: string }>;
}
-export async function GET(_request: Request, { params }: RouteParams) {
+export async function GET(request: Request, { params }: RouteParams) {
try {
const session = await auth();
if (!session?.user?.id) {
@@ -153,7 +153,8 @@ export async function GET(_request: Request, { params }: RouteParams) {
return NextResponse.json({ success: false, error: "缺少 taskId" }, { status: 400 });
}
- const config = await getTripoProviderConfig();
+ const { searchParams } = new URL(request.url);
+ const config = await getTripoProviderConfig(searchParams.get("providerId"));
const result = await tripoRequest(config, {
method: "GET",
path: `/task/${encodeURIComponent(normalizedTaskId)}`,
diff --git a/app/api/canvas/3d/tasks/route.ts b/app/api/canvas/3d/tasks/route.ts
index ad4b02089..b9db18be7 100644
--- a/app/api/canvas/3d/tasks/route.ts
+++ b/app/api/canvas/3d/tasks/route.ts
@@ -36,6 +36,7 @@ type CreateTaskRequest = {
files?: Array>;
imageTokens?: string[];
imageUrls?: string[];
+ providerId?: string;
originalModelTaskId?: string;
draftModelTaskId?: string;
format?: "GLTF" | "USDZ" | "FBX" | "OBJ" | "STL" | "3MF";
@@ -390,7 +391,9 @@ export async function POST(request: Request) {
}
}
- const config = await getTripoProviderConfig();
+ const config = await getTripoProviderConfig(
+ readOptionalTrimmedText(body.providerId)
+ );
const result = await tripoRequest>(config, {
method: "POST",
path: "/task",
@@ -443,6 +446,8 @@ export async function POST(request: Request) {
data: {
taskId,
status: String(result.data?.status || "queued"),
+ providerId: config.providerId || null,
+ providerName: config.providerName || null,
raw: result.data,
},
traceId: result.traceId,
diff --git a/app/api/canvas/3d/upload-image/route.ts b/app/api/canvas/3d/upload-image/route.ts
index 575bb5dcd..42bdd6f68 100644
--- a/app/api/canvas/3d/upload-image/route.ts
+++ b/app/api/canvas/3d/upload-image/route.ts
@@ -32,6 +32,7 @@ export async function POST(request: Request) {
const formData = await request.formData();
const file = formData.get("file");
+ const providerId = String(formData.get("providerId") || "").trim();
if (!(file instanceof File)) {
return NextResponse.json({ success: false, error: "请上传图片文件" }, { status: 400 });
}
@@ -50,7 +51,7 @@ export async function POST(request: Request) {
);
}
- const config = await getTripoProviderConfig();
+ const config = await getTripoProviderConfig(providerId);
const uploadPaths = ["/upload/sts", "/upload"];
let lastError: unknown = null;
@@ -70,6 +71,8 @@ export async function POST(request: Request) {
data: {
imageToken,
file: result.data,
+ providerId: config.providerId || null,
+ providerName: config.providerName || null,
},
traceId: result.traceId,
});
diff --git a/app/api/enhance/pro/route.ts b/app/api/enhance/pro/route.ts
index aeef26628..d757309d8 100644
--- a/app/api/enhance/pro/route.ts
+++ b/app/api/enhance/pro/route.ts
@@ -1,3 +1,4 @@
+import { createHash } from "node:crypto";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/app/(auth)/auth";
import { db } from "@/lib/db";
@@ -27,6 +28,7 @@ const ENHANCE_PRO_SYNC_WAIT_TIMEOUT_MS = 20_000;
const ENHANCE_PRO_TASK_POLL_INTERVAL_MS = 1_500;
const ENHANCE_PRO_MODEL_ID = "krea-topaz-generative";
const ENHANCE_PRO_MAX_RESULT_IMAGES = 4;
+let enhanceProKeyPoolCursor = 0;
type EnhanceProRequestBody = {
imageUrl?: string;
@@ -83,13 +85,55 @@ function normalizeJobStatus(value: unknown) {
return String(value || "").trim().toLowerCase();
}
-function buildEnhanceProTaskPendingResponse(taskId: string) {
+function getEnhanceProKeyFingerprint(apiKey: string) {
+ return createHash("sha256")
+ .update(String(apiKey || "").trim())
+ .digest("hex")
+ .slice(0, 16);
+}
+
+function getEnhanceProEffectiveApiKeys(config: EnhanceProKreaConfig) {
+ const pooledKeys = Array.isArray(config.apiKeys) ? config.apiKeys : [];
+ if (pooledKeys.length > 0) return pooledKeys;
+ return config.apiKey ? [config.apiKey] : [];
+}
+
+function resolveEnhanceProApiKey(
+ config: EnhanceProKreaConfig,
+ preferredKeyId?: string | null,
+) {
+ const keys = getEnhanceProEffectiveApiKeys(config);
+ if (keys.length === 0) return null;
+
+ const normalizedPreferredKeyId = String(preferredKeyId || "").trim();
+ if (normalizedPreferredKeyId) {
+ const matchedKey = keys.find(
+ (apiKey) => getEnhanceProKeyFingerprint(apiKey) === normalizedPreferredKeyId,
+ );
+ if (matchedKey) {
+ return {
+ apiKey: matchedKey,
+ keyId: normalizedPreferredKeyId,
+ };
+ }
+ }
+
+ const selectedKey = keys[enhanceProKeyPoolCursor % keys.length];
+ enhanceProKeyPoolCursor = (enhanceProKeyPoolCursor + 1) % keys.length;
+ return {
+ apiKey: selectedKey,
+ keyId: getEnhanceProKeyFingerprint(selectedKey),
+ };
+}
+
+function buildEnhanceProTaskPendingResponse(taskId: string, taskKeyId?: string | null) {
return NextResponse.json(
{
success: true,
processing: true,
status: "running",
taskId,
+ taskKeyId,
pollAfterMs: ENHANCE_PRO_TASK_POLL_INTERVAL_MS,
},
{ status: 202 }
@@ -513,16 +557,20 @@ export async function GET(request: NextRequest) {
if (!config.enabled) {
return NextResponse.json({ error: "图像增强 Pro 功能未开启" }, { status: 403 });
}
- if (!config.apiKey || !config.baseUrl) {
+ const keySelection = resolveEnhanceProApiKey(config, searchParams.get("taskKeyId"));
+ if (!keySelection || !config.baseUrl) {
return NextResponse.json({ error: "图像增强 Pro API 尚未配置" }, { status: 400 });
}
let taskResult: any;
try {
- taskResult = await pollEnhanceProJob(config, taskId);
+ taskResult = await pollEnhanceProJob(
+ { ...config, apiKey: keySelection.apiKey },
+ taskId
+ );
} catch (pollError) {
if (isEnhanceProTaskStillProcessing(pollError)) {
- return buildEnhanceProTaskPendingResponse(taskId);
+ return buildEnhanceProTaskPendingResponse(taskId, keySelection.keyId);
}
return NextResponse.json(
{
@@ -569,9 +617,11 @@ export async function POST(request: NextRequest) {
if (!config.enabled) {
return NextResponse.json({ error: "图像增强 Pro 功能未开启" }, { status: 403 });
}
- if (!config.apiKey || !config.baseUrl) {
+ const keySelection = resolveEnhanceProApiKey(config);
+ if (!keySelection || !config.baseUrl) {
return NextResponse.json({ error: "图像增强 Pro API 尚未配置" }, { status: 400 });
}
+ const requestConfig = { ...config, apiKey: keySelection.apiKey };
const body = (await request.json()) as EnhanceProRequestBody;
const prompt = String(body.prompt || "").trim();
@@ -628,7 +678,7 @@ export async function POST(request: NextRequest) {
const startTime = Date.now();
const createdJob = await createEnhanceProJob({
- config,
+ config: requestConfig,
requestBody: {
...body,
batchSize,
@@ -683,7 +733,7 @@ export async function POST(request: NextRequest) {
}
try {
- const taskResult = await pollEnhanceProJob(config, taskId);
+ const taskResult = await pollEnhanceProJob(requestConfig, taskId);
const responsePayload = await finalizeEnhanceProTaskResult({
userId: session.user.id,
taskResult,
@@ -701,7 +751,7 @@ export async function POST(request: NextRequest) {
});
} catch (pollError) {
if (isEnhanceProTaskStillProcessing(pollError)) {
- return buildEnhanceProTaskPendingResponse(taskId);
+ return buildEnhanceProTaskPendingResponse(taskId, keySelection.keyId);
}
return NextResponse.json(
diff --git a/app/canvas/[id]/model3d/page.tsx b/app/canvas/[id]/model3d/page.tsx
index 761aa2bd6..ce07fc1f5 100644
--- a/app/canvas/[id]/model3d/page.tsx
+++ b/app/canvas/[id]/model3d/page.tsx
@@ -110,6 +110,10 @@ type GenerationMode = "text" | "image" | "multiview" | "batch";
type WorkspaceMode = "model" | "world";
type WorldGenerationMode = "text" | "image" | "multi-image" | "video";
type WorldViewportMode = "world" | "imagery" | "mesh" | "splat";
+type Model3DTaskAssetPayload = WorldLabsTaskAssetPayload & {
+ providerId?: string | null;
+ providerName?: string | null;
+};
type TaskProvider = "tripo-3d" | "worldlabs-3d";
type TaskMode =
| GenerationMode
@@ -142,7 +146,7 @@ type TaskRecord = {
error?: string | null;
sourceTaskId?: string | null;
exportFormat?: string | null;
- assetPayload?: WorldLabsTaskAssetPayload | null;
+ assetPayload?: Model3DTaskAssetPayload | null;
shareEnabled?: boolean;
shareToken?: string | null;
sharedAt?: number | null;
@@ -403,9 +407,9 @@ function normalizeTaskProvider(provider: unknown): TaskProvider {
function normalizeAssetPayload(
payload: unknown
-): WorldLabsTaskAssetPayload | null {
+): Model3DTaskAssetPayload | null {
return payload && typeof payload === "object" && !Array.isArray(payload)
- ? (payload as WorldLabsTaskAssetPayload)
+ ? (payload as Model3DTaskAssetPayload)
: null;
}
@@ -1086,7 +1090,9 @@ export default function CanvasModel3DPage() {
? await worldLabsGenerationService.getTask(currentTask.taskId, {
providerId: currentTask.assetPayload?.providerId,
})
- : await model3DGenerationService.getTask(currentTask.taskId);
+ : await model3DGenerationService.getTask(currentTask.taskId, {
+ providerId: currentTask.assetPayload?.providerId,
+ });
if (!result.success || !result.data) {
const failMessage = result.error || "任务查询失败";
const failedRecord: TaskRecord = {
@@ -1295,7 +1301,7 @@ export default function CanvasModel3DPage() {
modelLabel: string;
sourceTaskId?: string | null;
exportFormat?: string | null;
- assetPayload?: WorldLabsTaskAssetPayload | null;
+ assetPayload?: Model3DTaskAssetPayload | null;
}) => {
const createResult =
params.provider === "worldlabs-3d"
@@ -1309,6 +1315,27 @@ export default function CanvasModel3DPage() {
"assetPayload" in createResult.data
? createResult.data
: null;
+ const tripoCreateResultData =
+ params.provider === "tripo-3d"
+ ? (createResult.data as {
+ providerId?: string | null;
+ providerName?: string | null;
+ })
+ : null;
+ const tripoAssetPayload =
+ params.provider === "tripo-3d"
+ ? {
+ ...(params.assetPayload || {}),
+ providerId:
+ tripoCreateResultData?.providerId ||
+ params.assetPayload?.providerId ||
+ null,
+ providerName:
+ tripoCreateResultData?.providerName ||
+ params.assetPayload?.providerName ||
+ null,
+ }
+ : null;
const newTask: TaskRecord = {
taskId: createResult.data.taskId,
@@ -1323,7 +1350,7 @@ export default function CanvasModel3DPage() {
assetPayload:
params.provider === "worldlabs-3d"
? worldCreateResultData?.assetPayload ?? params.assetPayload ?? null
- : params.assetPayload ?? null,
+ : tripoAssetPayload,
};
setCurrentTask(newTask);
upsertTaskHistory(newTask);
@@ -1419,12 +1446,19 @@ export default function CanvasModel3DPage() {
} else {
throw new Error("参考图上传未返回可用 token");
}
+ if (uploadResult.data.providerId) {
+ payload.providerId = uploadResult.data.providerId;
+ }
await submitTask({
provider: "tripo-3d",
payload,
mode: "image",
promptText: normalizedPrompt || "图生3D",
modelLabel: modelVersion,
+ assetPayload: {
+ providerId: uploadResult.data.providerId || null,
+ providerName: uploadResult.data.providerName || null,
+ },
});
toast.success("已提交图生3D任务");
return;
@@ -1446,12 +1480,19 @@ export default function CanvasModel3DPage() {
} else {
throw new Error(`图片未返回可用 token: ${file.name}`);
}
+ if (uploadResult.data.providerId) {
+ payload.providerId = uploadResult.data.providerId;
+ }
await submitTask({
provider: "tripo-3d",
payload,
mode: "batch",
promptText: `${normalizedPrompt || "批量图生3D"} · ${file.name}`,
modelLabel: modelVersion,
+ assetPayload: {
+ providerId: uploadResult.data.providerId || null,
+ providerName: uploadResult.data.providerName || null,
+ },
});
successCount += 1;
}
@@ -1460,11 +1501,21 @@ export default function CanvasModel3DPage() {
}
setUploadingImage(true);
- const uploadResults = await Promise.all(
- selectedMultiFiles.map(async (file) =>
- model3DGenerationService.uploadImage(file)
- )
- );
+ const uploadResults: Array<
+ Awaited>
+ > = [];
+ let boundTripoProviderId: string | null = null;
+ let boundTripoProviderName: string | null = null;
+ for (const file of selectedMultiFiles) {
+ const result = await model3DGenerationService.uploadImage(file, {
+ providerId: boundTripoProviderId,
+ });
+ if (result.success && result.data?.providerId && !boundTripoProviderId) {
+ boundTripoProviderId = result.data.providerId;
+ boundTripoProviderName = result.data.providerName || null;
+ }
+ uploadResults.push(result);
+ }
const filesPayload: Array> = [];
for (const result of uploadResults) {
if (!result.success || !result.data) {
@@ -1484,12 +1535,19 @@ export default function CanvasModel3DPage() {
}
const payload = buildGenerationPayload("multiview_to_model");
payload.files = filesPayload;
+ if (boundTripoProviderId) {
+ payload.providerId = boundTripoProviderId;
+ }
await submitTask({
provider: "tripo-3d",
payload,
mode: "multiview",
promptText: normalizedPrompt || "多视图3D",
modelLabel: modelVersion,
+ assetPayload: {
+ providerId: boundTripoProviderId,
+ providerName: boundTripoProviderName,
+ },
});
toast.success("已提交多视图3D任务");
} catch (error) {
diff --git a/components/home/lovart-home.tsx b/components/home/lovart-home.tsx
index 75d7173a7..4b78c2c16 100644
--- a/components/home/lovart-home.tsx
+++ b/components/home/lovart-home.tsx
@@ -1415,7 +1415,7 @@ export function LovartHome({
-