@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
106 lines (105 loc) • 5.6 kB
TypeScript
/**
* NeuroCoin ledger and per-window consumption tracking for borrowed traffic.
*
* Two jobs, deliberately in one module because they settle from the same event:
*
* 1. **Coins.** A grant with a coin entitlement spends against a balance. The
* balance itself lives on the grant (one source of truth); this module holds
* the entries and drives the deduction.
* 2. **Window buckets.** How much of an account's 5h/7d window a grant has taken,
* keyed by that window's reset timestamp so the counter starts fresh when the
* window does. This is what makes a slice ceiling mean "a fifth of *this*
* window" rather than "a fifth, once, forever".
*
* **Why hold-then-settle.** Real usage is only known when the response finishes
* — for a stream, at `message_delta`. A balance checked at admission and
* deducted at completion lets N concurrent streams each pass the same check and
* overspend by N-1 requests. So admission opens a *hold* for an estimate, the
* available balance is `balance - Σ open holds`, and settlement replaces the
* hold with the real figure. A crash loses in-flight holds, which is the safe
* direction: the balance is only ever reduced by traffic that actually happened.
*
* @module proxy/shareLedger
*/
import type { ProxyShareGrant, ProxyShareGrantUsageSummary, ProxyShareHold, ProxySharePoolUsage, ProxyShareSettlement, ProxyShareUsage, ProxyShareWindowObservation } from "../types/index.js";
/** Normalized tokens per coin. */
export declare const TOKENS_PER_COIN = 1000;
export declare function initShareLedger(ledgerFilePath: string): void;
export declare function modelCoinWeight(model: string | undefined): number;
/** Convert real usage into coins. Pure — the pricing table is the whole story. */
export declare function usageToCoins(usage: ProxyShareUsage, model: string | undefined): number;
/**
* What to hold at admission, before anything is known about the response.
*
* Intentionally rough: the hold exists to stop concurrent streams from each
* spending the last coin, not to predict the bill. Settlement replaces it with
* the real figure moments later.
*/
export declare function estimateHoldCoins(model: string | undefined, maxTokens: number | undefined): number;
/** Coins currently held against a grant by in-flight requests. */
export declare function heldCoins(grantId: string): number;
/**
* Balance a new request may draw on: the stored balance minus what in-flight
* requests have already claimed.
*/
export declare function availableCoins(grant: ProxyShareGrant): number;
export declare function openShareHold(grantId: string, coins: number, now?: number): ProxyShareHold;
/** Drop a hold without spending it — the request never reached the upstream. */
export declare function releaseShareHold(holdId: string | undefined): void;
/**
* Record how much of an account's windows a borrowed request consumed.
*
* Never throws: a bookkeeping failure must not turn a response the borrower has
* already received into an error.
*/
export declare function recordShareWindowDelta(observation: ProxyShareWindowObservation): Promise<void>;
/**
* Charge a finished borrowed request against its grant and close the hold.
*
* Never throws, for the same reason as above.
*/
export declare function settleShareUsage(settlement: ProxyShareSettlement): Promise<number>;
/**
* What a grant has taken from one account's current windows.
*
* Buckets whose reset timestamp no longer matches the account's are reported as
* zero: they describe a window that has since rolled over.
*/
export declare function readShareWindowUsage(grantId: string, accountKey: string, currentSessionResetAt: number | null, currentWeeklyResetAt: number | null): Promise<{
sessionFraction: number;
weeklyFraction: number;
}>;
/**
* What a grant has taken from the pool as a whole, normalised to one window.
*
* Summing the per-account fractions and dividing by the account count is what
* makes a ceiling mean the same thing on a one-account pool and a ten-account
* one. Without the division, `--max-slice 20` would silently grant 20% of every
* credential — 200% of a single window's worth across ten accounts.
*
* Buckets whose reset timestamp no longer matches contribute zero: they
* describe a window that has since rolled over.
*/
export declare function readSharePoolWindowUsage(grantId: string, accounts: ReadonlyArray<{
accountKey: string;
sessionResetAt: number | null;
weeklyResetAt: number | null;
}>): Promise<ProxySharePoolUsage>;
/** Per-grant rollup across accounts, for `share status`. */
export declare function summarizeGrantUsage(grantId: string): Promise<ProxyShareGrantUsageSummary>;
/**
* Apply a standing allowance for every period that has elapsed.
*
* Called opportunistically at admission — there is no timer, so a node that is
* off across one or more refill boundaries pays them on its next borrowed
* request. Paying only the newest period would quietly turn "100 a week" into
* "100 whenever you next happen to ask", which is not what the operator wrote.
*
* `lastAt` advances by whole periods rather than to `now`, so the schedule stays
* anchored to when the grant was issued instead of drifting later with every
* catch-up. It advances past the periods the cap refused to pay as well — see
* {@link MAX_REFILL_CATCHUP_PERIODS}.
*/
export declare function applyRefillIfDue(grant: ProxyShareGrant, now?: number): Promise<ProxyShareGrant>;
/** Drop all ledger state. Test isolation only. */
export declare function resetShareLedgerForTests(): void;