@trendyvideo/billing 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/billing-coverage.test.js +32 -8
- package/dist/__tests__/billing-coverage.test.js.map +1 -1
- package/dist/__tests__/pricing-gemini-billing.test.d.ts +2 -0
- package/dist/__tests__/pricing-gemini-billing.test.d.ts.map +1 -0
- package/dist/__tests__/pricing-gemini-billing.test.js +296 -0
- package/dist/__tests__/pricing-gemini-billing.test.js.map +1 -0
- package/dist/__tests__/pricing-gemini-embedding-2.test.d.ts +2 -0
- package/dist/__tests__/pricing-gemini-embedding-2.test.d.ts.map +1 -0
- package/dist/__tests__/pricing-gemini-embedding-2.test.js +104 -0
- package/dist/__tests__/pricing-gemini-embedding-2.test.js.map +1 -0
- package/dist/__tests__/pricing.test.js +138 -0
- package/dist/__tests__/pricing.test.js.map +1 -1
- package/dist/__tests__/usage-tracker.test.js +201 -3
- package/dist/__tests__/usage-tracker.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/pricing.d.ts +151 -14
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +355 -28
- package/dist/pricing.js.map +1 -1
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/usage-tracker.d.ts.map +1 -1
- package/dist/usage-tracker.js +6 -1
- package/dist/usage-tracker.js.map +1 -1
- package/package.json +1 -1
package/dist/pricing.d.ts
CHANGED
|
@@ -6,26 +6,107 @@ export declare const MODEL_PRICING: Record<string, ModelPricing>;
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const DEFAULT_PRICING: ModelPricing;
|
|
8
8
|
/**
|
|
9
|
-
* Look up pricing for a model
|
|
10
|
-
*
|
|
9
|
+
* Look up the raw pricing row for a model, ignoring any scheduled change.
|
|
10
|
+
* Tries exact match first, then prefix match, then strips a trailing date
|
|
11
|
+
* suffix (like `-20250514`). Returns `undefined` when nothing matches.
|
|
11
12
|
*/
|
|
12
|
-
export declare function
|
|
13
|
+
export declare function findPricingRow(model: string): ModelPricing | undefined;
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Look up pricing for a model as of `at` (default: now). Resolution order is
|
|
16
|
+
* exact match, prefix match, date-suffix strip, then DEFAULT_PRICING.
|
|
17
|
+
* Rows with a `scheduledChange` return the scheduled rates once `at` reaches
|
|
18
|
+
* the effective date.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getPricing(model: string, at?: Date): ModelPricing;
|
|
21
|
+
/**
|
|
22
|
+
* Does this provider report cached tokens as a SUBSET of `inputTokens`?
|
|
23
|
+
*
|
|
24
|
+
* Gemini's `usageMetadata.promptTokenCount` already includes
|
|
25
|
+
* `cachedContentTokenCount`, and the provider forwards them as
|
|
26
|
+
* `inputTokens` / `cacheReadTokens` unchanged. Anthropic reports
|
|
27
|
+
* `input_tokens` and `cache_read_input_tokens` disjointly. Billing the
|
|
28
|
+
* Gemini pair with the Anthropic formula charges every cached token twice
|
|
29
|
+
* (once at the input rate inside `inputTokens`, again at the cache-read
|
|
30
|
+
* rate). See docs/architecture/gemini-cache-with-tools.md §5.
|
|
31
|
+
*/
|
|
32
|
+
export declare function promptTokensIncludeCacheReads(model: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Margin applied to every charge. 1.5x means the customer pays 50% more than
|
|
35
|
+
* the provider charges us (~33% margin).
|
|
36
|
+
*
|
|
37
|
+
* WHERE it is applied (Sully, 2026-09-04: "token to credit should be exact,
|
|
38
|
+
* markup after conversion"): to the CREDIT figure, in `calculateBilledCredits`,
|
|
39
|
+
* after the exact microdollar-to-credit conversion. It is NEVER applied to
|
|
40
|
+
* `cost_microdollars`: that column stores the true provider cost so it
|
|
41
|
+
* reconciles against Google's and Anthropic's invoices. `recordUsage` is the
|
|
42
|
+
* only writer and runs the two steps in that order.
|
|
43
|
+
*
|
|
44
|
+
* History: 1.0.0 to 1.2.1 multiplied inside `calculateCostMicrodollars`, so
|
|
45
|
+
* the margin was baked into the stored cost. #118 made that function return
|
|
46
|
+
* raw cost and nothing picked the markup back up (zero margin, never
|
|
47
|
+
* published). PR #255 first restored it on the microdollar side; this
|
|
48
|
+
* revision moves it to the credit side per the decision above.
|
|
49
|
+
*
|
|
50
|
+
* Callers that pass a `costMicrodollarsOverride` (voice, Seedance, image gen,
|
|
51
|
+
* music) pass the RAW provider cost. It is stored as-is and marked up on the
|
|
52
|
+
* credit side like everything else.
|
|
17
53
|
*/
|
|
18
54
|
export declare const COST_MARKUP = 1.5;
|
|
19
55
|
/**
|
|
20
|
-
*
|
|
56
|
+
* True provider cost in microdollars (1 microdollar = $0.000001), NO markup.
|
|
21
57
|
*
|
|
22
58
|
* Since pricing is per-million tokens:
|
|
23
59
|
* cost_usd = (tokens / 1_000_000) * rate_per_million
|
|
24
60
|
* cost_microdollars = cost_usd * 1_000_000 = tokens * rate_per_million
|
|
25
61
|
*
|
|
26
|
-
*
|
|
62
|
+
* This is what Google or Anthropic invoices us and what `recordUsage` stores
|
|
63
|
+
* in `usage_events.cost_microdollars`. It is NOT what the customer is
|
|
64
|
+
* charged: `calculateBilledCredits` derives `credits_consumed` from it.
|
|
65
|
+
*
|
|
66
|
+
* Rounding: the exact figure is rounded to the nearest whole microdollar
|
|
67
|
+
* because the column is `bigint`. Maximum error 0.5 µ$ ($0.0000005), below
|
|
68
|
+
* any invoice's precision. An override gets the same treatment (callers pass
|
|
69
|
+
* integers, so in practice it passes through untouched). The full rounding
|
|
70
|
+
* rule for a charge is documented once, on `calculateBilledCredits`.
|
|
71
|
+
*/
|
|
72
|
+
export declare function calculateCostMicrodollars(model: string, inputTokens: number, outputTokens: number, cacheReadTokens?: number, cacheCreationTokens?: number, costMicrodollarsOverride?: number, at?: Date): number;
|
|
73
|
+
export type GeminiEmbedding2Modality = 'text' | 'image' | 'audio' | 'video';
|
|
74
|
+
/** USD per 1M input tokens, by modality. */
|
|
75
|
+
export declare const GEMINI_EMBEDDING_2_INPUT_PRICING: Record<GeminiEmbedding2Modality, number>;
|
|
76
|
+
/** Per-unit equivalents (raw microdollars) for when the API reports no token count. */
|
|
77
|
+
export declare const GEMINI_EMBEDDING_2_UNIT_RATES: {
|
|
78
|
+
readonly microdollarsPerImage: 120;
|
|
79
|
+
readonly microdollarsPerAudioSecond: 160;
|
|
80
|
+
readonly microdollarsPerVideoFrame: 790;
|
|
81
|
+
readonly videoMaxFrames: 32;
|
|
82
|
+
/** Rough text fallback: ~4 chars per token. */
|
|
83
|
+
readonly charsPerTextToken: 4;
|
|
84
|
+
};
|
|
85
|
+
/** Frames Google samples from a video of `durationSec` (1 fps, capped at 32). */
|
|
86
|
+
export declare function geminiEmbedding2VideoFrames(durationSec: number): number;
|
|
87
|
+
export interface GeminiEmbedding2CostInput {
|
|
88
|
+
modality: GeminiEmbedding2Modality;
|
|
89
|
+
/** `usageMetadata.promptTokenCount` from the embedContent response, when present. */
|
|
90
|
+
promptTokens?: number | undefined;
|
|
91
|
+
/** Fallback inputs, used only when `promptTokens` is absent. */
|
|
92
|
+
textChars?: number | undefined;
|
|
93
|
+
imageCount?: number | undefined;
|
|
94
|
+
durationSec?: number | undefined;
|
|
95
|
+
}
|
|
96
|
+
export interface GeminiEmbedding2Cost {
|
|
97
|
+
/** RAW microdollars — pass as `costMicrodollarsOverride`; COST_MARKUP is applied by recordUsage. */
|
|
98
|
+
costMicrodollars: number;
|
|
99
|
+
/** Token count to store as `inputTokens` (reported, or estimated for text). 0 when unknown. */
|
|
100
|
+
inputTokens: number;
|
|
101
|
+
basis: 'reported_tokens' | 'estimated_units';
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Raw cost of one `gemini-embedding-2:embedContent` call. Prefers the token
|
|
105
|
+
* count Google reports; otherwise falls back to the published per-unit rate
|
|
106
|
+
* for the modality. Always returns a cost for a non-empty input so a call can
|
|
107
|
+
* never be recorded as free by accident.
|
|
27
108
|
*/
|
|
28
|
-
export declare function
|
|
109
|
+
export declare function calculateGeminiEmbedding2CostMicrodollars(input: GeminiEmbedding2CostInput): GeminiEmbedding2Cost;
|
|
29
110
|
export interface MediaRate {
|
|
30
111
|
/** Pricing shape: per-unit (e.g. one image) or per-second (e.g. a video clip). */
|
|
31
112
|
unit: 'per_clip' | 'per_second';
|
|
@@ -38,8 +119,9 @@ export declare const MEDIA_RATE_PRICING: Record<string, MediaRate>;
|
|
|
38
119
|
/**
|
|
39
120
|
* Estimate the RAW microdollar cost of a media-generation call before
|
|
40
121
|
* COST_MARKUP is applied. Callers pass the result as
|
|
41
|
-
* `costMicrodollarsOverride` into `recordUsage
|
|
42
|
-
*
|
|
122
|
+
* `costMicrodollarsOverride` into `recordUsage`, which stores it as the
|
|
123
|
+
* row's `cost_microdollars` and applies the markup on the credit side via
|
|
124
|
+
* `calculateBilledCredits`.
|
|
43
125
|
*
|
|
44
126
|
* - For `per_clip` rates, pass `units` (default 1).
|
|
45
127
|
* - For `per_second` rates, pass `durationSec`.
|
|
@@ -52,15 +134,70 @@ export declare function estimateMediaCostMicrodollars(model: string, opts?: {
|
|
|
52
134
|
units?: number;
|
|
53
135
|
}): number;
|
|
54
136
|
/**
|
|
55
|
-
* Same estimate
|
|
56
|
-
*
|
|
57
|
-
* the
|
|
137
|
+
* Same estimate with COST_MARKUP applied, still in microdollars: what the
|
|
138
|
+
* user is billed, expressed as credits × 10,000. Use it for cost-preview
|
|
139
|
+
* UIs. `microdollarsToCredits` of this value equals the `credits_consumed`
|
|
140
|
+
* that `recordUsage` writes for the raw estimate, to the 0.01 credit, for
|
|
141
|
+
* every rate in the table (tests in usage-tracker.test.ts). It is NOT what
|
|
142
|
+
* `cost_microdollars` stores; that column holds the raw estimate.
|
|
58
143
|
*/
|
|
59
144
|
export declare function estimateBilledMediaCostMicrodollars(model: string, opts?: {
|
|
60
145
|
durationSec?: number;
|
|
61
146
|
units?: number;
|
|
62
147
|
}): number;
|
|
63
148
|
export declare const MICRODOLLARS_PER_CREDIT = 10000;
|
|
149
|
+
/**
|
|
150
|
+
* Trendy Voice (Gemini Live) per-minute billing rate.
|
|
151
|
+
*
|
|
152
|
+
* 12 credits = $0.12 = 120,000 microdollars per session-minute.
|
|
153
|
+
*
|
|
154
|
+
* Minutes are rounded UP — partial minutes are billed as a full minute.
|
|
155
|
+
* Fairness goes to Trendy on the round-up; very short sessions (<10s)
|
|
156
|
+
* are not billed at all (see the meter's startup guard).
|
|
157
|
+
*/
|
|
158
|
+
export declare const TRENDY_VOICE_CREDITS_PER_MINUTE = 12;
|
|
159
|
+
export declare const TRENDY_VOICE_MICRODOLLARS_PER_MINUTE: number;
|
|
160
|
+
export declare const TRENDY_VOICE_SKU: "trendy_voice_minute";
|
|
161
|
+
/**
|
|
162
|
+
* Precision of `usage_events.credits_consumed` (`numeric(12,2)`): charges are
|
|
163
|
+
* kept to 0.01 credit = 100 µ$ = $0.0001.
|
|
164
|
+
*/
|
|
165
|
+
export declare const CREDIT_DECIMALS = 2;
|
|
166
|
+
/** Exact conversion. No rounding, no markup. */
|
|
64
167
|
export declare function microdollarsToCredits(microdollars: number): number;
|
|
65
168
|
export declare function creditsToMicrodollars(credits: number): number;
|
|
169
|
+
/**
|
|
170
|
+
* Customer-billed credits for a true-cost `costMicrodollars` figure. This is
|
|
171
|
+
* the ONLY place COST_MARKUP is applied and the ONLY place a charge is
|
|
172
|
+
* rounded. `recordUsage` writes the result to `credits_consumed`.
|
|
173
|
+
*
|
|
174
|
+
* billed credits = round2( (cost_microdollars / 10,000) × COST_MARKUP )
|
|
175
|
+
*
|
|
176
|
+
* 1. exact conversion cost_microdollars / MICRODOLLARS_PER_CREDIT
|
|
177
|
+
* 2. margin × COST_MARKUP
|
|
178
|
+
* 3. one rounding half up, to 0.01 credit (CREDIT_DECIMALS), so the
|
|
179
|
+
* value passed to the RPC is the value stored and
|
|
180
|
+
* SQL reproduces it exactly:
|
|
181
|
+
* ROUND(cost_microdollars * 1.5 / 10000, 2)
|
|
182
|
+
*
|
|
183
|
+
* THE ROUNDING RULE, in full. Nothing else in this package rounds a charge.
|
|
184
|
+
* - `cost_microdollars`: nearest whole microdollar (bigint column), in
|
|
185
|
+
* `calculateCostMicrodollars`. A storage step, not a pricing step.
|
|
186
|
+
* - `credits_consumed`: here, once, half up, to 0.01 credit, computed from
|
|
187
|
+
* the stored `cost_microdollars` so every row is self-consistent.
|
|
188
|
+
* - No rounding up to a whole credit (1.2.1 did that with Math.ceil), no
|
|
189
|
+
* rounding before the markup, no rounding in `microdollarsToCredits`.
|
|
190
|
+
* - A charge below 0.005 credit (true cost of 33 µ$ or less) rounds to
|
|
191
|
+
* 0.00. The `record_usage_atomic` RPC then falls back to
|
|
192
|
+
* ROUND(cost_microdollars / 10000, 2), which is also 0.00 for every such
|
|
193
|
+
* value, so the two never disagree (test in pricing.test.ts).
|
|
194
|
+
*
|
|
195
|
+
* Implementation note: steps 1 and 2 commute, and the arithmetic is done in
|
|
196
|
+
* whole microdollars (`cost × 1.5` is exact for any integer) before the one
|
|
197
|
+
* division, because `1500 / 10000 * 1.5` is 0.224999… in floating point and
|
|
198
|
+
* would round to 0.22 instead of 0.23. `pricing.test.ts` checks the result
|
|
199
|
+
* against an integer reference implementation for every value up to
|
|
200
|
+
* 5,000,000 µ$ and a set of larger ones.
|
|
201
|
+
*/
|
|
202
|
+
export declare function calculateBilledCredits(costMicrodollars: number): number;
|
|
66
203
|
//# sourceMappingURL=pricing.d.ts.map
|
package/dist/pricing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|