ai-chatbot/lib/ai/image-model-pricing.ts
Your Name 1b1ff65de8
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: add discounted model pricing and fix realtime 3d media handling
2026-05-13 20:02:52 +08:00

105 lines
3.2 KiB
TypeScript

import { getVideoCreditCostFromConfig, type VideoImageGenConfigLike } from "@/lib/ai/video-model-config";
export type DiscountableImageGenConfigLike = VideoImageGenConfigLike & {
creditCostPerImage?: number | null;
creditDiscountRate?: number | null;
};
function normalizeRoundedCost(value: unknown) {
const normalized = Number(value);
if (!Number.isFinite(normalized)) return 0;
return Math.max(0, Math.round(normalized));
}
export function normalizeImageModelCreditDiscountRate(value: unknown) {
const normalized = Number(value);
if (!Number.isFinite(normalized)) return null;
const rounded = Math.round(normalized * 10) / 10;
if (rounded <= 0 || rounded >= 10) return null;
return rounded;
}
export function formatImageModelCreditDiscountLabel(value: unknown) {
const normalized = normalizeImageModelCreditDiscountRate(value);
if (normalized === null) return null;
const displayValue = Number.isInteger(normalized)
? String(normalized)
: normalized.toFixed(1).replace(/\.0$/, "");
return `${displayValue}折`;
}
export function getImageModelCreditDiscountLabel(
config: DiscountableImageGenConfigLike | null | undefined
) {
return formatImageModelCreditDiscountLabel(config?.creditDiscountRate);
}
export function applyImageModelCreditDiscount(
rawCost: unknown,
config: DiscountableImageGenConfigLike | null | undefined
) {
const baseCost = normalizeRoundedCost(rawCost);
if (baseCost <= 0) return 0;
const normalizedDiscountRate = normalizeImageModelCreditDiscountRate(
config?.creditDiscountRate
);
if (normalizedDiscountRate === null) return baseCost;
return Math.max(0, Math.round((baseCost * normalizedDiscountRate) / 10));
}
export function getGenerationCreditCostFromImageGenConfig(
config: DiscountableImageGenConfigLike | null | undefined,
target: "image" | "video",
options?: { duration?: number | null; resolution?: string | null }
) {
const sourceValue =
target === "video"
? (() => {
const resolvedVideoCost = getVideoCreditCostFromConfig(
config,
options?.duration,
options?.resolution
);
if (resolvedVideoCost > 0) return resolvedVideoCost;
return config?.creditCostPerVideo ?? config?.creditCostPerImage ?? 0;
})()
: config?.creditCostPerImage ?? 0;
return applyImageModelCreditDiscount(sourceValue, config);
}
export function getImageCreditCostFromConfig(
config: DiscountableImageGenConfigLike | null | undefined
) {
return getGenerationCreditCostFromImageGenConfig(config, "image");
}
export function getModelGenerationCreditCost(
modelConfig: { imageGenConfig?: DiscountableImageGenConfigLike | null } | null | undefined,
target: "image" | "video",
options?: { duration?: number | null; resolution?: string | null }
) {
return getGenerationCreditCostFromImageGenConfig(
modelConfig?.imageGenConfig,
target,
options
);
}
export function normalizeImageGenPricingConfig<
T extends DiscountableImageGenConfigLike | null | undefined,
>(config: T): T {
if (!config) return config;
const normalizedDiscountRate = normalizeImageModelCreditDiscountRate(
config.creditDiscountRate
);
return {
...config,
creditDiscountRate: normalizedDiscountRate ?? undefined,
} as T;
}