Files
memind/billing.mjs
T
John 6d99d762da Add image-designer skill, align billing to DeepSeek ×1, and enrich Plaza demo.
Introduce AI image generation with chat shortcut and agent API, improve MindSpace chat-to-page save resolution, and seed Plaza covers with production deploy scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 16:29:19 -07:00

88 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// DeepSeek V4 Flash 公开价($/1M tokenscache-miss 输入);见 api-docs.deepseek.com
export const DEEPSEEK_FLASH_INPUT_USD_PER_1M = 0.14;
export const DEEPSEEK_FLASH_OUTPUT_USD_PER_1M = 0.28;
export const DEEPSEEK_BILLING_MARKUP = 1;
function deepSeekFlashCentsPer1k(usdPer1M, usdCnyRate) {
return (usdPer1M / 1000) * usdCnyRate * 100 * DEEPSEEK_BILLING_MARKUP;
}
export function loadBillingConfig() {
// 默认按人民币分(CNY cents)计费;仅当 H5_USE_BACKEND_COST=1 时才用上游 USD 成本换算。
const useBackendCost = process.env.H5_USE_BACKEND_COST === '1';
const usdCnyRate = Number(process.env.H5_USD_CNY_RATE ?? 7.2);
const defaultInputCentsPer1k = deepSeekFlashCentsPer1k(
DEEPSEEK_FLASH_INPUT_USD_PER_1M,
usdCnyRate,
);
const defaultOutputCentsPer1k = deepSeekFlashCentsPer1k(
DEEPSEEK_FLASH_OUTPUT_USD_PER_1M,
usdCnyRate,
);
return {
useBackendCost,
usdCnyRate,
inputCentsPer1k: Number(process.env.H5_BILL_INPUT_CENTS_PER_1K ?? defaultInputCentsPer1k),
outputCentsPer1k: Number(process.env.H5_BILL_OUTPUT_CENTS_PER_1K ?? defaultOutputCentsPer1k),
minBillCents: Number(process.env.H5_MIN_BILL_CENTS ?? 1),
};
}
export function normalizeTokenState(raw) {
if (!raw || typeof raw !== 'object') {
return {
inputTokens: 0,
outputTokens: 0,
totalTokens: 0,
accumulatedInputTokens: 0,
accumulatedOutputTokens: 0,
accumulatedTotalTokens: 0,
accumulatedCost: null,
};
}
return {
inputTokens: Number(raw.inputTokens ?? raw.input_tokens ?? 0),
outputTokens: Number(raw.outputTokens ?? raw.output_tokens ?? 0),
totalTokens: Number(raw.totalTokens ?? raw.total_tokens ?? 0),
accumulatedInputTokens: Number(
raw.accumulatedInputTokens ?? raw.accumulated_input_tokens ?? raw.inputTokens ?? 0,
),
accumulatedOutputTokens: Number(
raw.accumulatedOutputTokens ?? raw.accumulated_output_tokens ?? raw.outputTokens ?? 0,
),
accumulatedTotalTokens: Number(
raw.accumulatedTotalTokens ?? raw.accumulated_total_tokens ?? raw.totalTokens ?? 0,
),
accumulatedCost:
raw.accumulatedCost ?? raw.accumulated_cost ?? null,
};
}
export function computeDeltaCostCents(previous, current, config = loadBillingConfig()) {
const prevCost =
previous?.lastAccumulatedCost == null ? null : Number(previous.lastAccumulatedCost);
const currCost =
current.accumulatedCost == null ? null : Number(current.accumulatedCost);
if (config.useBackendCost && prevCost != null && currCost != null && currCost >= prevCost) {
const deltaUsd = currCost - prevCost;
return Math.max(config.minBillCents, Math.ceil(deltaUsd * config.usdCnyRate * 100));
}
if (config.useBackendCost && prevCost == null && currCost != null && currCost > 0) {
return Math.max(config.minBillCents, Math.ceil(currCost * config.usdCnyRate * 100));
}
// 默认路径:按 Token 增量 × 人民币单价(分/1k tokens)扣费。
const prevIn = Number(previous?.lastInputTokens ?? 0);
const prevOut = Number(previous?.lastOutputTokens ?? 0);
const deltaIn = Math.max(0, current.accumulatedInputTokens - prevIn);
const deltaOut = Math.max(0, current.accumulatedOutputTokens - prevOut);
if (deltaIn === 0 && deltaOut === 0) return 0;
const raw =
(deltaIn / 1000) * config.inputCentsPer1k + (deltaOut / 1000) * config.outputCentsPer1k;
if (raw <= 0) return 0;
return Math.max(config.minBillCents, Math.ceil(raw));
}