@meyicloud/meyi-insight-server 1.1.3 → 1.1.5
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/controllers/tenant.controller.js +1 -1
- package/dist/controllers/tenant.controller.js.map +1 -1
- package/dist/features/ai-pricing/ai-pricing.controller.d.ts +9 -0
- package/dist/features/ai-pricing/ai-pricing.controller.d.ts.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.controller.js +73 -0
- package/dist/features/ai-pricing/ai-pricing.controller.js.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.model.d.ts +44 -0
- package/dist/features/ai-pricing/ai-pricing.model.d.ts.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.model.js +229 -0
- package/dist/features/ai-pricing/ai-pricing.model.js.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.routes.d.ts +10 -0
- package/dist/features/ai-pricing/ai-pricing.routes.d.ts.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.routes.js +18 -0
- package/dist/features/ai-pricing/ai-pricing.routes.js.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.service.d.ts +23 -0
- package/dist/features/ai-pricing/ai-pricing.service.d.ts.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.service.js +87 -0
- package/dist/features/ai-pricing/ai-pricing.service.js.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.sync.d.ts +48 -0
- package/dist/features/ai-pricing/ai-pricing.sync.d.ts.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.sync.js +119 -0
- package/dist/features/ai-pricing/ai-pricing.sync.js.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.types.d.ts +66 -0
- package/dist/features/ai-pricing/ai-pricing.types.d.ts.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.types.js +16 -0
- package/dist/features/ai-pricing/ai-pricing.types.js.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.worker.d.ts +47 -0
- package/dist/features/ai-pricing/ai-pricing.worker.d.ts.map +1 -0
- package/dist/features/ai-pricing/ai-pricing.worker.js +121 -0
- package/dist/features/ai-pricing/ai-pricing.worker.js.map +1 -0
- package/dist/features/ai-pricing/index.d.ts +8 -0
- package/dist/features/ai-pricing/index.d.ts.map +1 -0
- package/dist/features/ai-pricing/index.js +8 -0
- package/dist/features/ai-pricing/index.js.map +1 -0
- package/dist/features/ai-pricing/litellm.mapper.d.ts +11 -0
- package/dist/features/ai-pricing/litellm.mapper.d.ts.map +1 -0
- package/dist/features/ai-pricing/litellm.mapper.js +104 -0
- package/dist/features/ai-pricing/litellm.mapper.js.map +1 -0
- package/dist/features/ai-pricing/pricing.lookup.d.ts +39 -0
- package/dist/features/ai-pricing/pricing.lookup.d.ts.map +1 -0
- package/dist/features/ai-pricing/pricing.lookup.js +88 -0
- package/dist/features/ai-pricing/pricing.lookup.js.map +1 -0
- package/dist/features/ai-pricing/pricing.plan.d.ts +33 -0
- package/dist/features/ai-pricing/pricing.plan.d.ts.map +1 -0
- package/dist/features/ai-pricing/pricing.plan.js +63 -0
- package/dist/features/ai-pricing/pricing.plan.js.map +1 -0
- package/dist/features/ai-pricing/pricing.reprice.d.ts +25 -0
- package/dist/features/ai-pricing/pricing.reprice.d.ts.map +1 -0
- package/dist/features/ai-pricing/pricing.reprice.js +65 -0
- package/dist/features/ai-pricing/pricing.reprice.js.map +1 -0
- package/dist/features/ai-usage/ai-usage.controller.d.ts +1 -0
- package/dist/features/ai-usage/ai-usage.controller.d.ts.map +1 -1
- package/dist/features/ai-usage/ai-usage.controller.js +9 -0
- package/dist/features/ai-usage/ai-usage.controller.js.map +1 -1
- package/dist/features/ai-usage/ai-usage.model.d.ts +2 -0
- package/dist/features/ai-usage/ai-usage.model.d.ts.map +1 -1
- package/dist/features/ai-usage/ai-usage.model.js +16 -0
- package/dist/features/ai-usage/ai-usage.model.js.map +1 -1
- package/dist/features/ai-usage/ai-usage.query.d.ts.map +1 -1
- package/dist/features/ai-usage/ai-usage.query.js +8 -0
- package/dist/features/ai-usage/ai-usage.query.js.map +1 -1
- package/dist/features/ai-usage/ai-usage.routes.d.ts.map +1 -1
- package/dist/features/ai-usage/ai-usage.routes.js +1 -0
- package/dist/features/ai-usage/ai-usage.routes.js.map +1 -1
- package/dist/features/ai-usage/ai-usage.service.d.ts +2 -1
- package/dist/features/ai-usage/ai-usage.service.d.ts.map +1 -1
- package/dist/features/ai-usage/ai-usage.service.js +11 -0
- package/dist/features/ai-usage/ai-usage.service.js.map +1 -1
- package/dist/features/ai-usage/ai-usage.types.d.ts +26 -0
- package/dist/features/ai-usage/ai-usage.types.d.ts.map +1 -1
- package/dist/features/ai-usage/ai-usage.types.js.map +1 -1
- package/dist/migrations/run.d.ts.map +1 -1
- package/dist/migrations/run.js +103 -15
- package/dist/migrations/run.js.map +1 -1
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +7 -2
- package/dist/routes/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +4 -0
- package/dist/server.js.map +1 -1
- package/dist/service.d.ts +5 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +15 -0
- package/dist/service.js.map +1 -1
- package/dist/services/tenant.service.js +2 -2
- package/dist/services/tenant.service.js.map +1 -1
- package/dist/tenancy.d.ts +1 -0
- package/dist/tenancy.d.ts.map +1 -1
- package/dist/tenancy.js +9 -3
- package/dist/tenancy.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mapLiteLLM } from "./litellm.mapper.js";
|
|
2
|
+
import { planPriceChanges } from "./pricing.plan.js";
|
|
3
|
+
const SOURCE = "litellm";
|
|
4
|
+
// Reviewed LiteLLM commit (2026-10-21). Move it deliberately — never point
|
|
5
|
+
// production at "main", so an upstream mistake cannot arrive unreviewed.
|
|
6
|
+
export const DEFAULT_LITELLM_REF = "159b5b2f7243bba757c08e4ddc45a2514e4cd037";
|
|
7
|
+
const URL_TEMPLATE = "https://raw.githubusercontent.com/BerriAI/litellm/{ref}/model_prices_and_context_window.json";
|
|
8
|
+
export function syncConfigFromEnv(env = process.env) {
|
|
9
|
+
const mode = String(env.AI_PRICING_SYNC_MODE || "apply").trim().toLowerCase();
|
|
10
|
+
const ref = String(env.AI_PRICING_LITELLM_REF || DEFAULT_LITELLM_REF).trim();
|
|
11
|
+
if (!/^[A-Za-z0-9._-]{1,64}$/.test(ref))
|
|
12
|
+
throw new Error("AI_PRICING_LITELLM_REF is not a valid git ref");
|
|
13
|
+
const threshold = Number(env.AI_PRICING_REVIEW_THRESHOLD ?? 0.25);
|
|
14
|
+
return {
|
|
15
|
+
mode: mode === "off" || mode === "dry-run" ? mode : "apply",
|
|
16
|
+
ref,
|
|
17
|
+
url: URL_TEMPLATE.replace("{ref}", ref),
|
|
18
|
+
timeoutMs: 30_000,
|
|
19
|
+
maxBytes: 20 * 1024 * 1024,
|
|
20
|
+
threshold: Number.isFinite(threshold) && threshold > 0 ? threshold : 0.25,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** GET the price file with gzip + ETag, a timeout and a size cap. */
|
|
24
|
+
export async function fetchPriceFile(config, etag, fetchImpl = fetch) {
|
|
25
|
+
const headers = { "Accept-Encoding": "gzip", Accept: "application/json" };
|
|
26
|
+
if (etag)
|
|
27
|
+
headers["If-None-Match"] = etag;
|
|
28
|
+
const res = await fetchImpl(config.url, { headers, signal: AbortSignal.timeout(config.timeoutMs) });
|
|
29
|
+
if (res.status === 304)
|
|
30
|
+
return { notModified: true };
|
|
31
|
+
if (!res.ok || !res.body)
|
|
32
|
+
throw new Error(`price file request failed with HTTP ${res.status}`);
|
|
33
|
+
// fetch() decompresses gzip; count the decompressed bytes and stop past the cap.
|
|
34
|
+
const reader = res.body.getReader();
|
|
35
|
+
const chunks = [];
|
|
36
|
+
let size = 0;
|
|
37
|
+
for (;;) {
|
|
38
|
+
const { done, value } = await reader.read();
|
|
39
|
+
if (done)
|
|
40
|
+
break;
|
|
41
|
+
size += value.byteLength;
|
|
42
|
+
if (size > config.maxBytes) {
|
|
43
|
+
await reader.cancel();
|
|
44
|
+
throw new Error(`price file exceeds ${config.maxBytes} bytes`);
|
|
45
|
+
}
|
|
46
|
+
chunks.push(value);
|
|
47
|
+
}
|
|
48
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
49
|
+
return { notModified: false, data: JSON.parse(text), etag: res.headers.get("etag") };
|
|
50
|
+
}
|
|
51
|
+
export class AiPricingSync {
|
|
52
|
+
model;
|
|
53
|
+
config;
|
|
54
|
+
logger;
|
|
55
|
+
fetchImpl;
|
|
56
|
+
constructor(model, config, logger = console, fetchImpl) {
|
|
57
|
+
this.model = model;
|
|
58
|
+
this.config = config;
|
|
59
|
+
this.logger = logger;
|
|
60
|
+
this.fetchImpl = fetchImpl;
|
|
61
|
+
}
|
|
62
|
+
async run() {
|
|
63
|
+
const empty = { entries: 0, rejected: 0, inserted: 0, replaced: 0, verified: 0, queuedForReview: 0 };
|
|
64
|
+
if (this.config.mode === "off")
|
|
65
|
+
return { status: "off", ...empty };
|
|
66
|
+
const state = await this.model.getState(SOURCE);
|
|
67
|
+
// An ETag only holds for the ref it was fetched from.
|
|
68
|
+
const etag = state?.sourceRef === this.config.ref ? state.etag : null;
|
|
69
|
+
let fetched;
|
|
70
|
+
try {
|
|
71
|
+
fetched = await fetchPriceFile(this.config, etag, this.fetchImpl);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
await this.model.saveState(SOURCE, { lastCheckedAt: new Date(), lastError: String(e?.message ?? e).slice(0, 1000) });
|
|
75
|
+
this.logger.warn?.(`[AiPricing] sync failed: ${e?.message ?? e}`);
|
|
76
|
+
return { status: "failed", ...empty, error: String(e?.message ?? e) };
|
|
77
|
+
}
|
|
78
|
+
if (fetched.notModified === true) {
|
|
79
|
+
await this.model.saveState(SOURCE, { lastCheckedAt: new Date(), lastError: null });
|
|
80
|
+
return { status: "not_modified", ...empty };
|
|
81
|
+
}
|
|
82
|
+
const { entries, rejected } = mapLiteLLM(fetched.data);
|
|
83
|
+
if (rejected.length) {
|
|
84
|
+
this.logger.warn?.(`[AiPricing] ${rejected.length} entries rejected (missing/zero/invalid prices), e.g. ${rejected.slice(0, 3).map((r) => r.key).join(", ")}`);
|
|
85
|
+
}
|
|
86
|
+
const plan = planPriceChanges(entries, await this.model.loadPrices(true), {
|
|
87
|
+
threshold: this.config.threshold,
|
|
88
|
+
pendingReviewKeys: await this.model.pendingReviewKeys(),
|
|
89
|
+
});
|
|
90
|
+
const counts = {
|
|
91
|
+
entries: entries.length,
|
|
92
|
+
rejected: rejected.length,
|
|
93
|
+
inserted: plan.inserts.length,
|
|
94
|
+
replaced: plan.replaces.length,
|
|
95
|
+
verified: plan.verifies.length,
|
|
96
|
+
queuedForReview: plan.reviews.length,
|
|
97
|
+
};
|
|
98
|
+
if (this.config.mode === "dry-run") {
|
|
99
|
+
// Nothing written, and no ETag kept, so the next run compares again.
|
|
100
|
+
await this.model.saveState(SOURCE, { lastCheckedAt: new Date(), lastError: null });
|
|
101
|
+
this.logger.info?.(`[AiPricing] dry-run ${JSON.stringify(counts)}`);
|
|
102
|
+
return { status: "dry_run", ...counts };
|
|
103
|
+
}
|
|
104
|
+
if (!(await this.model.applyPlan(plan, this.config.ref))) {
|
|
105
|
+
return { status: "skipped_locked", ...empty };
|
|
106
|
+
}
|
|
107
|
+
const changed = plan.inserts.length + plan.replaces.length > 0;
|
|
108
|
+
await this.model.saveState(SOURCE, {
|
|
109
|
+
sourceRef: this.config.ref,
|
|
110
|
+
etag: fetched.etag,
|
|
111
|
+
lastCheckedAt: new Date(),
|
|
112
|
+
...(changed ? { lastChangedAt: new Date() } : {}),
|
|
113
|
+
lastError: null,
|
|
114
|
+
});
|
|
115
|
+
this.logger.info?.(`[AiPricing] sync applied ${JSON.stringify(counts)}`);
|
|
116
|
+
return { status: "applied", ...counts };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=ai-pricing.sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-pricing.sync.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI model pricing — platform-level list prices for every AI provider, used to
|
|
3
|
+
* price meyiconnect.ai_usage_log rows. Design: insight-ai/docs/ai-model-pricing-plan.md.
|
|
4
|
+
*
|
|
5
|
+
* List prices only: never store Meyi discounts, cost basis or margin here — these
|
|
6
|
+
* rates drive tenant-visible costs.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PRICE_SOURCES: readonly ["litellm", "manual"];
|
|
9
|
+
export type PriceSource = (typeof PRICE_SOURCES)[number];
|
|
10
|
+
/** Rates per 1,000,000 tokens, USD. A null cache rate means the provider publishes none. */
|
|
11
|
+
export interface PriceRates {
|
|
12
|
+
inputPerMillion: number;
|
|
13
|
+
outputPerMillion: number;
|
|
14
|
+
cacheReadPerMillion: number | null;
|
|
15
|
+
cacheWritePerMillion: number | null;
|
|
16
|
+
}
|
|
17
|
+
/** One row of meyiconnect.ai_model_pricing. */
|
|
18
|
+
export interface PriceRow extends PriceRates {
|
|
19
|
+
id: string;
|
|
20
|
+
provider: string;
|
|
21
|
+
modelKey: string;
|
|
22
|
+
/** '*' = any region. */
|
|
23
|
+
region: string;
|
|
24
|
+
currency: string;
|
|
25
|
+
effectiveFrom: Date;
|
|
26
|
+
effectiveTo: Date | null;
|
|
27
|
+
source: PriceSource;
|
|
28
|
+
sourceRef: string | null;
|
|
29
|
+
verifiedAt: Date | null;
|
|
30
|
+
}
|
|
31
|
+
export interface PriceAlias {
|
|
32
|
+
provider: string;
|
|
33
|
+
alias: string;
|
|
34
|
+
modelKey: string;
|
|
35
|
+
}
|
|
36
|
+
/** A price entry read from an external source, before it is compared with the table. */
|
|
37
|
+
export interface PriceEntry extends PriceRates {
|
|
38
|
+
provider: string;
|
|
39
|
+
modelKey: string;
|
|
40
|
+
region: string;
|
|
41
|
+
}
|
|
42
|
+
export interface SyncState {
|
|
43
|
+
source: string;
|
|
44
|
+
sourceRef: string | null;
|
|
45
|
+
etag: string | null;
|
|
46
|
+
lastCheckedAt: Date | null;
|
|
47
|
+
lastChangedAt: Date | null;
|
|
48
|
+
lastRepricedAt: Date | null;
|
|
49
|
+
lastError: string | null;
|
|
50
|
+
}
|
|
51
|
+
export type SyncMode = "off" | "dry-run" | "apply";
|
|
52
|
+
export interface SyncResult {
|
|
53
|
+
status: "not_modified" | "applied" | "dry_run" | "skipped_locked" | "failed" | "off";
|
|
54
|
+
entries: number;
|
|
55
|
+
rejected: number;
|
|
56
|
+
inserted: number;
|
|
57
|
+
replaced: number;
|
|
58
|
+
verified: number;
|
|
59
|
+
queuedForReview: number;
|
|
60
|
+
error?: string;
|
|
61
|
+
}
|
|
62
|
+
export declare class AiPricingError extends Error {
|
|
63
|
+
status: number;
|
|
64
|
+
constructor(message: string, status?: number);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=ai-pricing.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-pricing.types.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa,gCAAiC,CAAC;AAC5D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,+CAA+C;AAC/C,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,IAAI,CAAC;IACpB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wFAAwF;AACxF,MAAM,WAAW,UAAW,SAAQ,UAAU;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrF,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,cAAe,SAAQ,KAAK;IACH,MAAM;gBAA9B,OAAO,EAAE,MAAM,EAAS,MAAM,SAAM;CAGjD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI model pricing — platform-level list prices for every AI provider, used to
|
|
3
|
+
* price meyiconnect.ai_usage_log rows. Design: insight-ai/docs/ai-model-pricing-plan.md.
|
|
4
|
+
*
|
|
5
|
+
* List prices only: never store Meyi discounts, cost basis or margin here — these
|
|
6
|
+
* rates drive tenant-visible costs.
|
|
7
|
+
*/
|
|
8
|
+
export const PRICE_SOURCES = ["litellm", "manual"];
|
|
9
|
+
export class AiPricingError extends Error {
|
|
10
|
+
status;
|
|
11
|
+
constructor(message, status = 400) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=ai-pricing.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-pricing.types.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAU,CAAC;AA8D5D,MAAM,OAAO,cAAe,SAAQ,KAAK;IACH;IAApC,YAAY,OAAe,EAAS,SAAS,GAAG;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QADmB,WAAM,GAAN,MAAM,CAAM;IAEhD,CAAC;CACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AiPricingModel } from "./ai-pricing.model.js";
|
|
2
|
+
import type { AiPricingSync } from "./ai-pricing.sync.js";
|
|
3
|
+
type Logger = {
|
|
4
|
+
info?: (m: string) => void;
|
|
5
|
+
warn?: (m: string) => void;
|
|
6
|
+
error?: (e: any, m: string) => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Prices unpriced ai_usage_log rows from the last *windowDays* at the rate in
|
|
10
|
+
* force when each row was written. Rows that already have a cost are never
|
|
11
|
+
* touched (the UPDATE is guarded by priced = false), so this is idempotent.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AiPricingRepricer {
|
|
14
|
+
private model;
|
|
15
|
+
private windowDays;
|
|
16
|
+
private logger;
|
|
17
|
+
constructor(model: AiPricingModel, windowDays?: number, logger?: Logger);
|
|
18
|
+
run(now?: Date): Promise<{
|
|
19
|
+
scanned: number;
|
|
20
|
+
priced: number;
|
|
21
|
+
} | null>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Background scheduler: checks hourly, syncs when the last check is older than a
|
|
25
|
+
* day, and re-prices after a sync that changed prices (otherwise at most daily).
|
|
26
|
+
* Never on a request path; one replica at a time via the sync's advisory lock.
|
|
27
|
+
*/
|
|
28
|
+
export declare class AiPricingWorker {
|
|
29
|
+
private model;
|
|
30
|
+
private sync;
|
|
31
|
+
private repricer;
|
|
32
|
+
private logger;
|
|
33
|
+
private options;
|
|
34
|
+
private timer;
|
|
35
|
+
private initial;
|
|
36
|
+
private running;
|
|
37
|
+
constructor(model: AiPricingModel, sync: AiPricingSync, repricer: AiPricingRepricer, logger?: Logger, options?: {
|
|
38
|
+
tickMs?: number;
|
|
39
|
+
syncEveryMs?: number;
|
|
40
|
+
startDelayMs?: number;
|
|
41
|
+
});
|
|
42
|
+
tick(now?: Date): Promise<void>;
|
|
43
|
+
start(): void;
|
|
44
|
+
stop(): void;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=ai-pricing.worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-pricing.worker.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAM1D,KAAK,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CAAC;AAE9G;;;;GAIG;AACH,qBAAa,iBAAiB;IAChB,OAAO,CAAC,KAAK;IAAkB,OAAO,CAAC,UAAU;IAAO,OAAO,CAAC,MAAM;gBAA9D,KAAK,EAAE,cAAc,EAAU,UAAU,SAAK,EAAU,MAAM,GAAE,MAAuB;IAErG,GAAG,CAAC,GAAG,OAAa,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAmCjF;AAED;;;;GAIG;AACH,qBAAa,eAAe;IAMxB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IATjB,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,OAAO,CAAS;gBAGd,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,GAAE,MAAuB,EAC/B,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAO;IAGlF,IAAI,CAAC,GAAG,OAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB3C,KAAK,IAAI,IAAI;IAQb,IAAI,IAAI,IAAI;CAMb"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { PriceIndex } from "./pricing.lookup.js";
|
|
2
|
+
import { repriceRow } from "./pricing.reprice.js";
|
|
3
|
+
const SOURCE = "litellm";
|
|
4
|
+
const DAY_MS = 24 * 60 * 60_000;
|
|
5
|
+
const BATCH = 500;
|
|
6
|
+
/**
|
|
7
|
+
* Prices unpriced ai_usage_log rows from the last *windowDays* at the rate in
|
|
8
|
+
* force when each row was written. Rows that already have a cost are never
|
|
9
|
+
* touched (the UPDATE is guarded by priced = false), so this is idempotent.
|
|
10
|
+
*/
|
|
11
|
+
export class AiPricingRepricer {
|
|
12
|
+
model;
|
|
13
|
+
windowDays;
|
|
14
|
+
logger;
|
|
15
|
+
constructor(model, windowDays = 90, logger = console) {
|
|
16
|
+
this.model = model;
|
|
17
|
+
this.windowDays = windowDays;
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
}
|
|
20
|
+
async run(now = new Date()) {
|
|
21
|
+
const [rows, aliases] = await Promise.all([this.model.loadPrices(false), this.model.loadAliases()]);
|
|
22
|
+
if (!rows.length)
|
|
23
|
+
return { scanned: 0, priced: 0 };
|
|
24
|
+
const index = new PriceIndex(rows, aliases);
|
|
25
|
+
const since = new Date(now.getTime() - this.windowDays * DAY_MS);
|
|
26
|
+
const result = await this.model.tryLockForReprice(async (tx) => {
|
|
27
|
+
let scanned = 0;
|
|
28
|
+
let priced = 0;
|
|
29
|
+
let cursor = null;
|
|
30
|
+
for (;;) {
|
|
31
|
+
const batch = await this.model.unpricedBatch(tx, since, cursor, BATCH);
|
|
32
|
+
if (!batch.length)
|
|
33
|
+
break;
|
|
34
|
+
for (const row of batch) {
|
|
35
|
+
scanned += 1;
|
|
36
|
+
const createdAt = new Date(row.created_at);
|
|
37
|
+
const update = repriceRow(row.models, createdAt, index);
|
|
38
|
+
if (!update)
|
|
39
|
+
continue;
|
|
40
|
+
const totalTokens = update.totalInputTokens + Number(row.total_output_tokens || 0)
|
|
41
|
+
+ Number(row.total_cache_read_tokens || 0) + Number(row.total_cache_write_tokens || 0);
|
|
42
|
+
await this.model.saveRepriced(tx, String(row.id), { ...update, totalTokens });
|
|
43
|
+
priced += 1;
|
|
44
|
+
}
|
|
45
|
+
const last = batch[batch.length - 1];
|
|
46
|
+
cursor = { createdAt: new Date(last.created_at), id: String(last.id) };
|
|
47
|
+
if (batch.length < BATCH)
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
return { scanned, priced };
|
|
51
|
+
});
|
|
52
|
+
if (result) {
|
|
53
|
+
await this.model.saveState(SOURCE, { lastRepricedAt: new Date() });
|
|
54
|
+
if (result.priced)
|
|
55
|
+
this.logger.info?.(`[AiPricing] re-priced ${result.priced} of ${result.scanned} unpriced usage rows`);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Background scheduler: checks hourly, syncs when the last check is older than a
|
|
62
|
+
* day, and re-prices after a sync that changed prices (otherwise at most daily).
|
|
63
|
+
* Never on a request path; one replica at a time via the sync's advisory lock.
|
|
64
|
+
*/
|
|
65
|
+
export class AiPricingWorker {
|
|
66
|
+
model;
|
|
67
|
+
sync;
|
|
68
|
+
repricer;
|
|
69
|
+
logger;
|
|
70
|
+
options;
|
|
71
|
+
timer = null;
|
|
72
|
+
initial = null;
|
|
73
|
+
running = false;
|
|
74
|
+
constructor(model, sync, repricer, logger = console, options = {}) {
|
|
75
|
+
this.model = model;
|
|
76
|
+
this.sync = sync;
|
|
77
|
+
this.repricer = repricer;
|
|
78
|
+
this.logger = logger;
|
|
79
|
+
this.options = options;
|
|
80
|
+
}
|
|
81
|
+
async tick(now = new Date()) {
|
|
82
|
+
if (this.running)
|
|
83
|
+
return;
|
|
84
|
+
this.running = true;
|
|
85
|
+
try {
|
|
86
|
+
const state = await this.model.getState(SOURCE);
|
|
87
|
+
const syncEvery = this.options.syncEveryMs ?? DAY_MS;
|
|
88
|
+
let pricesChanged = false;
|
|
89
|
+
if (!state?.lastCheckedAt || now.getTime() - state.lastCheckedAt.getTime() >= syncEvery) {
|
|
90
|
+
const result = await this.sync.run();
|
|
91
|
+
pricesChanged = result.status === "applied" && result.inserted + result.replaced > 0;
|
|
92
|
+
}
|
|
93
|
+
const repriceDue = !state?.lastRepricedAt || now.getTime() - state.lastRepricedAt.getTime() >= DAY_MS;
|
|
94
|
+
if (pricesChanged || repriceDue)
|
|
95
|
+
await this.repricer.run(now);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
this.logger.error?.(e, "[AiPricing] tick failed");
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
this.running = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
start() {
|
|
105
|
+
if (this.timer)
|
|
106
|
+
return;
|
|
107
|
+
this.initial = setTimeout(() => void this.tick(), this.options.startDelayMs ?? 30_000);
|
|
108
|
+
this.initial.unref?.();
|
|
109
|
+
this.timer = setInterval(() => void this.tick(), this.options.tickMs ?? 60 * 60_000);
|
|
110
|
+
this.timer.unref?.();
|
|
111
|
+
}
|
|
112
|
+
stop() {
|
|
113
|
+
if (this.initial)
|
|
114
|
+
clearTimeout(this.initial);
|
|
115
|
+
if (this.timer)
|
|
116
|
+
clearInterval(this.timer);
|
|
117
|
+
this.initial = null;
|
|
118
|
+
this.timer = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=ai-pricing.worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-pricing.worker.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./ai-pricing.types.js";
|
|
2
|
+
export { AiPricingModel } from "./ai-pricing.model.js";
|
|
3
|
+
export { AiPricingService } from "./ai-pricing.service.js";
|
|
4
|
+
export { AiPricingSync, syncConfigFromEnv, DEFAULT_LITELLM_REF } from "./ai-pricing.sync.js";
|
|
5
|
+
export { AiPricingWorker, AiPricingRepricer } from "./ai-pricing.worker.js";
|
|
6
|
+
export { PriceIndex, costOf } from "./pricing.lookup.js";
|
|
7
|
+
export { createAiPricingRouter } from "./ai-pricing.routes.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./ai-pricing.types.js";
|
|
2
|
+
export { AiPricingModel } from "./ai-pricing.model.js";
|
|
3
|
+
export { AiPricingService } from "./ai-pricing.service.js";
|
|
4
|
+
export { AiPricingSync, syncConfigFromEnv, DEFAULT_LITELLM_REF } from "./ai-pricing.sync.js";
|
|
5
|
+
export { AiPricingWorker, AiPricingRepricer } from "./ai-pricing.worker.js";
|
|
6
|
+
export { PriceIndex, costOf } from "./pricing.lookup.js";
|
|
7
|
+
export { createAiPricingRouter } from "./ai-pricing.routes.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PriceEntry } from "./ai-pricing.types.js";
|
|
2
|
+
export interface MapResult {
|
|
3
|
+
entries: PriceEntry[];
|
|
4
|
+
/** Keys that looked like ours but carried unusable prices — logged, never stored as free. */
|
|
5
|
+
rejected: Array<{
|
|
6
|
+
key: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export declare function mapLiteLLM(data: unknown): MapResult;
|
|
11
|
+
//# sourceMappingURL=litellm.mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"litellm.mapper.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/litellm.mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAmCxD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,6FAA6F;IAC7F,QAAQ,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAwBD,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAiDnD"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteLLM's model_prices_and_context_window.json → our price entries.
|
|
3
|
+
*
|
|
4
|
+
* The file is untrusted data: it is only ever parsed as JSON, never executed,
|
|
5
|
+
* and every value is validated here. Costs there are USD per token; ours are per
|
|
6
|
+
* 1,000,000 tokens.
|
|
7
|
+
*
|
|
8
|
+
* Key shapes kept (verified against commit 159b5b2f, 2026-10-21):
|
|
9
|
+
* bedrock/<region>/<model id> → bedrock, region-specific
|
|
10
|
+
* us.|eu.|apac.|au.|jp.|global.|us-gov.<id>, <id> → bedrock, region '*', prefix kept
|
|
11
|
+
* gpt-4o, openai/gpt-4o → openai
|
|
12
|
+
* claude-sonnet-4-5-20250929 → anthropic
|
|
13
|
+
* gemini/gemini-2.5-pro → gemini (prefix stripped: we send "gemini-2.5-pro")
|
|
14
|
+
* Skipped: other providers, non-chat modes (image/audio/embedding/…), and
|
|
15
|
+
* bedrock/*\/<commitment>/… (provisioned throughput, not on-demand).
|
|
16
|
+
*/
|
|
17
|
+
// litellm_provider → our provider. bedrock_converse is listed first so that, when
|
|
18
|
+
// the same model appears under both, the Converse entry (the API we call) wins.
|
|
19
|
+
const PROVIDERS = [
|
|
20
|
+
["bedrock_converse", "bedrock"],
|
|
21
|
+
["bedrock", "bedrock"],
|
|
22
|
+
["openai", "openai"],
|
|
23
|
+
["anthropic", "anthropic"],
|
|
24
|
+
["gemini", "gemini"],
|
|
25
|
+
];
|
|
26
|
+
const PRIORITY = new Map(PROVIDERS.map(([litellm], i) => [litellm, i]));
|
|
27
|
+
const OUR_PROVIDER = new Map(PROVIDERS);
|
|
28
|
+
const MODES = new Set(["chat", "responses"]);
|
|
29
|
+
const REGION = /^[a-z]{2}(-gov)?-[a-z]+-\d$/;
|
|
30
|
+
const STRIP_PREFIX = { openai: "openai/", gemini: "gemini/", anthropic: "anthropic/" };
|
|
31
|
+
function perMillion(value) {
|
|
32
|
+
if (value === undefined || value === null)
|
|
33
|
+
return null;
|
|
34
|
+
const n = Number(value);
|
|
35
|
+
if (!Number.isFinite(n) || n < 0)
|
|
36
|
+
return "invalid";
|
|
37
|
+
// numeric(18,6): round so 3e-06 * 1e6 stores as 3, not 2.9999999999999996.
|
|
38
|
+
return Math.round(n * 1_000_000 * 1e6) / 1e6;
|
|
39
|
+
}
|
|
40
|
+
function keyParts(provider, key) {
|
|
41
|
+
if (provider === "bedrock") {
|
|
42
|
+
if (!key.startsWith("bedrock/"))
|
|
43
|
+
return key.includes("/") ? null : { modelKey: key, region: "*" };
|
|
44
|
+
const [, region, ...rest] = key.split("/");
|
|
45
|
+
const modelKey = rest.join("/");
|
|
46
|
+
if (!REGION.test(region) || !modelKey || modelKey.includes("/"))
|
|
47
|
+
return null; // bedrock/*/commitment/…, image sizes
|
|
48
|
+
return { modelKey, region };
|
|
49
|
+
}
|
|
50
|
+
const prefix = STRIP_PREFIX[provider];
|
|
51
|
+
const modelKey = prefix && key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
52
|
+
if (!modelKey || modelKey.includes("/"))
|
|
53
|
+
return null; // e.g. openai image-size keys "1024-x-1024/…"
|
|
54
|
+
return { modelKey, region: "*" };
|
|
55
|
+
}
|
|
56
|
+
export function mapLiteLLM(data) {
|
|
57
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
58
|
+
throw new Error("LiteLLM price file is not a JSON object");
|
|
59
|
+
}
|
|
60
|
+
const chosen = new Map();
|
|
61
|
+
const rejected = [];
|
|
62
|
+
for (const [key, raw] of Object.entries(data)) {
|
|
63
|
+
if (key === "sample_spec" || !raw || typeof raw !== "object")
|
|
64
|
+
continue;
|
|
65
|
+
const spec = raw;
|
|
66
|
+
const litellmProvider = String(spec.litellm_provider ?? "");
|
|
67
|
+
const provider = OUR_PROVIDER.get(litellmProvider);
|
|
68
|
+
if (!provider || !MODES.has(String(spec.mode ?? "")))
|
|
69
|
+
continue;
|
|
70
|
+
const parts = keyParts(provider, key);
|
|
71
|
+
if (!parts)
|
|
72
|
+
continue;
|
|
73
|
+
const input = perMillion(spec.input_cost_per_token);
|
|
74
|
+
const output = perMillion(spec.output_cost_per_token);
|
|
75
|
+
const cacheRead = perMillion(spec.cache_read_input_token_cost);
|
|
76
|
+
const cacheWrite = perMillion(spec.cache_creation_input_token_cost);
|
|
77
|
+
if (input === null || output === null || input === "invalid" || output === "invalid" || input === 0 || output === 0) {
|
|
78
|
+
rejected.push({ key, reason: "input/output price missing, zero or invalid" });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (cacheRead === "invalid" || cacheWrite === "invalid") {
|
|
82
|
+
rejected.push({ key, reason: "cache price invalid" });
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const entry = {
|
|
86
|
+
provider,
|
|
87
|
+
modelKey: parts.modelKey,
|
|
88
|
+
region: parts.region,
|
|
89
|
+
inputPerMillion: input,
|
|
90
|
+
outputPerMillion: output,
|
|
91
|
+
cacheReadPerMillion: cacheRead,
|
|
92
|
+
cacheWritePerMillion: cacheWrite,
|
|
93
|
+
};
|
|
94
|
+
const id = `${provider}|${parts.modelKey}|${parts.region}`;
|
|
95
|
+
const priority = PRIORITY.get(litellmProvider) ?? 99;
|
|
96
|
+
const existing = chosen.get(id);
|
|
97
|
+
if (!existing || priority < existing.priority)
|
|
98
|
+
chosen.set(id, { entry, priority });
|
|
99
|
+
}
|
|
100
|
+
const entries = [...chosen.values()].map((c) => c.entry);
|
|
101
|
+
entries.sort((a, b) => `${a.provider}|${a.modelKey}|${a.region}`.localeCompare(`${b.provider}|${b.modelKey}|${b.region}`));
|
|
102
|
+
return { entries, rejected };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=litellm.mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"litellm.mapper.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/litellm.mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { PriceAlias, PriceRates, PriceRow } from "./ai-pricing.types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The one price-lookup rule set. insight-cost-server (JS) and insight-ai (Python)
|
|
4
|
+
* implement the same rules; all three are tested against
|
|
5
|
+
* docs/fixtures/pricing-lookup-vectors.json so they cannot drift.
|
|
6
|
+
*
|
|
7
|
+
* provider lower-cased → ARN reduced to the ID after its last '/' → alias →
|
|
8
|
+
* exact (provider, model_key, region) → (provider, model_key, '*') → unpriced.
|
|
9
|
+
*
|
|
10
|
+
* Exact IDs only — no fuzzy matching. Pure and in-memory: no I/O, so it can run
|
|
11
|
+
* anywhere without delaying anything.
|
|
12
|
+
*/
|
|
13
|
+
export declare class PriceIndex {
|
|
14
|
+
private rows;
|
|
15
|
+
private aliases;
|
|
16
|
+
constructor(rows: PriceRow[], aliases?: PriceAlias[]);
|
|
17
|
+
static key(provider: string, modelKey: string, region: string): string;
|
|
18
|
+
get size(): number;
|
|
19
|
+
/** The row in force at *at* (default: the current row), or null. */
|
|
20
|
+
lookup(provider: string, modelId: string, region?: string | null, at?: Date): PriceRow | null;
|
|
21
|
+
private inForce;
|
|
22
|
+
}
|
|
23
|
+
export interface TokenCounts {
|
|
24
|
+
/** Uncached input tokens. */
|
|
25
|
+
inputTokens: number;
|
|
26
|
+
outputTokens: number;
|
|
27
|
+
cacheReadTokens: number;
|
|
28
|
+
cacheWriteTokens: number;
|
|
29
|
+
}
|
|
30
|
+
export interface LineCost {
|
|
31
|
+
cost: number;
|
|
32
|
+
/** Set when some tokens had no published rate and are excluded from the cost. */
|
|
33
|
+
reason: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** tokens × rate per kind, summed; rounded once to 6 places (numeric(18,6)). */
|
|
36
|
+
export declare function costOf(tokens: TokenCounts, rates: PriceRates): LineCost;
|
|
37
|
+
/** The snapshot stored on each priced model line in ai_usage_log.models. */
|
|
38
|
+
export declare function priceSnapshot(row: PriceRow): Record<string, unknown>;
|
|
39
|
+
//# sourceMappingURL=pricing.lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.lookup.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/pricing.lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE9E;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAiC;IAC7C,OAAO,CAAC,OAAO,CAA6B;gBAEhC,IAAI,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAE,UAAU,EAAO;IAaxD,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAItE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,oEAAoE;IACpE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,GAAG,QAAQ,GAAG,IAAI;IAW7F,OAAO,CAAC,OAAO;CAOhB;AAED,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,gFAAgF;AAChF,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,GAAG,QAAQ,CAgBvE;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUpE"}
|