130 lines
3.7 KiB
TypeScript
130 lines
3.7 KiB
TypeScript
import "server-only";
|
|
|
|
import {
|
|
getAiProviderById,
|
|
getAiProvidersByType,
|
|
} from "@/lib/db/queries";
|
|
import { DEFAULT_WORLDLABS_BASE_URL } from "./client";
|
|
import type { WorldLabsProviderConfig } from "./types";
|
|
let worldLabsProviderCursor = 0;
|
|
|
|
export function sanitizeWorldLabsApiKey(value: string): string {
|
|
return String(value || "")
|
|
.replace(/[\u2028\u2029]/g, "")
|
|
.replace(/[\u0000-\u001F\u007F]/g, "")
|
|
.replace(/\s+/g, "")
|
|
.trim();
|
|
}
|
|
|
|
export function sanitizeWorldLabsBaseUrl(value: string): string {
|
|
return String(value || "")
|
|
.replace(/[\u2028\u2029]/g, "")
|
|
.replace(/[\u0000-\u001F\u007F]/g, "")
|
|
.trim();
|
|
}
|
|
|
|
function providerToWorldLabsConfig(
|
|
provider: Awaited<ReturnType<typeof getAiProvidersByType>>[number] | undefined
|
|
): WorldLabsProviderConfig | null {
|
|
if (!provider) return null;
|
|
const providerApiKey = sanitizeWorldLabsApiKey(String(provider.apiKey || ""));
|
|
if (!providerApiKey) return null;
|
|
return {
|
|
apiKey: providerApiKey,
|
|
baseUrl:
|
|
sanitizeWorldLabsBaseUrl(String(provider.baseUrl || "")) ||
|
|
DEFAULT_WORLDLABS_BASE_URL,
|
|
providerId: provider.id,
|
|
providerName: provider.name,
|
|
} satisfies WorldLabsProviderConfig;
|
|
}
|
|
|
|
export async function getWorldLabsProviderConfigs(): Promise<
|
|
WorldLabsProviderConfig[]
|
|
> {
|
|
const legacyProviders = await getAiProvidersByType({
|
|
type: "worldlabs-3d",
|
|
enabledOnly: true,
|
|
});
|
|
const providers =
|
|
legacyProviders.length > 0
|
|
? legacyProviders
|
|
: await getAiProvidersByType({
|
|
type: "vip98k",
|
|
enabledOnly: true,
|
|
});
|
|
|
|
const normalizedProviders = providers
|
|
.map((provider) => providerToWorldLabsConfig(provider))
|
|
.filter((item): item is WorldLabsProviderConfig => Boolean(item));
|
|
|
|
if (normalizedProviders.length > 0) {
|
|
return normalizedProviders;
|
|
}
|
|
|
|
const envApiKey = sanitizeWorldLabsApiKey(String(process.env.WORLDLABS_API_KEY || ""));
|
|
if (!envApiKey) {
|
|
return [];
|
|
}
|
|
|
|
return [
|
|
{
|
|
apiKey: envApiKey,
|
|
baseUrl:
|
|
sanitizeWorldLabsBaseUrl(String(process.env.WORLDLABS_BASE_URL || "")) ||
|
|
DEFAULT_WORLDLABS_BASE_URL,
|
|
providerName: "WORLDLABS_API_KEY",
|
|
},
|
|
];
|
|
}
|
|
|
|
export async function getWorldLabsProviderConfig(
|
|
preferredProviderId?: string | null,
|
|
options?: { includeDisabledPreferred?: boolean }
|
|
): Promise<WorldLabsProviderConfig> {
|
|
const providers = await getWorldLabsProviderConfigs();
|
|
const preferredId = String(preferredProviderId || "").trim();
|
|
if (preferredId) {
|
|
const matchedProvider =
|
|
providers.find((item) => item.providerId === preferredId) || null;
|
|
if (matchedProvider) {
|
|
return matchedProvider;
|
|
}
|
|
if (options?.includeDisabledPreferred) {
|
|
const disabledOrEnabledProvider = providerToWorldLabsConfig(
|
|
await getAiProviderById({ id: preferredId })
|
|
);
|
|
if (disabledOrEnabledProvider) {
|
|
return disabledOrEnabledProvider;
|
|
}
|
|
}
|
|
}
|
|
|
|
const worldLabsProvider =
|
|
providers.length > 0
|
|
? providers[worldLabsProviderCursor % providers.length]
|
|
: null;
|
|
if (providers.length > 0) {
|
|
worldLabsProviderCursor = (worldLabsProviderCursor + 1) % providers.length;
|
|
}
|
|
|
|
if (worldLabsProvider) {
|
|
return worldLabsProvider;
|
|
}
|
|
|
|
throw new Error("未配置 3D 世界服务,请启用 VIP98K 服务商并同步 world-generation 模型");
|
|
}
|
|
|
|
export async function reportWorldLabsProviderRuntimeFailure(
|
|
providerId?: string | null,
|
|
reason?: string
|
|
) {
|
|
const normalizedProviderId = String(providerId || "").trim();
|
|
if (!normalizedProviderId) return;
|
|
|
|
console.warn("[WorldLabsPool] Provider reported a runtime failure; keeping enabled", {
|
|
providerId: normalizedProviderId,
|
|
reason,
|
|
});
|
|
}
|