若干更新迭代

This commit is contained in:
andy committed 2026-10-02 19:56:32 +08:00
1 parent deb9cc5bcf
commit 4ac786aafb
64 files changed
+3251 -291

No files matched your search

+73 -5
View File
@@ -101,6 +101,15 @@ export async function quoteGenerationCharge(input: BillingGenerationInput): Prom
};
}
if (input.provider === "minimax" && input.reqKey === MINIMAX_H3_MODEL) {
const inputImageCount = Math.ceil(Number(parameters.referenceImageCount) || 0);
const inputImagePriceFen = Math.max(0, inputImageCount - 5) * 20;
const amountFen = Math.ceil((quote.standardUnitPriceFen * quantity + inputImagePriceFen) * quote.markupMultiplier);
return {
...quote, amountFen, reservedAmountFen: amountFen, settlementStatus: "pending",
parameters: { ...quote.parameters, inputImageCount, inputImagePriceFen }
};
}
if (input.provider === "bailian" && input.reqKey === "wan3.0-video") {
return { ...quote, reservedAmountFen: quote.amountFen, settlementStatus: "pending" };
}
if (input.provider !== "seedance" || !isSeedanceBillingModel(input.reqKey)) {
@@ -135,7 +144,7 @@ export async function chargeGenerationJob(job: GenerationJob): Promise<Generatio
...billing,
quotaExempt: true,
status: "not_charged",
settlementStatus: job.provider === "seedance" || job.provider === "minimax" ? "pending" : billing.settlementStatus
settlementStatus: job.provider === "seedance" || job.provider === "minimax" || job.provider === "bailian" && job.reqKey === "wan3.0-video" ? "pending" : billing.settlementStatus
}
});
}
@@ -278,7 +287,7 @@ export async function settleSeedanceGenerationCharge(
export async function settleMinimaxGenerationCharge(
jobOrId: GenerationJob | string,
usage?: { outputSeconds: number; inputSeconds: number; inputImageCount: number }
usage?: { outputSeconds: number; inputSeconds: number; inputImageCount?: number }
): Promise<GenerationJob | null> {
const job = typeof jobOrId === "string" ? await getGenerationJob(jobOrId) : jobOrId;
if (!job) return null;
@@ -293,9 +302,12 @@ export async function settleMinimaxGenerationCharge(
}
const resolution = billing.parameters?.resolution;
const inputImageCount = Number.isInteger(usage.inputImageCount) && (usage.inputImageCount ?? -1) >= 0
? usage.inputImageCount!
: minimaxQuotedInputImageCount(billing.parameters, job.requestPayload);
const actualAmountFen = calculateMinimaxH3ActualAmountFen({
resolution, outputSeconds: usage.outputSeconds, inputVideoSeconds: usage.inputSeconds,
inputImageCount: usage.inputImageCount, markupMultiplier: billing.markupMultiplier
inputImageCount, markupMultiplier: billing.markupMultiplier
});
const deltaFen = actualAmountFen - billing.amountFen;
let settlementLedgerEntryId: string | undefined;
@@ -309,7 +321,7 @@ export async function settleMinimaxGenerationCharge(
metadata: {
operation: "minimax_h3_actual_settlement", reservedAmountFen: billing.reservedAmountFen ?? billing.amountFen,
actualAmountFen, outputSeconds: usage.outputSeconds, inputVideoSeconds: usage.inputSeconds,
inputImageCount: usage.inputImageCount, resolution: String(resolution || "768P"), model: job.reqKey
inputImageCount, resolution: String(resolution || "768P"), model: job.reqKey
}
});
settlementLedgerEntryId = settlement.entry.id;
@@ -320,13 +332,58 @@ export async function settleMinimaxGenerationCharge(
...billing, amountFen: actualAmountFen, settlementStatus: "settled", settlementLedgerEntryId, settledAt,
providerUsage: {
model: job.reqKey, resolution: String(resolution || "768P"), outputSeconds: usage.outputSeconds,
inputVideoSeconds: usage.inputSeconds, inputImageCount: usage.inputImageCount,
inputVideoSeconds: usage.inputSeconds, inputImageCount,
videoPriceFenPerSecond: minimaxH3VideoPriceFenPerSecond(resolution)
}
}
});
}
export async function settleBailianWan3GenerationCharge(
jobOrId: GenerationJob | string,
outputSeconds?: number
): Promise<GenerationJob | null> {
const job = typeof jobOrId === "string" ? await getGenerationJob(jobOrId) : jobOrId;
if (!job) return null;
const billing = job.billing;
const quotaExempt = isQuotaExemptBilling(job);
const organizationId = job.usageContext?.organizationId;
const chargeReady = billing?.status === "charged" || quotaExempt && billing?.status === "not_charged";
if (job.provider !== "bailian" || job.reqKey !== "wan3.0-video" || !billing || !chargeReady || !quotaExempt && !organizationId) return job;
if (billing.settlementStatus === "settled" || billing.settlementStatus === "estimated") return job;
if (!Number.isFinite(outputSeconds) || !outputSeconds || outputSeconds <= 0) {
return updateGenerationJob(job.id, { billing: { ...billing, settlementStatus: "estimated", settlementReason: "provider_usage_unavailable", settledAt: new Date().toISOString() } });
}
const actualAmountFen = Math.ceil(outputSeconds * billing.standardUnitPriceFen * billing.markupMultiplier);
const deltaFen = actualAmountFen - billing.amountFen;
let settlementLedgerEntryId: string | undefined;
let settledAt = new Date().toISOString();
if (deltaFen !== 0 && !quotaExempt && organizationId) {
const settlement = await postWalletEntry({
organizationId, accountId: job.usageContext?.accountId, jobId: job.id,
kind: deltaFen > 0 ? "charge" : "refund", deltaFen: -deltaFen,
idempotencyKey: `job-settlement:${job.id}`,
description: deltaFen > 0 ? `${capabilityLabel(job.capability)}实际用量补扣` : `${capabilityLabel(job.capability)}实际用量差额退回`,
metadata: {
operation: "bailian_wan30_actual_settlement", reservedAmountFen: billing.reservedAmountFen ?? billing.amountFen,
actualAmountFen, outputSeconds, resolution: String(billing.parameters?.resolution || "720p"), model: job.reqKey
}
});
settlementLedgerEntryId = settlement.entry.id;
settledAt = settlement.entry.createdAt;
}
return updateGenerationJob(job.id, {
billing: {
...billing, amountFen: actualAmountFen, settlementStatus: "settled", settlementLedgerEntryId, settledAt,
providerUsage: {
model: job.reqKey, resolution: String(billing.parameters?.resolution || "720p"), outputSeconds,
videoPriceFenPerSecond: billing.standardUnitPriceFen
}
}
});
}
function isSeedanceBillingModel(model: string) {
return model === SEEDANCE_20_MODEL || model === SEEDANCE_25_MODEL;
}
@@ -532,6 +589,17 @@ export function normalizeBillingParameters(requestPayload: Record<string, unknow
return parameters;
}
function minimaxQuotedInputImageCount(
parameters: BillingParameterSnapshot | undefined,
requestPayload: Record<string, unknown>
): number {
for (const value of [parameters?.inputImageCount, parameters?.referenceImageCount, normalizeBillingParameters(requestPayload).referenceImageCount]) {
const count = Number(value);
if (value !== undefined && Number.isInteger(count) && count >= 0) return count;
}
return 0;
}
export function effectiveBillingRuleConditions(rule: BillingPriceRule): BillingRuleConditions {
const legacyConditions = parseLegacyVariantKey(rule.variantKey);
return { ...legacyConditions, ...(rule.conditions || {}) };