187 lines
5.0 KiB
TypeScript
187 lines
5.0 KiB
TypeScript
export const DEFAULT_BFL_MODEL = "flux-2-klein-4b";
|
|
export const DEFAULT_BFL_BASE_URL = "https://api.bfl.ai";
|
|
|
|
export type RealtimeCanvasConfig = {
|
|
enabled?: boolean;
|
|
bflApiKey?: string;
|
|
bflModel?: string;
|
|
bflBaseUrl?: string;
|
|
runwareApiKey?: string;
|
|
runwareModel?: string;
|
|
falApiKey?: string;
|
|
apiKey?: string;
|
|
fallbackEnabled?: boolean;
|
|
fallbackBflApiKey?: string;
|
|
fallbackBflModel?: string;
|
|
fallbackBflBaseUrl?: string;
|
|
fallbackRunwareApiKey?: string;
|
|
fallbackRunwareModel?: string;
|
|
fallbackFalApiKey?: string;
|
|
fallbackApiKey?: string;
|
|
negativePrompt?: string;
|
|
renderMode?: "strict-structure" | "balanced" | "stylize";
|
|
strictSketch?: boolean;
|
|
throttleInterval?: number;
|
|
maxBuffering?: number;
|
|
maxInputResolution?: number;
|
|
jpegQuality?: number;
|
|
defaults?: {
|
|
steps?: number;
|
|
cfgScale?: number;
|
|
strength?: number;
|
|
scheduleMu?: number;
|
|
feedback?: number;
|
|
imageSize?: "square" | "square_hd";
|
|
};
|
|
};
|
|
|
|
export type RealtimeProviderConfig = {
|
|
apiKey: string;
|
|
baseUrl: string;
|
|
model: string;
|
|
source: "primary" | "fallback";
|
|
label: string;
|
|
};
|
|
|
|
function trimString(value: unknown) {
|
|
return String(value || "").trim();
|
|
}
|
|
|
|
export function normalizeBaseUrl(raw: string | undefined) {
|
|
const next = trimString(raw || DEFAULT_BFL_BASE_URL);
|
|
return (next || DEFAULT_BFL_BASE_URL).replace(/\/+$/, "");
|
|
}
|
|
|
|
function hasFallbackIntent(config: RealtimeCanvasConfig) {
|
|
if (config.fallbackEnabled === true) return true;
|
|
if (config.fallbackEnabled === false) return false;
|
|
|
|
return Boolean(
|
|
trimString(
|
|
config.fallbackBflApiKey ||
|
|
config.fallbackRunwareApiKey ||
|
|
config.fallbackFalApiKey ||
|
|
config.fallbackApiKey ||
|
|
config.fallbackBflBaseUrl ||
|
|
config.fallbackBflModel ||
|
|
config.fallbackRunwareModel
|
|
)
|
|
);
|
|
}
|
|
|
|
export function resolvePrimaryRealtimeProvider(
|
|
config: RealtimeCanvasConfig,
|
|
env: NodeJS.ProcessEnv = process.env
|
|
): RealtimeProviderConfig | null {
|
|
const apiKey = trimString(
|
|
config.bflApiKey ||
|
|
config.runwareApiKey ||
|
|
config.falApiKey ||
|
|
config.apiKey ||
|
|
env.BFL_API_KEY ||
|
|
env.BFL_KEY
|
|
);
|
|
if (!apiKey) return null;
|
|
|
|
const model =
|
|
trimString(config.bflModel || config.runwareModel || env.BFL_MODEL || env.RUNWARE_MODEL) ||
|
|
DEFAULT_BFL_MODEL;
|
|
const baseUrl = normalizeBaseUrl(config.bflBaseUrl || env.BFL_BASE_URL);
|
|
|
|
return {
|
|
apiKey,
|
|
model,
|
|
baseUrl,
|
|
source: "primary",
|
|
label: "主实时服务",
|
|
};
|
|
}
|
|
|
|
export function resolveFallbackRealtimeProvider(
|
|
config: RealtimeCanvasConfig
|
|
): RealtimeProviderConfig | null {
|
|
if (!hasFallbackIntent(config)) return null;
|
|
|
|
const apiKey = trimString(
|
|
config.fallbackBflApiKey ||
|
|
config.fallbackRunwareApiKey ||
|
|
config.fallbackFalApiKey ||
|
|
config.fallbackApiKey
|
|
);
|
|
if (!apiKey) return null;
|
|
|
|
const model =
|
|
trimString(config.fallbackBflModel || config.fallbackRunwareModel) || DEFAULT_BFL_MODEL;
|
|
const baseUrl = normalizeBaseUrl(config.fallbackBflBaseUrl || DEFAULT_BFL_BASE_URL);
|
|
|
|
return {
|
|
apiKey,
|
|
model,
|
|
baseUrl,
|
|
source: "fallback",
|
|
label: "备用实时服务",
|
|
};
|
|
}
|
|
|
|
export function resolveRealtimeProviders(
|
|
config: RealtimeCanvasConfig,
|
|
env: NodeJS.ProcessEnv = process.env
|
|
) {
|
|
const providers: RealtimeProviderConfig[] = [];
|
|
const primary = resolvePrimaryRealtimeProvider(config, env);
|
|
const fallback = resolveFallbackRealtimeProvider(config);
|
|
|
|
if (primary) {
|
|
providers.push(primary);
|
|
}
|
|
|
|
if (
|
|
fallback &&
|
|
!providers.some(
|
|
(provider) =>
|
|
provider.apiKey === fallback.apiKey &&
|
|
provider.baseUrl === fallback.baseUrl &&
|
|
provider.model === fallback.model
|
|
)
|
|
) {
|
|
providers.push(fallback);
|
|
}
|
|
|
|
return providers;
|
|
}
|
|
|
|
export function normalizeRealtimeCanvasConfigForStorage(
|
|
value: Record<string, unknown>,
|
|
env: NodeJS.ProcessEnv = process.env
|
|
): RealtimeCanvasConfig {
|
|
const renderMode =
|
|
value.renderMode === "strict-structure" ||
|
|
value.renderMode === "stylize" ||
|
|
value.renderMode === "balanced"
|
|
? value.renderMode
|
|
: "balanced";
|
|
|
|
const primary = resolvePrimaryRealtimeProvider(value as RealtimeCanvasConfig, env);
|
|
const fallback = resolveFallbackRealtimeProvider(value as RealtimeCanvasConfig);
|
|
|
|
return {
|
|
...value,
|
|
bflApiKey: primary?.apiKey || "",
|
|
bflModel: primary?.model || DEFAULT_BFL_MODEL,
|
|
bflBaseUrl: primary?.baseUrl || normalizeBaseUrl(env.BFL_BASE_URL),
|
|
runwareApiKey: primary?.apiKey || "",
|
|
falApiKey: primary?.apiKey || "",
|
|
apiKey: primary?.apiKey || "",
|
|
runwareModel: primary?.model || DEFAULT_BFL_MODEL,
|
|
fallbackEnabled: Boolean(fallback),
|
|
fallbackBflApiKey: fallback?.apiKey || "",
|
|
fallbackBflModel: fallback?.model || DEFAULT_BFL_MODEL,
|
|
fallbackBflBaseUrl: fallback?.baseUrl || DEFAULT_BFL_BASE_URL,
|
|
fallbackRunwareApiKey: fallback?.apiKey || "",
|
|
fallbackFalApiKey: fallback?.apiKey || "",
|
|
fallbackApiKey: fallback?.apiKey || "",
|
|
fallbackRunwareModel: fallback?.model || DEFAULT_BFL_MODEL,
|
|
renderMode,
|
|
};
|
|
}
|