6d99d762da
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>
88 lines
3.4 KiB
JavaScript
88 lines
3.4 KiB
JavaScript
// DeepSeek V4 Flash 公开价($/1M tokens,cache-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));
|
||
}
|