ai-chatbot/lib/realtime/config.ts
Your Name e300153a01
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
Add realtime canvas failover config
2026-03-11 09:49:56 +08:00

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,
};
}