Expose enterprise video pricing config
This commit is contained in:
@@ -21,6 +21,53 @@ const ENTERPRISE_VIDEO_ALLOWED_MODELS = new Set([
|
||||
|
||||
const CREDITS_PER_CNY = 100;
|
||||
const CREDIT_UNITS_PER_CREDIT = 100;
|
||||
const ENTERPRISE_VIDEO_RESOLUTIONS = ["720P", "1080P"];
|
||||
const ENTERPRISE_VIDEO_DEFAULT_RESOLUTION = "1080P";
|
||||
|
||||
const ENTERPRISE_VIDEO_PRICING_RULES = [
|
||||
{
|
||||
id: "happyhorse",
|
||||
modelIncludes: ["happyhorse"],
|
||||
rates: { "720P": 0.72, "1080P": 1.28 },
|
||||
},
|
||||
{
|
||||
id: "wanxiang-i2v",
|
||||
modelIncludes: ["wan2.7-i2v", "wanxiang"],
|
||||
rates: { "720P": 0.6, "1080P": 1 },
|
||||
},
|
||||
{
|
||||
id: "wan-animate-s2v",
|
||||
modelIncludes: ["animate-mix", "s2v"],
|
||||
rates: { "720P": 0.6, "1080P": 1 },
|
||||
},
|
||||
{
|
||||
id: "kling-muted-reference",
|
||||
modelIncludes: ["kling"],
|
||||
when: { muted: true, hasReferenceVideo: true },
|
||||
rates: { "720P": 0.9, "1080P": 1.2 },
|
||||
},
|
||||
{
|
||||
id: "kling-muted",
|
||||
modelIncludes: ["kling"],
|
||||
when: { muted: true, hasReferenceVideo: false },
|
||||
rates: { "720P": 0.6, "1080P": 0.8 },
|
||||
},
|
||||
{
|
||||
id: "kling-default",
|
||||
modelIncludes: ["kling"],
|
||||
rates: { "720P": 0.9, "1080P": 1.2 },
|
||||
},
|
||||
{
|
||||
id: "vidu",
|
||||
modelIncludes: ["vidu"],
|
||||
rates: { "720P": 0.6, "1080P": 1 },
|
||||
},
|
||||
{
|
||||
id: "pixverse",
|
||||
modelIncludes: ["pixverse"],
|
||||
rates: { "720P": 0.6, "1080P": 1 },
|
||||
},
|
||||
];
|
||||
|
||||
function normalizeModel(value) {
|
||||
return String(value || "").trim().toLowerCase();
|
||||
@@ -36,6 +83,21 @@ function normalizeEnterpriseVideoDuration(value) {
|
||||
return Math.max(1, Math.ceil(numeric));
|
||||
}
|
||||
|
||||
function enterpriseVideoPricingRuleMatches(rule, input, model) {
|
||||
if (!rule.modelIncludes.some((pattern) => model.includes(pattern))) return false;
|
||||
if (!rule.when) return true;
|
||||
if (Object.prototype.hasOwnProperty.call(rule.when, "muted") && Boolean(input.muted) !== rule.when.muted) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(rule.when, "hasReferenceVideo") &&
|
||||
Boolean(input.hasReferenceVideo) !== rule.when.hasReferenceVideo
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isEnterpriseVideoBillingUser(user) {
|
||||
return Boolean(user?.enterpriseId);
|
||||
}
|
||||
@@ -69,33 +131,10 @@ function getEnterpriseVideoCreditRate(input) {
|
||||
const resolution = normalizeEnterpriseVideoResolution(input.resolution || input.quality);
|
||||
const model = normalizeModel(input.model || input.requestedModel);
|
||||
|
||||
if (model.includes("happyhorse")) {
|
||||
return resolution === "720P" ? 0.72 : 1.28;
|
||||
}
|
||||
|
||||
if (model.includes("wan2.7-i2v") || model.includes("wanxiang")) {
|
||||
return resolution === "720P" ? 0.6 : 1;
|
||||
}
|
||||
|
||||
if (model.includes("animate-mix") || model.includes("s2v")) {
|
||||
return resolution === "720P" ? 0.6 : 1;
|
||||
}
|
||||
|
||||
if (model.includes("kling")) {
|
||||
if (input.muted) {
|
||||
if (input.hasReferenceVideo) return resolution === "720P" ? 0.9 : 1.2;
|
||||
return resolution === "720P" ? 0.6 : 0.8;
|
||||
}
|
||||
return resolution === "720P" ? 0.9 : 1.2;
|
||||
}
|
||||
|
||||
if (model.includes("vidu")) {
|
||||
return resolution === "720P" ? 0.6 : 1.0;
|
||||
}
|
||||
|
||||
if (model.includes("pixverse")) {
|
||||
return resolution === "720P" ? 0.6 : 1.0;
|
||||
}
|
||||
const rule = ENTERPRISE_VIDEO_PRICING_RULES.find((candidate) =>
|
||||
enterpriseVideoPricingRuleMatches(candidate, input, model),
|
||||
);
|
||||
if (rule) return rule.rates[resolution] ?? rule.rates[ENTERPRISE_VIDEO_DEFAULT_RESOLUTION];
|
||||
|
||||
const error = new Error(`Unsupported enterprise video model: ${input.model || input.requestedModel}`);
|
||||
error.status = 403;
|
||||
@@ -103,6 +142,22 @@ function getEnterpriseVideoCreditRate(input) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
function getEnterpriseVideoPricingConfig() {
|
||||
return {
|
||||
currency: "CNY",
|
||||
creditsPerCny: CREDITS_PER_CNY,
|
||||
billingUnit: "per_second",
|
||||
defaultResolution: ENTERPRISE_VIDEO_DEFAULT_RESOLUTION,
|
||||
resolutions: [...ENTERPRISE_VIDEO_RESOLUTIONS],
|
||||
rules: ENTERPRISE_VIDEO_PRICING_RULES.map((rule) => ({
|
||||
id: rule.id,
|
||||
modelIncludes: [...rule.modelIncludes],
|
||||
when: rule.when ? { ...rule.when } : undefined,
|
||||
rates: { ...rule.rates },
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function calculateEnterpriseVideoCredits(input) {
|
||||
const duration = normalizeEnterpriseVideoDuration(input.durationSeconds || input.duration);
|
||||
return Number((getEnterpriseVideoCreditRate(input) * duration * CREDITS_PER_CNY).toFixed(2));
|
||||
@@ -233,6 +288,7 @@ module.exports = {
|
||||
assertEnterpriseVideoModelAllowed,
|
||||
calculateEnterpriseVideoCost,
|
||||
calculateEnterpriseVideoCredits,
|
||||
getEnterpriseVideoPricingConfig,
|
||||
getEnterpriseVideoCreditRate,
|
||||
isEnterpriseVideoBillingUser,
|
||||
isEnterpriseVideoModelAllowed,
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
const { keyManager, listModelPrices, pool } = require("./context");
|
||||
const { getEnterpriseVideoPricingConfig } = require("../enterpriseVideoBilling");
|
||||
|
||||
function registerPriceRoutes(router) {
|
||||
// ── Public ───────────────────────────────────────────────────────────
|
||||
|
||||
router.get("/prices", async (_req, res) => {
|
||||
const prices = await listModelPrices({ enabledOnly: true });
|
||||
res.json(prices);
|
||||
res.json({
|
||||
prices,
|
||||
modelPrices: prices,
|
||||
enterpriseVideoPricing: getEnterpriseVideoPricingConfig(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user