ai-chatbot/lib/ai/video-model-config.ts
Your Name 477f215259
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: update video billing and agent workspace improvements
2026-04-10 16:25:35 +08:00

107 lines
3.5 KiB
TypeScript

export type VideoDurationCostMap = Record<string, number>;
export type VideoImageGenConfigLike = {
creditCostPerVideo?: number | null;
videoDurationOptions?: number[] | null;
creditCostPerVideoByDuration?: Record<string, number> | null;
};
function normalizePositiveInteger(value: unknown) {
const normalized = Number(value);
if (!Number.isFinite(normalized)) return null;
const rounded = Math.round(normalized);
if (rounded < 1) return null;
return rounded;
}
export function normalizeVideoDurationOptions(value: unknown): number[] {
if (!Array.isArray(value)) return [];
return Array.from(
new Set(
value
.map((item) => normalizePositiveInteger(item))
.filter((item): item is number => item !== null)
)
).sort((left, right) => left - right);
}
export function normalizeVideoDurationCostMap(
value: unknown,
durationOptions?: readonly number[] | null
): VideoDurationCostMap | undefined {
if (!value || typeof value !== "object") return undefined;
const allowedDurations = Array.isArray(durationOptions)
? new Set(durationOptions.map((item) => String(item)))
: null;
const entries = Object.entries(value as Record<string, unknown>)
.map(([durationKey, costValue]) => {
const normalizedDuration = normalizePositiveInteger(durationKey);
const normalizedCost = Number(costValue);
if (normalizedDuration === null || !Number.isFinite(normalizedCost)) {
return null;
}
const durationText = String(normalizedDuration);
if (allowedDurations && !allowedDurations.has(durationText)) {
return null;
}
return [durationText, Math.max(0, Math.round(normalizedCost))] as const;
})
.filter((entry): entry is readonly [string, number] => Boolean(entry));
if (entries.length === 0) return undefined;
return Object.fromEntries(entries);
}
export function getConfiguredVideoDurationOptions(
config: VideoImageGenConfigLike | null | undefined
): number[] {
return normalizeVideoDurationOptions(config?.videoDurationOptions);
}
export function buildVideoDurationCostMap(params: {
durationOptions: readonly number[];
costMap?: unknown;
fallbackCost?: number | null | undefined;
}): VideoDurationCostMap | undefined {
const fallbackCost = Number.isFinite(Number(params.fallbackCost))
? Math.max(0, Math.round(Number(params.fallbackCost)))
: 0;
const normalizedCostMap =
normalizeVideoDurationCostMap(params.costMap, params.durationOptions) || {};
if (params.durationOptions.length === 0 && Object.keys(normalizedCostMap).length === 0) {
return undefined;
}
return Object.fromEntries(
params.durationOptions.map((duration) => {
const key = String(duration);
return [key, normalizedCostMap[key] ?? fallbackCost];
})
);
}
export function getVideoCreditCostFromConfig(
config: VideoImageGenConfigLike | null | undefined,
duration?: number | null
): number {
const normalizedDuration = normalizePositiveInteger(duration);
const durationOptions = getConfiguredVideoDurationOptions(config);
const durationCostMap = normalizeVideoDurationCostMap(
config?.creditCostPerVideoByDuration,
durationOptions
);
if (normalizedDuration !== null && durationCostMap) {
const durationKey = String(normalizedDuration);
if (Object.prototype.hasOwnProperty.call(durationCostMap, durationKey)) {
return durationCostMap[durationKey] || 0;
}
}
const fallbackCost = Number(config?.creditCostPerVideo ?? 0);
return Number.isFinite(fallbackCost) ? Math.max(0, Math.round(fallbackCost)) : 0;
}