Files
memind/billing-token-state.mjs
T
john 32fb2cdeaf feat: chat uploads, vision turn isolation, and MindSpace agent improvements
Add chat file/image upload UX, attachment proxying, vision thumbnails, and per-turn image scoping so agents only use the current upload. Extend MindSpace asset context, billing token state, OA/scenario verify scripts, and related runtime config.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-11 00:23:01 +08:00

80 lines
2.8 KiB
JavaScript

import { normalizeTokenState } from './billing.mjs';
export function loadCostEstimateConfig(env = process.env) {
const useBackendCost = env.H5_USE_BACKEND_COST === '1';
const enabled = useBackendCost && env.H5_COST_ESTIMATE_FROM_TOKENS !== '0';
const inputUsdPer1M = Number(env.H5_COST_ESTIMATE_INPUT_USD_PER_1M ?? 0.27);
const outputUsdPer1M = Number(env.H5_COST_ESTIMATE_OUTPUT_USD_PER_1M ?? 1.1);
return {
enabled,
inputUsdPer1M: Number.isFinite(inputUsdPer1M) && inputUsdPer1M >= 0 ? inputUsdPer1M : 0.27,
outputUsdPer1M: Number.isFinite(outputUsdPer1M) && outputUsdPer1M >= 0 ? outputUsdPer1M : 1.1,
};
}
export function pickSessionAccumulatedCost(session) {
if (!session || typeof session !== 'object') return null;
const raw = session.accumulatedCost ?? session.accumulated_cost ?? null;
if (raw == null) return null;
const value = Number(raw);
return Number.isFinite(value) && value >= 0 ? value : null;
}
export function estimateAccumulatedCostUsd(tokenStateRaw, estimateConfig = loadCostEstimateConfig()) {
if (!estimateConfig?.enabled) return null;
const state = normalizeTokenState(tokenStateRaw);
if (state.accumulatedInputTokens <= 0 && state.accumulatedOutputTokens <= 0) return null;
const inputUsd =
(state.accumulatedInputTokens * estimateConfig.inputUsdPer1M) / 1_000_000;
const outputUsd =
(state.accumulatedOutputTokens * estimateConfig.outputUsdPer1M) / 1_000_000;
const total = inputUsd + outputUsd;
return total > 0 ? total : null;
}
export function enrichTokenStateForBilling(
tokenStateRaw,
{ sessionCost = null } = {},
env = process.env,
) {
const state = normalizeTokenState(tokenStateRaw);
if (state.accumulatedCost != null && Number(state.accumulatedCost) >= 0) {
return state;
}
const sessionUsd = pickSessionAccumulatedCost(sessionCost);
if (sessionUsd != null) {
return { ...state, accumulatedCost: sessionUsd };
}
const estimatedUsd = estimateAccumulatedCostUsd(state, loadCostEstimateConfig(env));
if (estimatedUsd != null) {
return { ...state, accumulatedCost: estimatedUsd };
}
return state;
}
export async function resolveBillingTokenState(
tokenStateRaw,
{ sessionId = null, fetchSession = null } = {},
env = process.env,
) {
const state = normalizeTokenState(tokenStateRaw);
if (state.accumulatedCost != null && Number(state.accumulatedCost) >= 0) {
return state;
}
if (typeof fetchSession === 'function' && sessionId) {
try {
const session = await fetchSession(sessionId);
const enriched = enrichTokenStateForBilling(state, { sessionCost: session }, env);
if (enriched.accumulatedCost != null) return enriched;
} catch {
// Best-effort: fall through to token estimate.
}
}
return enrichTokenStateForBilling(state, {}, env);
}