@graphorin/pricing 0.5.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -0
- package/dist/diff.d.ts +15 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +65 -0
- package/dist/diff.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/lookup.d.ts +52 -0
- package/dist/lookup.d.ts.map +1 -0
- package/dist/lookup.js +92 -0
- package/dist/lookup.js.map +1 -0
- package/dist/missing.d.ts +24 -0
- package/dist/missing.d.ts.map +1 -0
- package/dist/missing.js +62 -0
- package/dist/missing.js.map +1 -0
- package/dist/refresh.d.ts +44 -0
- package/dist/refresh.d.ts.map +1 -0
- package/dist/refresh.js +83 -0
- package/dist/refresh.js.map +1 -0
- package/dist/snapshot/bundled.d.ts +25 -0
- package/dist/snapshot/bundled.d.ts.map +1 -0
- package/dist/snapshot/bundled.js +157 -0
- package/dist/snapshot/bundled.js.map +1 -0
- package/dist/snapshot/index.d.ts +2 -0
- package/dist/snapshot/index.js +3 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +65 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oleksiy Stepurenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @graphorin/pricing
|
|
2
|
+
|
|
3
|
+
> Bundled LLM pricing snapshot for the [Graphorin](https://github.com/o-stepper/graphorin) framework.
|
|
4
|
+
|
|
5
|
+
`@graphorin/pricing` is a tiny dependency-free package that ships a
|
|
6
|
+
representative LLM pricing catalogue, a `lookupPrice(...)` resolver, a
|
|
7
|
+
snapshot-diff utility, an opt-in `refreshPricing(...)` hook, and the
|
|
8
|
+
`listMissingModels(...)` utility used by trace audits.
|
|
9
|
+
|
|
10
|
+
The package is intentionally separate from `@graphorin/observability`
|
|
11
|
+
so consumers can:
|
|
12
|
+
|
|
13
|
+
- pin the pricing snapshot independently of the framework version,
|
|
14
|
+
- ship a custom snapshot without forking the observability package,
|
|
15
|
+
- decline the bundled snapshot entirely (just don't import it).
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @graphorin/pricing
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import {
|
|
27
|
+
BUNDLED_SNAPSHOT,
|
|
28
|
+
calculateCost,
|
|
29
|
+
lookupPrice,
|
|
30
|
+
} from '@graphorin/pricing';
|
|
31
|
+
|
|
32
|
+
const price = lookupPrice({ provider: 'anthropic', model: 'claude-3-5-sonnet-20241022' });
|
|
33
|
+
if (price !== null) {
|
|
34
|
+
console.log(`Input: $${price.inputUsdPerToken} / token`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cost = calculateCost(
|
|
38
|
+
{
|
|
39
|
+
provider: 'openai',
|
|
40
|
+
model: 'gpt-4o-2024-11-20',
|
|
41
|
+
inputTokens: 1_000,
|
|
42
|
+
outputTokens: 500,
|
|
43
|
+
},
|
|
44
|
+
BUNDLED_SNAPSHOT,
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Refreshing
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { refreshPricing } from '@graphorin/pricing';
|
|
52
|
+
import { writeFile } from 'node:fs/promises';
|
|
53
|
+
|
|
54
|
+
const snapshot = await refreshPricing({
|
|
55
|
+
url: 'https://example.com/pricing.json',
|
|
56
|
+
});
|
|
57
|
+
await writeFile('./pricing.json', JSON.stringify(snapshot, null, 2));
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The framework never invokes `refreshPricing(...)` automatically per
|
|
61
|
+
the zero-default-telemetry policy; the operator runs it on demand.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT © 2026 [Oleksiy Stepurenko](https://github.com/o-stepper).
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
**Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/config.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Configuration shapes consumed by `pricing.*`.
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Auto-refresh configuration. The `enabled` flag exists in the type
|
|
9
|
+
* for forward compatibility but is **enforced false** in v0.1 per the
|
|
10
|
+
* zero-default-telemetry policy: refreshing the snapshot makes an
|
|
11
|
+
* outbound HTTP call, so it must remain an explicit user action.
|
|
12
|
+
*
|
|
13
|
+
* @stable
|
|
14
|
+
*/
|
|
15
|
+
interface PricingAutoRefreshConfig {
|
|
16
|
+
/** Always `false` in v0.1. Reserved for v0.2+. */
|
|
17
|
+
readonly enabled: false;
|
|
18
|
+
/** Suggested cadence for v0.2+. Ignored at runtime in v0.1. */
|
|
19
|
+
readonly intervalHours?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Default auto-refresh configuration.
|
|
23
|
+
*
|
|
24
|
+
* @stable
|
|
25
|
+
*/
|
|
26
|
+
declare const DEFAULT_PRICING_AUTO_REFRESH: PricingAutoRefreshConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Container shape for the broader `pricing.*` configuration.
|
|
29
|
+
*
|
|
30
|
+
* @stable
|
|
31
|
+
*/
|
|
32
|
+
interface PricingConfig {
|
|
33
|
+
readonly autoRefresh?: PricingAutoRefreshConfig;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { DEFAULT_PRICING_AUTO_REFRESH, PricingAutoRefreshConfig, PricingConfig };
|
|
37
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;AAcA;AAYA;AAUA;;;;;;;;;;UAtBiB,wBAAA;;;;;;;;;;;cAYJ,8BAA8B;;;;;;UAU1B,aAAA;yBACQ"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/config.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default auto-refresh configuration.
|
|
4
|
+
*
|
|
5
|
+
* @stable
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_PRICING_AUTO_REFRESH = Object.freeze({
|
|
8
|
+
enabled: false,
|
|
9
|
+
intervalHours: 24
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { DEFAULT_PRICING_AUTO_REFRESH };
|
|
14
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","names":["DEFAULT_PRICING_AUTO_REFRESH: PricingAutoRefreshConfig"],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Configuration shapes consumed by `pricing.*`.\n *\n * @packageDocumentation\n */\n\n/**\n * Auto-refresh configuration. The `enabled` flag exists in the type\n * for forward compatibility but is **enforced false** in v0.1 per the\n * zero-default-telemetry policy: refreshing the snapshot makes an\n * outbound HTTP call, so it must remain an explicit user action.\n *\n * @stable\n */\nexport interface PricingAutoRefreshConfig {\n /** Always `false` in v0.1. Reserved for v0.2+. */\n readonly enabled: false;\n /** Suggested cadence for v0.2+. Ignored at runtime in v0.1. */\n readonly intervalHours?: number;\n}\n\n/**\n * Default auto-refresh configuration.\n *\n * @stable\n */\nexport const DEFAULT_PRICING_AUTO_REFRESH: PricingAutoRefreshConfig = Object.freeze({\n enabled: false as const,\n intervalHours: 24,\n});\n\n/**\n * Container shape for the broader `pricing.*` configuration.\n *\n * @stable\n */\nexport interface PricingConfig {\n readonly autoRefresh?: PricingAutoRefreshConfig;\n}\n"],"mappings":";;;;;;AA0BA,MAAaA,+BAAyD,OAAO,OAAO;CAClF,SAAS;CACT,eAAe;CAChB,CAAC"}
|
package/dist/diff.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PricingDiffEntry, PricingSnapshot } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/diff.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compare two snapshots and return one entry per (provider, model)
|
|
7
|
+
* pair that has been added, removed, or changed. The result is sorted
|
|
8
|
+
* by `(provider, model, kind)` for deterministic output.
|
|
9
|
+
*
|
|
10
|
+
* @stable
|
|
11
|
+
*/
|
|
12
|
+
declare function diffPricing(before: PricingSnapshot, after: PricingSnapshot): ReadonlyArray<PricingDiffEntry>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { diffPricing };
|
|
15
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","names":[],"sources":["../src/diff.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;iBA0BgB,WAAA,SACN,wBACD,kBACN,cAAc"}
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/diff.ts
|
|
2
|
+
const COMPARED_FIELDS = [
|
|
3
|
+
"inputUsdPerToken",
|
|
4
|
+
"outputUsdPerToken",
|
|
5
|
+
"cachedReadUsdPerToken",
|
|
6
|
+
"reasoningUsdPerToken",
|
|
7
|
+
"region",
|
|
8
|
+
"notes"
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Compare two snapshots and return one entry per (provider, model)
|
|
12
|
+
* pair that has been added, removed, or changed. The result is sorted
|
|
13
|
+
* by `(provider, model, kind)` for deterministic output.
|
|
14
|
+
*
|
|
15
|
+
* @stable
|
|
16
|
+
*/
|
|
17
|
+
function diffPricing(before, after) {
|
|
18
|
+
const beforeMap = indexBy(before.entries);
|
|
19
|
+
const afterMap = indexBy(after.entries);
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const [key, entry] of beforeMap) {
|
|
22
|
+
const next = afterMap.get(key);
|
|
23
|
+
if (next === void 0) {
|
|
24
|
+
out.push({
|
|
25
|
+
provider: entry.provider,
|
|
26
|
+
model: entry.model,
|
|
27
|
+
kind: "removed",
|
|
28
|
+
before: entry
|
|
29
|
+
});
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const changedFields = COMPARED_FIELDS.filter((field) => entry[field] !== next[field]);
|
|
33
|
+
if (changedFields.length > 0) out.push({
|
|
34
|
+
provider: entry.provider,
|
|
35
|
+
model: entry.model,
|
|
36
|
+
kind: "changed",
|
|
37
|
+
before: entry,
|
|
38
|
+
after: next,
|
|
39
|
+
changedFields
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
for (const [key, entry] of afterMap) {
|
|
43
|
+
if (beforeMap.has(key)) continue;
|
|
44
|
+
out.push({
|
|
45
|
+
provider: entry.provider,
|
|
46
|
+
model: entry.model,
|
|
47
|
+
kind: "added",
|
|
48
|
+
after: entry
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return out.sort((a, b) => {
|
|
52
|
+
if (a.provider !== b.provider) return a.provider < b.provider ? -1 : 1;
|
|
53
|
+
if (a.model !== b.model) return a.model < b.model ? -1 : 1;
|
|
54
|
+
return a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function indexBy(entries) {
|
|
58
|
+
const out = /* @__PURE__ */ new Map();
|
|
59
|
+
for (const entry of entries) out.set(`${entry.provider}/${entry.model}/${entry.region ?? ""}`, entry);
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { diffPricing };
|
|
65
|
+
//# sourceMappingURL=diff.js.map
|
package/dist/diff.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","names":["COMPARED_FIELDS: ReadonlyArray<keyof ModelPrice>","out: PricingDiffEntry[]"],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * `diffPricing(...)` — produce a row-by-row delta between two pricing\n * snapshots. Used by the upcoming `graphorin pricing diff` CLI to\n * surface upstream changes after a `refresh`.\n *\n * @packageDocumentation\n */\n\nimport type { ModelPrice, PricingDiffEntry, PricingSnapshot } from './types.js';\n\nconst COMPARED_FIELDS: ReadonlyArray<keyof ModelPrice> = [\n 'inputUsdPerToken',\n 'outputUsdPerToken',\n 'cachedReadUsdPerToken',\n 'reasoningUsdPerToken',\n 'region',\n 'notes',\n];\n\n/**\n * Compare two snapshots and return one entry per (provider, model)\n * pair that has been added, removed, or changed. The result is sorted\n * by `(provider, model, kind)` for deterministic output.\n *\n * @stable\n */\nexport function diffPricing(\n before: PricingSnapshot,\n after: PricingSnapshot,\n): ReadonlyArray<PricingDiffEntry> {\n const beforeMap = indexBy(before.entries);\n const afterMap = indexBy(after.entries);\n const out: PricingDiffEntry[] = [];\n\n for (const [key, entry] of beforeMap) {\n const next = afterMap.get(key);\n if (next === undefined) {\n out.push({ provider: entry.provider, model: entry.model, kind: 'removed', before: entry });\n continue;\n }\n const changedFields = COMPARED_FIELDS.filter((field) => entry[field] !== next[field]);\n if (changedFields.length > 0) {\n out.push({\n provider: entry.provider,\n model: entry.model,\n kind: 'changed',\n before: entry,\n after: next,\n changedFields,\n });\n }\n }\n\n for (const [key, entry] of afterMap) {\n if (beforeMap.has(key)) continue;\n out.push({ provider: entry.provider, model: entry.model, kind: 'added', after: entry });\n }\n\n return out.sort((a, b) => {\n if (a.provider !== b.provider) return a.provider < b.provider ? -1 : 1;\n if (a.model !== b.model) return a.model < b.model ? -1 : 1;\n return a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0;\n });\n}\n\nfunction indexBy(entries: ReadonlyArray<ModelPrice>): Map<string, ModelPrice> {\n const out = new Map<string, ModelPrice>();\n for (const entry of entries) {\n out.set(`${entry.provider}/${entry.model}/${entry.region ?? ''}`, entry);\n }\n return out;\n}\n"],"mappings":";AAUA,MAAMA,kBAAmD;CACvD;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,SAAgB,YACd,QACA,OACiC;CACjC,MAAM,YAAY,QAAQ,OAAO,QAAQ;CACzC,MAAM,WAAW,QAAQ,MAAM,QAAQ;CACvC,MAAMC,MAA0B,EAAE;AAElC,MAAK,MAAM,CAAC,KAAK,UAAU,WAAW;EACpC,MAAM,OAAO,SAAS,IAAI,IAAI;AAC9B,MAAI,SAAS,QAAW;AACtB,OAAI,KAAK;IAAE,UAAU,MAAM;IAAU,OAAO,MAAM;IAAO,MAAM;IAAW,QAAQ;IAAO,CAAC;AAC1F;;EAEF,MAAM,gBAAgB,gBAAgB,QAAQ,UAAU,MAAM,WAAW,KAAK,OAAO;AACrF,MAAI,cAAc,SAAS,EACzB,KAAI,KAAK;GACP,UAAU,MAAM;GAChB,OAAO,MAAM;GACb,MAAM;GACN,QAAQ;GACR,OAAO;GACP;GACD,CAAC;;AAIN,MAAK,MAAM,CAAC,KAAK,UAAU,UAAU;AACnC,MAAI,UAAU,IAAI,IAAI,CAAE;AACxB,MAAI,KAAK;GAAE,UAAU,MAAM;GAAU,OAAO,MAAM;GAAO,MAAM;GAAS,OAAO;GAAO,CAAC;;AAGzF,QAAO,IAAI,MAAM,GAAG,MAAM;AACxB,MAAI,EAAE,aAAa,EAAE,SAAU,QAAO,EAAE,WAAW,EAAE,WAAW,KAAK;AACrE,MAAI,EAAE,UAAU,EAAE,MAAO,QAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK;AACzD,SAAO,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI;GACpD;;AAGJ,SAAS,QAAQ,SAA6D;CAC5E,MAAM,sBAAM,IAAI,KAAyB;AACzC,MAAK,MAAM,SAAS,QAClB,KAAI,IAAI,GAAG,MAAM,SAAS,GAAG,MAAM,MAAM,GAAG,MAAM,UAAU,MAAM,MAAM;AAE1E,QAAO"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DEFAULT_PRICING_AUTO_REFRESH, PricingAutoRefreshConfig, PricingConfig } from "./config.js";
|
|
2
|
+
import { Cost, LookupPriceArgs, LookupPriceResult, ModelPrice, PricingDiffEntry, PricingSnapshot, PricingTraceSpanLike } from "./types.js";
|
|
3
|
+
import { diffPricing } from "./diff.js";
|
|
4
|
+
import { _resetLookupWarningsForTesting, calculateCost, lookupPrice, setLookupWarnSink } from "./lookup.js";
|
|
5
|
+
import { MissingModelEntry, listMissingModels } from "./missing.js";
|
|
6
|
+
import { RefreshPricingOptions, refreshPricing } from "./refresh.js";
|
|
7
|
+
import { BUNDLED_SNAPSHOT, SNAPSHOT_DATE, computeEntriesDigest } from "./snapshot/bundled.js";
|
|
8
|
+
|
|
9
|
+
//#region src/index.d.ts
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @graphorin/pricing — bundled LLM pricing snapshot for the Graphorin
|
|
13
|
+
* framework.
|
|
14
|
+
*
|
|
15
|
+
* Highlights:
|
|
16
|
+
*
|
|
17
|
+
* - {@link BUNDLED_SNAPSHOT} — bundled snapshot covering Anthropic,
|
|
18
|
+
* OpenAI, Google, Mistral, Cohere + the local providers
|
|
19
|
+
* (Ollama / llama.cpp). Each entry carries a per-token price and
|
|
20
|
+
* the canonical SHA-256 digest of the `entries` array.
|
|
21
|
+
* - {@link lookupPrice} — resolve a `(provider, model)` pair against a
|
|
22
|
+
* snapshot. Returns `null` for unknown entries plus emits one WARN
|
|
23
|
+
* per process-lifetime per unknown pair.
|
|
24
|
+
* - {@link calculateCost} — multiply a price by token counts for a
|
|
25
|
+
* single LLM call.
|
|
26
|
+
* - {@link diffPricing} — row-by-row delta between two snapshots.
|
|
27
|
+
* - {@link refreshPricing} — opt-in network call. Never automatic.
|
|
28
|
+
* - {@link listMissingModels} — scan trace spans for unknown models;
|
|
29
|
+
* used by `graphorin pricing missing` (Phase 15).
|
|
30
|
+
*
|
|
31
|
+
* The bundled snapshot is intentionally small — operators wanting the
|
|
32
|
+
* full upstream catalogue should run `refreshPricing(...)` and
|
|
33
|
+
* persist the result to disk.
|
|
34
|
+
*
|
|
35
|
+
* @packageDocumentation
|
|
36
|
+
*/
|
|
37
|
+
/** Canonical version constant. Mirrors the `package.json` version. */
|
|
38
|
+
declare const VERSION = "0.5.0";
|
|
39
|
+
//#endregion
|
|
40
|
+
export { BUNDLED_SNAPSHOT, type Cost, DEFAULT_PRICING_AUTO_REFRESH, type LookupPriceArgs, type LookupPriceResult, type MissingModelEntry, type ModelPrice, type PricingAutoRefreshConfig, type PricingConfig, type PricingDiffEntry, type PricingSnapshot, type PricingTraceSpanLike, type RefreshPricingOptions, SNAPSHOT_DATE, VERSION, _resetLookupWarningsForTesting, calculateCost, computeEntriesDigest, diffPricing, listMissingModels, lookupPrice, refreshPricing, setLookupWarnSink };
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;AA4BA;;;;;;;;;;;;;;;;;;cAAa,OAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DEFAULT_PRICING_AUTO_REFRESH } from "./config.js";
|
|
2
|
+
import { diffPricing } from "./diff.js";
|
|
3
|
+
import { BUNDLED_SNAPSHOT, SNAPSHOT_DATE, computeEntriesDigest } from "./snapshot/bundled.js";
|
|
4
|
+
import { _resetLookupWarningsForTesting, calculateCost, lookupPrice, setLookupWarnSink } from "./lookup.js";
|
|
5
|
+
import { listMissingModels } from "./missing.js";
|
|
6
|
+
import { refreshPricing } from "./refresh.js";
|
|
7
|
+
import "./snapshot/index.js";
|
|
8
|
+
|
|
9
|
+
//#region src/index.ts
|
|
10
|
+
/**
|
|
11
|
+
* @graphorin/pricing — bundled LLM pricing snapshot for the Graphorin
|
|
12
|
+
* framework.
|
|
13
|
+
*
|
|
14
|
+
* Highlights:
|
|
15
|
+
*
|
|
16
|
+
* - {@link BUNDLED_SNAPSHOT} — bundled snapshot covering Anthropic,
|
|
17
|
+
* OpenAI, Google, Mistral, Cohere + the local providers
|
|
18
|
+
* (Ollama / llama.cpp). Each entry carries a per-token price and
|
|
19
|
+
* the canonical SHA-256 digest of the `entries` array.
|
|
20
|
+
* - {@link lookupPrice} — resolve a `(provider, model)` pair against a
|
|
21
|
+
* snapshot. Returns `null` for unknown entries plus emits one WARN
|
|
22
|
+
* per process-lifetime per unknown pair.
|
|
23
|
+
* - {@link calculateCost} — multiply a price by token counts for a
|
|
24
|
+
* single LLM call.
|
|
25
|
+
* - {@link diffPricing} — row-by-row delta between two snapshots.
|
|
26
|
+
* - {@link refreshPricing} — opt-in network call. Never automatic.
|
|
27
|
+
* - {@link listMissingModels} — scan trace spans for unknown models;
|
|
28
|
+
* used by `graphorin pricing missing` (Phase 15).
|
|
29
|
+
*
|
|
30
|
+
* The bundled snapshot is intentionally small — operators wanting the
|
|
31
|
+
* full upstream catalogue should run `refreshPricing(...)` and
|
|
32
|
+
* persist the result to disk.
|
|
33
|
+
*
|
|
34
|
+
* @packageDocumentation
|
|
35
|
+
*/
|
|
36
|
+
/** Canonical version constant. Mirrors the `package.json` version. */
|
|
37
|
+
const VERSION = "0.5.0";
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { BUNDLED_SNAPSHOT, DEFAULT_PRICING_AUTO_REFRESH, SNAPSHOT_DATE, VERSION, _resetLookupWarningsForTesting, calculateCost, computeEntriesDigest, diffPricing, listMissingModels, lookupPrice, refreshPricing, setLookupWarnSink };
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/pricing — bundled LLM pricing snapshot for the Graphorin\n * framework.\n *\n * Highlights:\n *\n * - {@link BUNDLED_SNAPSHOT} — bundled snapshot covering Anthropic,\n * OpenAI, Google, Mistral, Cohere + the local providers\n * (Ollama / llama.cpp). Each entry carries a per-token price and\n * the canonical SHA-256 digest of the `entries` array.\n * - {@link lookupPrice} — resolve a `(provider, model)` pair against a\n * snapshot. Returns `null` for unknown entries plus emits one WARN\n * per process-lifetime per unknown pair.\n * - {@link calculateCost} — multiply a price by token counts for a\n * single LLM call.\n * - {@link diffPricing} — row-by-row delta between two snapshots.\n * - {@link refreshPricing} — opt-in network call. Never automatic.\n * - {@link listMissingModels} — scan trace spans for unknown models;\n * used by `graphorin pricing missing` (Phase 15).\n *\n * The bundled snapshot is intentionally small — operators wanting the\n * full upstream catalogue should run `refreshPricing(...)` and\n * persist the result to disk.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.5.0';\n\nexport {\n DEFAULT_PRICING_AUTO_REFRESH,\n type PricingAutoRefreshConfig,\n type PricingConfig,\n} from './config.js';\nexport { diffPricing } from './diff.js';\nexport {\n _resetLookupWarningsForTesting,\n calculateCost,\n lookupPrice,\n setLookupWarnSink,\n} from './lookup.js';\nexport { listMissingModels, type MissingModelEntry } from './missing.js';\nexport { type RefreshPricingOptions, refreshPricing } from './refresh.js';\nexport { BUNDLED_SNAPSHOT, computeEntriesDigest, SNAPSHOT_DATE } from './snapshot/index.js';\nexport type {\n Cost,\n LookupPriceArgs,\n LookupPriceResult,\n ModelPrice,\n PricingDiffEntry,\n PricingSnapshot,\n PricingTraceSpanLike,\n} from './types.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,UAAU"}
|
package/dist/lookup.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { LookupPriceArgs, LookupPriceResult, PricingSnapshot } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lookup.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Optional sink for the deduplicated WARN emitted on unknown models.
|
|
7
|
+
* Defaults to `console.warn`. Override in tests.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
declare function setLookupWarnSink(sink: (line: string) => void): void;
|
|
12
|
+
/**
|
|
13
|
+
* @internal — exposed for tests so the WARN-once cache can be reset.
|
|
14
|
+
*/
|
|
15
|
+
declare function _resetLookupWarningsForTesting(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a per-token price for the (provider, model) pair. Returns
|
|
18
|
+
* `null` when the snapshot does not contain an entry for the model.
|
|
19
|
+
*
|
|
20
|
+
* The function emits one WARN per process per unknown (provider, model)
|
|
21
|
+
* pair so cost dashboards surface drift without spamming the log.
|
|
22
|
+
*
|
|
23
|
+
* @stable
|
|
24
|
+
*/
|
|
25
|
+
declare function lookupPrice(args: LookupPriceArgs, snapshot?: PricingSnapshot): LookupPriceResult | null;
|
|
26
|
+
/**
|
|
27
|
+
* Multiply a per-token price by an integer token count. Returns `null`
|
|
28
|
+
* when the price is unknown. Useful when caller wants to compute cost
|
|
29
|
+
* for a single LLM call without instantiating the cost tracker.
|
|
30
|
+
*
|
|
31
|
+
* Token-count contract (PS-19):
|
|
32
|
+
* - `inputTokens` **excludes** `cachedReadTokens` — cached reads are billed
|
|
33
|
+
* separately at the cheaper cached rate, so pass the non-cached prompt count
|
|
34
|
+
* to avoid double-billing.
|
|
35
|
+
* - `reasoningTokens` are billed at `outputUsdPerToken` unless the model entry
|
|
36
|
+
* declares an explicit `reasoningUsdPerToken`.
|
|
37
|
+
*
|
|
38
|
+
* @stable
|
|
39
|
+
*/
|
|
40
|
+
declare function calculateCost(args: LookupPriceArgs & {
|
|
41
|
+
/** Non-cached prompt tokens (excludes `cachedReadTokens`). */
|
|
42
|
+
readonly inputTokens: number;
|
|
43
|
+
readonly outputTokens: number;
|
|
44
|
+
readonly cachedReadTokens?: number;
|
|
45
|
+
readonly reasoningTokens?: number;
|
|
46
|
+
}, snapshot?: PricingSnapshot): {
|
|
47
|
+
readonly amount: number;
|
|
48
|
+
readonly currency: 'USD';
|
|
49
|
+
} | null;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { _resetLookupWarningsForTesting, calculateCost, lookupPrice, setLookupWarnSink };
|
|
52
|
+
//# sourceMappingURL=lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.d.ts","names":[],"sources":["../src/lookup.ts"],"sourcesContent":[],"mappings":";;;;;AAyGA;;;;;iBArFgB,iBAAA;;;;iBASA,8BAAA,CAAA;;;;;;;;;;iBAaA,WAAA,OACR,4BACI,kBACT;;;;;;;;;;;;;;;iBA4Da,aAAA,OACR;;;;;;cAOI"}
|
package/dist/lookup.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { BUNDLED_SNAPSHOT } from "./snapshot/bundled.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lookup.ts
|
|
4
|
+
/**
|
|
5
|
+
* `lookupPrice(...)` — resolve a per-token price for a given
|
|
6
|
+
* (provider, model) pair against a snapshot. Returns `null` when the
|
|
7
|
+
* model is unknown and emits one WARN per process-lifetime per
|
|
8
|
+
* unknown (provider, model) pair.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
const WARNED = /* @__PURE__ */ new Set();
|
|
13
|
+
/**
|
|
14
|
+
* Optional sink for the deduplicated WARN emitted on unknown models.
|
|
15
|
+
* Defaults to `console.warn`. Override in tests.
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
function setLookupWarnSink(sink) {
|
|
20
|
+
WARN_SINK = sink;
|
|
21
|
+
}
|
|
22
|
+
let WARN_SINK = (line) => console.warn(line);
|
|
23
|
+
/**
|
|
24
|
+
* @internal — exposed for tests so the WARN-once cache can be reset.
|
|
25
|
+
*/
|
|
26
|
+
function _resetLookupWarningsForTesting() {
|
|
27
|
+
WARNED.clear();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a per-token price for the (provider, model) pair. Returns
|
|
31
|
+
* `null` when the snapshot does not contain an entry for the model.
|
|
32
|
+
*
|
|
33
|
+
* The function emits one WARN per process per unknown (provider, model)
|
|
34
|
+
* pair so cost dashboards surface drift without spamming the log.
|
|
35
|
+
*
|
|
36
|
+
* @stable
|
|
37
|
+
*/
|
|
38
|
+
function lookupPrice(args, snapshot = BUNDLED_SNAPSHOT) {
|
|
39
|
+
const exact = snapshot.entries.find((entry) => entry.provider === args.provider && entry.model === args.model && (args.region === void 0 || entry.region === void 0 || entry.region === args.region));
|
|
40
|
+
if (exact !== void 0) return entryToResult(exact, snapshot);
|
|
41
|
+
const wildcard = snapshot.entries.find((entry) => entry.provider === args.provider && entry.model === "*");
|
|
42
|
+
if (wildcard !== void 0) return entryToResult(wildcard, snapshot);
|
|
43
|
+
warnOnce(args);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function entryToResult(entry, snapshot) {
|
|
47
|
+
return {
|
|
48
|
+
inputUsdPerToken: entry.inputUsdPerToken,
|
|
49
|
+
outputUsdPerToken: entry.outputUsdPerToken,
|
|
50
|
+
...entry.cachedReadUsdPerToken === void 0 ? {} : { cachedReadUsdPerToken: entry.cachedReadUsdPerToken },
|
|
51
|
+
...entry.reasoningUsdPerToken === void 0 ? {} : { reasoningUsdPerToken: entry.reasoningUsdPerToken },
|
|
52
|
+
source: snapshot.source,
|
|
53
|
+
snapshotDate: snapshot.snapshotDate
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function warnOnce(args) {
|
|
57
|
+
const key = `${args.provider}/${args.model}`;
|
|
58
|
+
if (WARNED.has(key)) return;
|
|
59
|
+
WARNED.add(key);
|
|
60
|
+
WARN_SINK(`[graphorin/pricing] WARN: no pricing entry for ${args.provider}/${args.model}; cost is null. Consider running \`graphorin pricing refresh\` or contributing the entry upstream.`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Multiply a per-token price by an integer token count. Returns `null`
|
|
64
|
+
* when the price is unknown. Useful when caller wants to compute cost
|
|
65
|
+
* for a single LLM call without instantiating the cost tracker.
|
|
66
|
+
*
|
|
67
|
+
* Token-count contract (PS-19):
|
|
68
|
+
* - `inputTokens` **excludes** `cachedReadTokens` — cached reads are billed
|
|
69
|
+
* separately at the cheaper cached rate, so pass the non-cached prompt count
|
|
70
|
+
* to avoid double-billing.
|
|
71
|
+
* - `reasoningTokens` are billed at `outputUsdPerToken` unless the model entry
|
|
72
|
+
* declares an explicit `reasoningUsdPerToken`.
|
|
73
|
+
*
|
|
74
|
+
* @stable
|
|
75
|
+
*/
|
|
76
|
+
function calculateCost(args, snapshot = BUNDLED_SNAPSHOT) {
|
|
77
|
+
const price = lookupPrice(args, snapshot);
|
|
78
|
+
if (price === null) return null;
|
|
79
|
+
let amount = 0;
|
|
80
|
+
amount += price.inputUsdPerToken * args.inputTokens;
|
|
81
|
+
amount += price.outputUsdPerToken * args.outputTokens;
|
|
82
|
+
if (args.cachedReadTokens !== void 0 && price.cachedReadUsdPerToken !== void 0) amount += price.cachedReadUsdPerToken * args.cachedReadTokens;
|
|
83
|
+
if (args.reasoningTokens !== void 0) amount += (price.reasoningUsdPerToken ?? price.outputUsdPerToken) * args.reasoningTokens;
|
|
84
|
+
return {
|
|
85
|
+
amount,
|
|
86
|
+
currency: "USD"
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { _resetLookupWarningsForTesting, calculateCost, lookupPrice, setLookupWarnSink };
|
|
92
|
+
//# sourceMappingURL=lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.js","names":["WARN_SINK: (line: string) => void"],"sources":["../src/lookup.ts"],"sourcesContent":["/**\n * `lookupPrice(...)` — resolve a per-token price for a given\n * (provider, model) pair against a snapshot. Returns `null` when the\n * model is unknown and emits one WARN per process-lifetime per\n * unknown (provider, model) pair.\n *\n * @packageDocumentation\n */\n\nimport { BUNDLED_SNAPSHOT } from './snapshot/bundled.js';\nimport type { LookupPriceArgs, LookupPriceResult, ModelPrice, PricingSnapshot } from './types.js';\n\nconst WARNED = new Set<string>();\n\n/**\n * Optional sink for the deduplicated WARN emitted on unknown models.\n * Defaults to `console.warn`. Override in tests.\n *\n * @internal\n */\nexport function setLookupWarnSink(sink: (line: string) => void): void {\n WARN_SINK = sink;\n}\n\nlet WARN_SINK: (line: string) => void = (line) => console.warn(line);\n\n/**\n * @internal — exposed for tests so the WARN-once cache can be reset.\n */\nexport function _resetLookupWarningsForTesting(): void {\n WARNED.clear();\n}\n\n/**\n * Resolve a per-token price for the (provider, model) pair. Returns\n * `null` when the snapshot does not contain an entry for the model.\n *\n * The function emits one WARN per process per unknown (provider, model)\n * pair so cost dashboards surface drift without spamming the log.\n *\n * @stable\n */\nexport function lookupPrice(\n args: LookupPriceArgs,\n snapshot: PricingSnapshot = BUNDLED_SNAPSHOT,\n): LookupPriceResult | null {\n const exact = snapshot.entries.find(\n (entry) =>\n entry.provider === args.provider &&\n entry.model === args.model &&\n (args.region === undefined || entry.region === undefined || entry.region === args.region),\n );\n\n if (exact !== undefined) return entryToResult(exact, snapshot);\n\n // Wildcard fallback — used by local providers (`provider: 'ollama', model: '*'`).\n const wildcard = snapshot.entries.find(\n (entry) => entry.provider === args.provider && entry.model === '*',\n );\n if (wildcard !== undefined) return entryToResult(wildcard, snapshot);\n\n warnOnce(args);\n return null;\n}\n\nfunction entryToResult(entry: ModelPrice, snapshot: PricingSnapshot): LookupPriceResult {\n return {\n inputUsdPerToken: entry.inputUsdPerToken,\n outputUsdPerToken: entry.outputUsdPerToken,\n ...(entry.cachedReadUsdPerToken === undefined\n ? {}\n : { cachedReadUsdPerToken: entry.cachedReadUsdPerToken }),\n ...(entry.reasoningUsdPerToken === undefined\n ? {}\n : { reasoningUsdPerToken: entry.reasoningUsdPerToken }),\n source: snapshot.source,\n snapshotDate: snapshot.snapshotDate,\n };\n}\n\nfunction warnOnce(args: LookupPriceArgs): void {\n const key = `${args.provider}/${args.model}`;\n if (WARNED.has(key)) return;\n WARNED.add(key);\n WARN_SINK(\n `[graphorin/pricing] WARN: no pricing entry for ${args.provider}/${args.model}; ` +\n 'cost is null. Consider running `graphorin pricing refresh` or contributing the ' +\n 'entry upstream.',\n );\n}\n\n/**\n * Multiply a per-token price by an integer token count. Returns `null`\n * when the price is unknown. Useful when caller wants to compute cost\n * for a single LLM call without instantiating the cost tracker.\n *\n * Token-count contract (PS-19):\n * - `inputTokens` **excludes** `cachedReadTokens` — cached reads are billed\n * separately at the cheaper cached rate, so pass the non-cached prompt count\n * to avoid double-billing.\n * - `reasoningTokens` are billed at `outputUsdPerToken` unless the model entry\n * declares an explicit `reasoningUsdPerToken`.\n *\n * @stable\n */\nexport function calculateCost(\n args: LookupPriceArgs & {\n /** Non-cached prompt tokens (excludes `cachedReadTokens`). */\n readonly inputTokens: number;\n readonly outputTokens: number;\n readonly cachedReadTokens?: number;\n readonly reasoningTokens?: number;\n },\n snapshot: PricingSnapshot = BUNDLED_SNAPSHOT,\n): { readonly amount: number; readonly currency: 'USD' } | null {\n const price = lookupPrice(args, snapshot);\n if (price === null) return null;\n let amount = 0;\n // `inputTokens` is the NON-cached prompt; cached reads are billed separately\n // at their own (cheaper) rate, so they must be excluded from `inputTokens` to\n // avoid double-counting (PS-19).\n amount += price.inputUsdPerToken * args.inputTokens;\n amount += price.outputUsdPerToken * args.outputTokens;\n if (args.cachedReadTokens !== undefined && price.cachedReadUsdPerToken !== undefined) {\n amount += price.cachedReadUsdPerToken * args.cachedReadTokens;\n }\n if (args.reasoningTokens !== undefined) {\n // PS-19: reasoning tokens follow completion (output) pricing unless the\n // entry declares an explicit `reasoningUsdPerToken` — the documented\n // contract that was previously billed at $0 for every bundled entry.\n amount += (price.reasoningUsdPerToken ?? price.outputUsdPerToken) * args.reasoningTokens;\n }\n return { amount, currency: 'USD' };\n}\n"],"mappings":";;;;;;;;;;;AAYA,MAAM,yBAAS,IAAI,KAAa;;;;;;;AAQhC,SAAgB,kBAAkB,MAAoC;AACpE,aAAY;;AAGd,IAAIA,aAAqC,SAAS,QAAQ,KAAK,KAAK;;;;AAKpE,SAAgB,iCAAuC;AACrD,QAAO,OAAO;;;;;;;;;;;AAYhB,SAAgB,YACd,MACA,WAA4B,kBACF;CAC1B,MAAM,QAAQ,SAAS,QAAQ,MAC5B,UACC,MAAM,aAAa,KAAK,YACxB,MAAM,UAAU,KAAK,UACpB,KAAK,WAAW,UAAa,MAAM,WAAW,UAAa,MAAM,WAAW,KAAK,QACrF;AAED,KAAI,UAAU,OAAW,QAAO,cAAc,OAAO,SAAS;CAG9D,MAAM,WAAW,SAAS,QAAQ,MAC/B,UAAU,MAAM,aAAa,KAAK,YAAY,MAAM,UAAU,IAChE;AACD,KAAI,aAAa,OAAW,QAAO,cAAc,UAAU,SAAS;AAEpE,UAAS,KAAK;AACd,QAAO;;AAGT,SAAS,cAAc,OAAmB,UAA8C;AACtF,QAAO;EACL,kBAAkB,MAAM;EACxB,mBAAmB,MAAM;EACzB,GAAI,MAAM,0BAA0B,SAChC,EAAE,GACF,EAAE,uBAAuB,MAAM,uBAAuB;EAC1D,GAAI,MAAM,yBAAyB,SAC/B,EAAE,GACF,EAAE,sBAAsB,MAAM,sBAAsB;EACxD,QAAQ,SAAS;EACjB,cAAc,SAAS;EACxB;;AAGH,SAAS,SAAS,MAA6B;CAC7C,MAAM,MAAM,GAAG,KAAK,SAAS,GAAG,KAAK;AACrC,KAAI,OAAO,IAAI,IAAI,CAAE;AACrB,QAAO,IAAI,IAAI;AACf,WACE,kDAAkD,KAAK,SAAS,GAAG,KAAK,MAAM,oGAG/E;;;;;;;;;;;;;;;;AAiBH,SAAgB,cACd,MAOA,WAA4B,kBACkC;CAC9D,MAAM,QAAQ,YAAY,MAAM,SAAS;AACzC,KAAI,UAAU,KAAM,QAAO;CAC3B,IAAI,SAAS;AAIb,WAAU,MAAM,mBAAmB,KAAK;AACxC,WAAU,MAAM,oBAAoB,KAAK;AACzC,KAAI,KAAK,qBAAqB,UAAa,MAAM,0BAA0B,OACzE,WAAU,MAAM,wBAAwB,KAAK;AAE/C,KAAI,KAAK,oBAAoB,OAI3B,YAAW,MAAM,wBAAwB,MAAM,qBAAqB,KAAK;AAE3E,QAAO;EAAE;EAAQ,UAAU;EAAO"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PricingSnapshot, PricingTraceSpanLike } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/missing.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single missing-model row.
|
|
7
|
+
*
|
|
8
|
+
* @stable
|
|
9
|
+
*/
|
|
10
|
+
interface MissingModelEntry {
|
|
11
|
+
readonly provider: string;
|
|
12
|
+
readonly model: string;
|
|
13
|
+
readonly count: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Return one entry per (provider, model) pair that the snapshot does
|
|
17
|
+
* not recognise, sorted by descending occurrence count.
|
|
18
|
+
*
|
|
19
|
+
* @stable
|
|
20
|
+
*/
|
|
21
|
+
declare function listMissingModels(spans: Iterable<PricingTraceSpanLike>, snapshot?: PricingSnapshot): ReadonlyArray<MissingModelEntry>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { MissingModelEntry, listMissingModels };
|
|
24
|
+
//# sourceMappingURL=missing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing.d.ts","names":[],"sources":["../src/missing.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAsBiB,iBAAA;;;;;;;;;;;iBAYD,iBAAA,QACP,SAAS,kCACN,kBACT,cAAc"}
|
package/dist/missing.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BUNDLED_SNAPSHOT } from "./snapshot/bundled.js";
|
|
2
|
+
import { lookupPrice } from "./lookup.js";
|
|
3
|
+
|
|
4
|
+
//#region src/missing.ts
|
|
5
|
+
/**
|
|
6
|
+
* `listMissingModels(...)` — scan a sequence of trace spans and return
|
|
7
|
+
* the unique (provider, model) pairs that the snapshot does not
|
|
8
|
+
* recognise.
|
|
9
|
+
*
|
|
10
|
+
* The function reads `gen_ai.system` + `gen_ai.request.model` (the
|
|
11
|
+
* canonical OpenTelemetry GenAI attributes) and falls back to the
|
|
12
|
+
* Graphorin-prefixed `graphorin.provider.id` /
|
|
13
|
+
* `graphorin.provider.model` pair when `gen_ai.*` is absent.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Return one entry per (provider, model) pair that the snapshot does
|
|
19
|
+
* not recognise, sorted by descending occurrence count.
|
|
20
|
+
*
|
|
21
|
+
* @stable
|
|
22
|
+
*/
|
|
23
|
+
function listMissingModels(spans, snapshot = BUNDLED_SNAPSHOT) {
|
|
24
|
+
const seen = /* @__PURE__ */ new Map();
|
|
25
|
+
for (const span of spans) {
|
|
26
|
+
const provider = stringAttr(span, ["gen_ai.system", "graphorin.provider.id"]);
|
|
27
|
+
const model = stringAttr(span, [
|
|
28
|
+
"gen_ai.request.model",
|
|
29
|
+
"gen_ai.response.model",
|
|
30
|
+
"graphorin.provider.model"
|
|
31
|
+
]);
|
|
32
|
+
if (provider === null || model === null) continue;
|
|
33
|
+
if (lookupPrice({
|
|
34
|
+
provider,
|
|
35
|
+
model
|
|
36
|
+
}, snapshot) !== null) continue;
|
|
37
|
+
const key = `${provider}/${model}`;
|
|
38
|
+
const existing = seen.get(key);
|
|
39
|
+
if (existing === void 0) seen.set(key, {
|
|
40
|
+
provider,
|
|
41
|
+
model,
|
|
42
|
+
count: 1
|
|
43
|
+
});
|
|
44
|
+
else seen.set(key, {
|
|
45
|
+
provider,
|
|
46
|
+
model,
|
|
47
|
+
count: existing.count + 1
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return [...seen.values()].sort((a, b) => b.count - a.count);
|
|
51
|
+
}
|
|
52
|
+
function stringAttr(span, keys) {
|
|
53
|
+
for (const key of keys) {
|
|
54
|
+
const value = span.attributes[key];
|
|
55
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { listMissingModels };
|
|
62
|
+
//# sourceMappingURL=missing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing.js","names":[],"sources":["../src/missing.ts"],"sourcesContent":["/**\n * `listMissingModels(...)` — scan a sequence of trace spans and return\n * the unique (provider, model) pairs that the snapshot does not\n * recognise.\n *\n * The function reads `gen_ai.system` + `gen_ai.request.model` (the\n * canonical OpenTelemetry GenAI attributes) and falls back to the\n * Graphorin-prefixed `graphorin.provider.id` /\n * `graphorin.provider.model` pair when `gen_ai.*` is absent.\n *\n * @packageDocumentation\n */\n\nimport { lookupPrice } from './lookup.js';\nimport { BUNDLED_SNAPSHOT } from './snapshot/bundled.js';\nimport type { PricingSnapshot, PricingTraceSpanLike } from './types.js';\n\n/**\n * Single missing-model row.\n *\n * @stable\n */\nexport interface MissingModelEntry {\n readonly provider: string;\n readonly model: string;\n readonly count: number;\n}\n\n/**\n * Return one entry per (provider, model) pair that the snapshot does\n * not recognise, sorted by descending occurrence count.\n *\n * @stable\n */\nexport function listMissingModels(\n spans: Iterable<PricingTraceSpanLike>,\n snapshot: PricingSnapshot = BUNDLED_SNAPSHOT,\n): ReadonlyArray<MissingModelEntry> {\n const seen = new Map<string, MissingModelEntry & { count: number }>();\n for (const span of spans) {\n const provider = stringAttr(span, ['gen_ai.system', 'graphorin.provider.id']);\n const model = stringAttr(span, [\n 'gen_ai.request.model',\n 'gen_ai.response.model',\n 'graphorin.provider.model',\n ]);\n if (provider === null || model === null) continue;\n if (lookupPrice({ provider, model }, snapshot) !== null) continue;\n const key = `${provider}/${model}`;\n const existing = seen.get(key);\n if (existing === undefined) {\n seen.set(key, { provider, model, count: 1 });\n } else {\n seen.set(key, { provider, model, count: existing.count + 1 });\n }\n }\n return [...seen.values()].sort((a, b) => b.count - a.count);\n}\n\nfunction stringAttr(span: PricingTraceSpanLike, keys: ReadonlyArray<string>): string | null {\n for (const key of keys) {\n const value = span.attributes[key];\n if (typeof value === 'string' && value.length > 0) return value;\n }\n return null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,kBACd,OACA,WAA4B,kBACM;CAClC,MAAM,uBAAO,IAAI,KAAoD;AACrE,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,WAAW,WAAW,MAAM,CAAC,iBAAiB,wBAAwB,CAAC;EAC7E,MAAM,QAAQ,WAAW,MAAM;GAC7B;GACA;GACA;GACD,CAAC;AACF,MAAI,aAAa,QAAQ,UAAU,KAAM;AACzC,MAAI,YAAY;GAAE;GAAU;GAAO,EAAE,SAAS,KAAK,KAAM;EACzD,MAAM,MAAM,GAAG,SAAS,GAAG;EAC3B,MAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,MAAI,aAAa,OACf,MAAK,IAAI,KAAK;GAAE;GAAU;GAAO,OAAO;GAAG,CAAC;MAE5C,MAAK,IAAI,KAAK;GAAE;GAAU;GAAO,OAAO,SAAS,QAAQ;GAAG,CAAC;;AAGjE,QAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;;AAG7D,SAAS,WAAW,MAA4B,MAA4C;AAC1F,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,KAAK,WAAW;AAC9B,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;;AAE5D,QAAO"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PricingSnapshot } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/refresh.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration shape for {@link refreshPricing}. The `fetchImpl`
|
|
7
|
+
* override exists so tests can exercise the function without making
|
|
8
|
+
* a real network call.
|
|
9
|
+
*
|
|
10
|
+
* @stable
|
|
11
|
+
*/
|
|
12
|
+
interface RefreshPricingOptions {
|
|
13
|
+
/** Snapshot URL — typically the upstream pricing JSON. */
|
|
14
|
+
readonly url: string;
|
|
15
|
+
/** Optional headers (auth, conditional GET, etc.). */
|
|
16
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
17
|
+
/** Optional fetch override — useful in tests. */
|
|
18
|
+
readonly fetchImpl?: typeof fetch;
|
|
19
|
+
/** Override the snapshot date stamped on the result. Defaults to today. */
|
|
20
|
+
readonly snapshotDate?: string;
|
|
21
|
+
/** Override the snapshot version string. Defaults to `'graphorin/0.1+refreshed'`. */
|
|
22
|
+
readonly version?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Hard timeout for the network fetch in milliseconds. Default
|
|
25
|
+
* `30000`. Aborts the request (and throws) if the upstream is slow
|
|
26
|
+
* or unreachable so `graphorin pricing refresh` cannot hang. Pass an
|
|
27
|
+
* explicit {@link RefreshPricingOptions.signal} to manage
|
|
28
|
+
* cancellation yourself; the two are combined.
|
|
29
|
+
*/
|
|
30
|
+
readonly timeoutMs?: number;
|
|
31
|
+
/** Caller-supplied abort signal, combined with the timeout. */
|
|
32
|
+
readonly signal?: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pull a fresh snapshot from the supplied URL and return it. Network
|
|
36
|
+
* failures and shape mismatches surface as thrown errors so the CLI
|
|
37
|
+
* can surface them to the operator.
|
|
38
|
+
*
|
|
39
|
+
* @stable
|
|
40
|
+
*/
|
|
41
|
+
declare function refreshPricing(opts: RefreshPricingOptions): Promise<PricingSnapshot>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { RefreshPricingOptions, refreshPricing };
|
|
44
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","names":[],"sources":["../src/refresh.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;UAwBiB,qBAAA;;;;qBAII,SAAS;;8BAEA;;;;;;;;;;;;;;oBAcV;;;;;;;;;iBAUE,cAAA,OAAqB,wBAAwB,QAAQ"}
|
package/dist/refresh.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { computeEntriesDigest } from "./snapshot/bundled.js";
|
|
2
|
+
|
|
3
|
+
//#region src/refresh.ts
|
|
4
|
+
/**
|
|
5
|
+
* `refreshPricing(...)` — opt-in network call. The framework never
|
|
6
|
+
* invokes this automatically; it exists so the CLI (`graphorin
|
|
7
|
+
* pricing refresh`) and operator scripts can pull a fresh snapshot
|
|
8
|
+
* without re-installing the package.
|
|
9
|
+
*
|
|
10
|
+
* The function fetches the supplied URL as JSON and validates the
|
|
11
|
+
* shape against the {@link PricingSnapshot} contract. The bundled
|
|
12
|
+
* snapshot is **not** mutated; callers persist the refreshed
|
|
13
|
+
* snapshot to disk and reload it explicitly.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Pull a fresh snapshot from the supplied URL and return it. Network
|
|
19
|
+
* failures and shape mismatches surface as thrown errors so the CLI
|
|
20
|
+
* can surface them to the operator.
|
|
21
|
+
*
|
|
22
|
+
* @stable
|
|
23
|
+
*/
|
|
24
|
+
async function refreshPricing(opts) {
|
|
25
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
26
|
+
if (typeof fetchImpl !== "function") throw new TypeError("refreshPricing: no fetch implementation available. Provide `fetchImpl` explicitly or run on a Node.js version with global fetch (>= 18).");
|
|
27
|
+
const timeoutMs = opts.timeoutMs ?? 3e4;
|
|
28
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
29
|
+
const signal = opts.signal === void 0 ? timeoutSignal : AbortSignal.any([opts.signal, timeoutSignal]);
|
|
30
|
+
let res;
|
|
31
|
+
try {
|
|
32
|
+
res = await fetchImpl(opts.url, {
|
|
33
|
+
headers: opts.headers ?? {},
|
|
34
|
+
signal
|
|
35
|
+
});
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (timeoutSignal.aborted) throw new Error(`refreshPricing: timed out after ${timeoutMs} ms fetching ${opts.url}`, { cause: err });
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
if (!res.ok) throw new Error(`refreshPricing: HTTP ${res.status} ${res.statusText} from ${opts.url}`);
|
|
41
|
+
const entries = parseEntries(await res.json());
|
|
42
|
+
const snapshotDate = opts.snapshotDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
43
|
+
const version = opts.version ?? "graphorin/0.1+refreshed";
|
|
44
|
+
return Object.freeze({
|
|
45
|
+
version,
|
|
46
|
+
source: opts.url,
|
|
47
|
+
snapshotDate,
|
|
48
|
+
currency: "USD",
|
|
49
|
+
sha256: computeEntriesDigest(entries),
|
|
50
|
+
entries
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function parseEntries(body) {
|
|
54
|
+
if (body === null || typeof body !== "object") throw new Error("refreshPricing: response body is not an object");
|
|
55
|
+
let raw;
|
|
56
|
+
if (Array.isArray(body)) raw = body;
|
|
57
|
+
else {
|
|
58
|
+
raw = body.entries;
|
|
59
|
+
if (!Array.isArray(raw)) throw new Error("refreshPricing: response object is missing the `entries` array");
|
|
60
|
+
}
|
|
61
|
+
const entries = raw.map(parseEntry);
|
|
62
|
+
return Object.freeze(entries);
|
|
63
|
+
}
|
|
64
|
+
function parseEntry(raw) {
|
|
65
|
+
if (raw === null || typeof raw !== "object") throw new Error("refreshPricing: pricing entry is not an object");
|
|
66
|
+
const e = raw;
|
|
67
|
+
if (typeof e.provider !== "string" || typeof e.model !== "string") throw new Error("refreshPricing: pricing entry is missing provider / model");
|
|
68
|
+
if (typeof e.inputUsdPerToken !== "number" || typeof e.outputUsdPerToken !== "number") throw new Error(`refreshPricing: pricing entry ${e.provider}/${e.model} is missing input / output prices`);
|
|
69
|
+
return Object.freeze({
|
|
70
|
+
provider: e.provider,
|
|
71
|
+
model: e.model,
|
|
72
|
+
inputUsdPerToken: e.inputUsdPerToken,
|
|
73
|
+
outputUsdPerToken: e.outputUsdPerToken,
|
|
74
|
+
...typeof e.cachedReadUsdPerToken === "number" ? { cachedReadUsdPerToken: e.cachedReadUsdPerToken } : {},
|
|
75
|
+
...typeof e.reasoningUsdPerToken === "number" ? { reasoningUsdPerToken: e.reasoningUsdPerToken } : {},
|
|
76
|
+
...typeof e.region === "string" ? { region: e.region } : {},
|
|
77
|
+
...typeof e.notes === "string" ? { notes: e.notes } : {}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//#endregion
|
|
82
|
+
export { refreshPricing };
|
|
83
|
+
//# sourceMappingURL=refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.js","names":["res: Awaited<ReturnType<typeof fetchImpl>>","raw: unknown"],"sources":["../src/refresh.ts"],"sourcesContent":["/**\n * `refreshPricing(...)` — opt-in network call. The framework never\n * invokes this automatically; it exists so the CLI (`graphorin\n * pricing refresh`) and operator scripts can pull a fresh snapshot\n * without re-installing the package.\n *\n * The function fetches the supplied URL as JSON and validates the\n * shape against the {@link PricingSnapshot} contract. The bundled\n * snapshot is **not** mutated; callers persist the refreshed\n * snapshot to disk and reload it explicitly.\n *\n * @packageDocumentation\n */\n\nimport { computeEntriesDigest } from './snapshot/bundled.js';\nimport type { ModelPrice, PricingSnapshot } from './types.js';\n\n/**\n * Configuration shape for {@link refreshPricing}. The `fetchImpl`\n * override exists so tests can exercise the function without making\n * a real network call.\n *\n * @stable\n */\nexport interface RefreshPricingOptions {\n /** Snapshot URL — typically the upstream pricing JSON. */\n readonly url: string;\n /** Optional headers (auth, conditional GET, etc.). */\n readonly headers?: Readonly<Record<string, string>>;\n /** Optional fetch override — useful in tests. */\n readonly fetchImpl?: typeof fetch;\n /** Override the snapshot date stamped on the result. Defaults to today. */\n readonly snapshotDate?: string;\n /** Override the snapshot version string. Defaults to `'graphorin/0.1+refreshed'`. */\n readonly version?: string;\n /**\n * Hard timeout for the network fetch in milliseconds. Default\n * `30000`. Aborts the request (and throws) if the upstream is slow\n * or unreachable so `graphorin pricing refresh` cannot hang. Pass an\n * explicit {@link RefreshPricingOptions.signal} to manage\n * cancellation yourself; the two are combined.\n */\n readonly timeoutMs?: number;\n /** Caller-supplied abort signal, combined with the timeout. */\n readonly signal?: AbortSignal;\n}\n\n/**\n * Pull a fresh snapshot from the supplied URL and return it. Network\n * failures and shape mismatches surface as thrown errors so the CLI\n * can surface them to the operator.\n *\n * @stable\n */\nexport async function refreshPricing(opts: RefreshPricingOptions): Promise<PricingSnapshot> {\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n if (typeof fetchImpl !== 'function') {\n throw new TypeError(\n 'refreshPricing: no fetch implementation available. Provide `fetchImpl` ' +\n 'explicitly or run on a Node.js version with global fetch (>= 18).',\n );\n }\n const timeoutMs = opts.timeoutMs ?? 30_000;\n const timeoutSignal = AbortSignal.timeout(timeoutMs);\n const signal =\n opts.signal === undefined ? timeoutSignal : AbortSignal.any([opts.signal, timeoutSignal]);\n let res: Awaited<ReturnType<typeof fetchImpl>>;\n try {\n res = await fetchImpl(opts.url, { headers: opts.headers ?? {}, signal });\n } catch (err) {\n if (timeoutSignal.aborted) {\n throw new Error(`refreshPricing: timed out after ${timeoutMs} ms fetching ${opts.url}`, {\n cause: err,\n });\n }\n throw err;\n }\n if (!res.ok) {\n throw new Error(`refreshPricing: HTTP ${res.status} ${res.statusText} from ${opts.url}`);\n }\n const body = (await res.json()) as unknown;\n const entries = parseEntries(body);\n const snapshotDate = opts.snapshotDate ?? new Date().toISOString().slice(0, 10);\n const version = opts.version ?? 'graphorin/0.1+refreshed';\n return Object.freeze<PricingSnapshot>({\n version,\n source: opts.url,\n snapshotDate,\n currency: 'USD',\n sha256: computeEntriesDigest(entries),\n entries,\n });\n}\n\nfunction parseEntries(body: unknown): ReadonlyArray<ModelPrice> {\n if (body === null || typeof body !== 'object') {\n throw new Error('refreshPricing: response body is not an object');\n }\n // Two accepted shapes:\n // - `{ entries: ModelPrice[] }`\n // - `ModelPrice[]`\n let raw: unknown;\n if (Array.isArray(body)) {\n raw = body;\n } else {\n raw = (body as { entries?: unknown }).entries;\n if (!Array.isArray(raw)) {\n throw new Error('refreshPricing: response object is missing the `entries` array');\n }\n }\n const entries = (raw as ReadonlyArray<unknown>).map(parseEntry);\n return Object.freeze(entries);\n}\n\nfunction parseEntry(raw: unknown): ModelPrice {\n if (raw === null || typeof raw !== 'object') {\n throw new Error('refreshPricing: pricing entry is not an object');\n }\n const e = raw as Partial<ModelPrice>;\n if (typeof e.provider !== 'string' || typeof e.model !== 'string') {\n throw new Error('refreshPricing: pricing entry is missing provider / model');\n }\n if (typeof e.inputUsdPerToken !== 'number' || typeof e.outputUsdPerToken !== 'number') {\n throw new Error(\n `refreshPricing: pricing entry ${e.provider}/${e.model} is missing input / output prices`,\n );\n }\n return Object.freeze({\n provider: e.provider,\n model: e.model,\n inputUsdPerToken: e.inputUsdPerToken,\n outputUsdPerToken: e.outputUsdPerToken,\n ...(typeof e.cachedReadUsdPerToken === 'number'\n ? { cachedReadUsdPerToken: e.cachedReadUsdPerToken }\n : {}),\n ...(typeof e.reasoningUsdPerToken === 'number'\n ? { reasoningUsdPerToken: e.reasoningUsdPerToken }\n : {}),\n ...(typeof e.region === 'string' ? { region: e.region } : {}),\n ...(typeof e.notes === 'string' ? { notes: e.notes } : {}),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsDA,eAAsB,eAAe,MAAuD;CAC1F,MAAM,YAAY,KAAK,aAAa,WAAW;AAC/C,KAAI,OAAO,cAAc,WACvB,OAAM,IAAI,UACR,2IAED;CAEH,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,gBAAgB,YAAY,QAAQ,UAAU;CACpD,MAAM,SACJ,KAAK,WAAW,SAAY,gBAAgB,YAAY,IAAI,CAAC,KAAK,QAAQ,cAAc,CAAC;CAC3F,IAAIA;AACJ,KAAI;AACF,QAAM,MAAM,UAAU,KAAK,KAAK;GAAE,SAAS,KAAK,WAAW,EAAE;GAAE;GAAQ,CAAC;UACjE,KAAK;AACZ,MAAI,cAAc,QAChB,OAAM,IAAI,MAAM,mCAAmC,UAAU,eAAe,KAAK,OAAO,EACtF,OAAO,KACR,CAAC;AAEJ,QAAM;;AAER,KAAI,CAAC,IAAI,GACP,OAAM,IAAI,MAAM,wBAAwB,IAAI,OAAO,GAAG,IAAI,WAAW,QAAQ,KAAK,MAAM;CAG1F,MAAM,UAAU,aADF,MAAM,IAAI,MAAM,CACI;CAClC,MAAM,eAAe,KAAK,iCAAgB,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;CAC/E,MAAM,UAAU,KAAK,WAAW;AAChC,QAAO,OAAO,OAAwB;EACpC;EACA,QAAQ,KAAK;EACb;EACA,UAAU;EACV,QAAQ,qBAAqB,QAAQ;EACrC;EACD,CAAC;;AAGJ,SAAS,aAAa,MAA0C;AAC9D,KAAI,SAAS,QAAQ,OAAO,SAAS,SACnC,OAAM,IAAI,MAAM,iDAAiD;CAKnE,IAAIC;AACJ,KAAI,MAAM,QAAQ,KAAK,CACrB,OAAM;MACD;AACL,QAAO,KAA+B;AACtC,MAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,iEAAiE;;CAGrF,MAAM,UAAW,IAA+B,IAAI,WAAW;AAC/D,QAAO,OAAO,OAAO,QAAQ;;AAG/B,SAAS,WAAW,KAA0B;AAC5C,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,iDAAiD;CAEnE,MAAM,IAAI;AACV,KAAI,OAAO,EAAE,aAAa,YAAY,OAAO,EAAE,UAAU,SACvD,OAAM,IAAI,MAAM,4DAA4D;AAE9E,KAAI,OAAO,EAAE,qBAAqB,YAAY,OAAO,EAAE,sBAAsB,SAC3E,OAAM,IAAI,MACR,iCAAiC,EAAE,SAAS,GAAG,EAAE,MAAM,mCACxD;AAEH,QAAO,OAAO,OAAO;EACnB,UAAU,EAAE;EACZ,OAAO,EAAE;EACT,kBAAkB,EAAE;EACpB,mBAAmB,EAAE;EACrB,GAAI,OAAO,EAAE,0BAA0B,WACnC,EAAE,uBAAuB,EAAE,uBAAuB,GAClD,EAAE;EACN,GAAI,OAAO,EAAE,yBAAyB,WAClC,EAAE,sBAAsB,EAAE,sBAAsB,GAChD,EAAE;EACN,GAAI,OAAO,EAAE,WAAW,WAAW,EAAE,QAAQ,EAAE,QAAQ,GAAG,EAAE;EAC5D,GAAI,OAAO,EAAE,UAAU,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE;EAC1D,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ModelPrice, PricingSnapshot } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/snapshot/bundled.d.ts
|
|
4
|
+
|
|
5
|
+
/** @internal — used for `lookupPrice` defaults. */
|
|
6
|
+
declare const SNAPSHOT_DATE = "2026-04-30";
|
|
7
|
+
/**
|
|
8
|
+
* Compute a deterministic SHA-256 of the entries. Sorting the entry
|
|
9
|
+
* keys before serialisation keeps the digest stable across Node
|
|
10
|
+
* versions / object-property-iteration orderings.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
declare function computeEntriesDigest(entries: ReadonlyArray<ModelPrice>): string;
|
|
15
|
+
/**
|
|
16
|
+
* The bundled snapshot. The `sha256` digest is computed over the
|
|
17
|
+
* canonical JSON form of `entries` at module load time so consumers
|
|
18
|
+
* can verify integrity without trusting the package metadata.
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
declare const BUNDLED_SNAPSHOT: PricingSnapshot;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { BUNDLED_SNAPSHOT, SNAPSHOT_DATE, computeEntriesDigest };
|
|
25
|
+
//# sourceMappingURL=bundled.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled.d.ts","names":[],"sources":["../../src/snapshot/bundled.ts"],"sourcesContent":[],"mappings":";;;;;cAkBa,aAAA;;;;;;;;iBAoIG,oBAAA,UAA8B,cAAc;;;;;;;;cAsB/C,kBAAkB"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/snapshot/bundled.ts
|
|
4
|
+
/**
|
|
5
|
+
* Bundled pricing snapshot. The catalogue is intentionally small —
|
|
6
|
+
* operators wanting the full upstream catalogue should run the opt-in
|
|
7
|
+
* `refreshPricing(...)` flow at build time and persist the result to
|
|
8
|
+
* a custom snapshot file.
|
|
9
|
+
*
|
|
10
|
+
* Prices are stored in **USD per token** (i.e. divide the per-million
|
|
11
|
+
* upstream prices by 1_000_000). Numbers are accurate to the snapshot
|
|
12
|
+
* date; consumers are expected to refresh on a regular cadence.
|
|
13
|
+
*
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
/** @internal — used for `lookupPrice` defaults. */
|
|
17
|
+
const SNAPSHOT_DATE = "2026-04-30";
|
|
18
|
+
const ENTRIES = Object.freeze([
|
|
19
|
+
{
|
|
20
|
+
provider: "anthropic",
|
|
21
|
+
model: "claude-3-5-sonnet-20241022",
|
|
22
|
+
inputUsdPerToken: 3 / 1e6,
|
|
23
|
+
outputUsdPerToken: 15 / 1e6,
|
|
24
|
+
cachedReadUsdPerToken: .3 / 1e6
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
provider: "anthropic",
|
|
28
|
+
model: "claude-3-5-haiku-20241022",
|
|
29
|
+
inputUsdPerToken: .8 / 1e6,
|
|
30
|
+
outputUsdPerToken: 4 / 1e6,
|
|
31
|
+
cachedReadUsdPerToken: .08 / 1e6
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
provider: "anthropic",
|
|
35
|
+
model: "claude-3-opus-20240229",
|
|
36
|
+
inputUsdPerToken: 15 / 1e6,
|
|
37
|
+
outputUsdPerToken: 75 / 1e6,
|
|
38
|
+
cachedReadUsdPerToken: 1.5 / 1e6
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
provider: "openai",
|
|
42
|
+
model: "gpt-4o-2024-11-20",
|
|
43
|
+
inputUsdPerToken: 2.5 / 1e6,
|
|
44
|
+
outputUsdPerToken: 10 / 1e6,
|
|
45
|
+
cachedReadUsdPerToken: 1.25 / 1e6
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
provider: "openai",
|
|
49
|
+
model: "gpt-4o-mini-2024-07-18",
|
|
50
|
+
inputUsdPerToken: .15 / 1e6,
|
|
51
|
+
outputUsdPerToken: .6 / 1e6,
|
|
52
|
+
cachedReadUsdPerToken: .075 / 1e6
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
provider: "openai",
|
|
56
|
+
model: "o1-2024-12-17",
|
|
57
|
+
inputUsdPerToken: 15 / 1e6,
|
|
58
|
+
outputUsdPerToken: 60 / 1e6,
|
|
59
|
+
cachedReadUsdPerToken: 7.5 / 1e6
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
provider: "openai",
|
|
63
|
+
model: "o3-mini-2025-01-31",
|
|
64
|
+
inputUsdPerToken: 1.1 / 1e6,
|
|
65
|
+
outputUsdPerToken: 4.4 / 1e6,
|
|
66
|
+
cachedReadUsdPerToken: .55 / 1e6
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
provider: "google",
|
|
70
|
+
model: "gemini-1.5-pro-002",
|
|
71
|
+
inputUsdPerToken: 1.25 / 1e6,
|
|
72
|
+
outputUsdPerToken: 5 / 1e6,
|
|
73
|
+
cachedReadUsdPerToken: .3125 / 1e6
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
provider: "google",
|
|
77
|
+
model: "gemini-1.5-flash-002",
|
|
78
|
+
inputUsdPerToken: .075 / 1e6,
|
|
79
|
+
outputUsdPerToken: .3 / 1e6,
|
|
80
|
+
cachedReadUsdPerToken: .01875 / 1e6
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
provider: "mistral",
|
|
84
|
+
model: "mistral-large-2411",
|
|
85
|
+
inputUsdPerToken: 2 / 1e6,
|
|
86
|
+
outputUsdPerToken: 6 / 1e6
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
provider: "mistral",
|
|
90
|
+
model: "mistral-small-2503",
|
|
91
|
+
inputUsdPerToken: .2 / 1e6,
|
|
92
|
+
outputUsdPerToken: .6 / 1e6
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
provider: "cohere",
|
|
96
|
+
model: "command-r-plus-08-2024",
|
|
97
|
+
inputUsdPerToken: 2.5 / 1e6,
|
|
98
|
+
outputUsdPerToken: 10 / 1e6
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
provider: "cohere",
|
|
102
|
+
model: "command-r-08-2024",
|
|
103
|
+
inputUsdPerToken: .15 / 1e6,
|
|
104
|
+
outputUsdPerToken: .6 / 1e6
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
provider: "ollama",
|
|
108
|
+
model: "*",
|
|
109
|
+
inputUsdPerToken: 0,
|
|
110
|
+
outputUsdPerToken: 0,
|
|
111
|
+
notes: "Local Ollama deployment — operator-priced."
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
provider: "graphorin-llamacpp",
|
|
115
|
+
model: "*",
|
|
116
|
+
inputUsdPerToken: 0,
|
|
117
|
+
outputUsdPerToken: 0,
|
|
118
|
+
notes: "Local llama.cpp deployment — operator-priced."
|
|
119
|
+
}
|
|
120
|
+
]);
|
|
121
|
+
/**
|
|
122
|
+
* Compute a deterministic SHA-256 of the entries. Sorting the entry
|
|
123
|
+
* keys before serialisation keeps the digest stable across Node
|
|
124
|
+
* versions / object-property-iteration orderings.
|
|
125
|
+
*
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
function computeEntriesDigest(entries) {
|
|
129
|
+
const canonical = JSON.stringify(entries.map((entry) => sortKeys(entry)));
|
|
130
|
+
return createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
131
|
+
}
|
|
132
|
+
function sortKeys(value) {
|
|
133
|
+
if (value === null || typeof value !== "object") return value;
|
|
134
|
+
if (Array.isArray(value)) return value.map(sortKeys);
|
|
135
|
+
const out = {};
|
|
136
|
+
for (const key of Object.keys(value).sort()) out[key] = sortKeys(value[key]);
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The bundled snapshot. The `sha256` digest is computed over the
|
|
141
|
+
* canonical JSON form of `entries` at module load time so consumers
|
|
142
|
+
* can verify integrity without trusting the package metadata.
|
|
143
|
+
*
|
|
144
|
+
* @stable
|
|
145
|
+
*/
|
|
146
|
+
const BUNDLED_SNAPSHOT = Object.freeze({
|
|
147
|
+
version: "graphorin/0.1",
|
|
148
|
+
source: "https://github.com/o-stepper/graphorin/tree/main/packages/pricing/src/snapshot/bundled.ts",
|
|
149
|
+
snapshotDate: SNAPSHOT_DATE,
|
|
150
|
+
currency: "USD",
|
|
151
|
+
sha256: computeEntriesDigest(ENTRIES),
|
|
152
|
+
entries: ENTRIES
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
//#endregion
|
|
156
|
+
export { BUNDLED_SNAPSHOT, SNAPSHOT_DATE, computeEntriesDigest };
|
|
157
|
+
//# sourceMappingURL=bundled.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled.js","names":["ENTRIES: ReadonlyArray<ModelPrice>","out: Record<string, unknown>","BUNDLED_SNAPSHOT: PricingSnapshot"],"sources":["../../src/snapshot/bundled.ts"],"sourcesContent":["/**\n * Bundled pricing snapshot. The catalogue is intentionally small —\n * operators wanting the full upstream catalogue should run the opt-in\n * `refreshPricing(...)` flow at build time and persist the result to\n * a custom snapshot file.\n *\n * Prices are stored in **USD per token** (i.e. divide the per-million\n * upstream prices by 1_000_000). Numbers are accurate to the snapshot\n * date; consumers are expected to refresh on a regular cadence.\n *\n * @packageDocumentation\n */\n\nimport { createHash } from 'node:crypto';\n\nimport type { ModelPrice, PricingSnapshot } from '../types.js';\n\n/** @internal — used for `lookupPrice` defaults. */\nexport const SNAPSHOT_DATE = '2026-04-30';\n\nconst ENTRIES: ReadonlyArray<ModelPrice> = Object.freeze([\n // -----------------------------------------------------------------\n // Anthropic\n // -----------------------------------------------------------------\n {\n provider: 'anthropic',\n model: 'claude-3-5-sonnet-20241022',\n inputUsdPerToken: 3 / 1_000_000,\n outputUsdPerToken: 15 / 1_000_000,\n cachedReadUsdPerToken: 0.3 / 1_000_000,\n },\n {\n provider: 'anthropic',\n model: 'claude-3-5-haiku-20241022',\n inputUsdPerToken: 0.8 / 1_000_000,\n outputUsdPerToken: 4 / 1_000_000,\n cachedReadUsdPerToken: 0.08 / 1_000_000,\n },\n {\n provider: 'anthropic',\n model: 'claude-3-opus-20240229',\n inputUsdPerToken: 15 / 1_000_000,\n outputUsdPerToken: 75 / 1_000_000,\n cachedReadUsdPerToken: 1.5 / 1_000_000,\n },\n // -----------------------------------------------------------------\n // OpenAI\n // -----------------------------------------------------------------\n {\n provider: 'openai',\n model: 'gpt-4o-2024-11-20',\n inputUsdPerToken: 2.5 / 1_000_000,\n outputUsdPerToken: 10 / 1_000_000,\n cachedReadUsdPerToken: 1.25 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'gpt-4o-mini-2024-07-18',\n inputUsdPerToken: 0.15 / 1_000_000,\n outputUsdPerToken: 0.6 / 1_000_000,\n cachedReadUsdPerToken: 0.075 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'o1-2024-12-17',\n inputUsdPerToken: 15 / 1_000_000,\n outputUsdPerToken: 60 / 1_000_000,\n cachedReadUsdPerToken: 7.5 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'o3-mini-2025-01-31',\n inputUsdPerToken: 1.1 / 1_000_000,\n outputUsdPerToken: 4.4 / 1_000_000,\n cachedReadUsdPerToken: 0.55 / 1_000_000,\n },\n // -----------------------------------------------------------------\n // Google\n // -----------------------------------------------------------------\n {\n provider: 'google',\n model: 'gemini-1.5-pro-002',\n inputUsdPerToken: 1.25 / 1_000_000,\n outputUsdPerToken: 5 / 1_000_000,\n cachedReadUsdPerToken: 0.3125 / 1_000_000,\n },\n {\n provider: 'google',\n model: 'gemini-1.5-flash-002',\n inputUsdPerToken: 0.075 / 1_000_000,\n outputUsdPerToken: 0.3 / 1_000_000,\n cachedReadUsdPerToken: 0.01875 / 1_000_000,\n },\n // -----------------------------------------------------------------\n // Mistral\n // -----------------------------------------------------------------\n {\n provider: 'mistral',\n model: 'mistral-large-2411',\n inputUsdPerToken: 2 / 1_000_000,\n outputUsdPerToken: 6 / 1_000_000,\n },\n {\n provider: 'mistral',\n model: 'mistral-small-2503',\n inputUsdPerToken: 0.2 / 1_000_000,\n outputUsdPerToken: 0.6 / 1_000_000,\n },\n // -----------------------------------------------------------------\n // Cohere\n // -----------------------------------------------------------------\n {\n provider: 'cohere',\n model: 'command-r-plus-08-2024',\n inputUsdPerToken: 2.5 / 1_000_000,\n outputUsdPerToken: 10 / 1_000_000,\n },\n {\n provider: 'cohere',\n model: 'command-r-08-2024',\n inputUsdPerToken: 0.15 / 1_000_000,\n outputUsdPerToken: 0.6 / 1_000_000,\n },\n // -----------------------------------------------------------------\n // Local providers — documented as zero-cost so cost rollups still\n // include the call counts.\n // -----------------------------------------------------------------\n {\n provider: 'ollama',\n model: '*',\n inputUsdPerToken: 0,\n outputUsdPerToken: 0,\n notes: 'Local Ollama deployment — operator-priced.',\n },\n {\n provider: 'graphorin-llamacpp',\n model: '*',\n inputUsdPerToken: 0,\n outputUsdPerToken: 0,\n notes: 'Local llama.cpp deployment — operator-priced.',\n },\n] as const);\n\n/**\n * Compute a deterministic SHA-256 of the entries. Sorting the entry\n * keys before serialisation keeps the digest stable across Node\n * versions / object-property-iteration orderings.\n *\n * @internal\n */\nexport function computeEntriesDigest(entries: ReadonlyArray<ModelPrice>): string {\n const canonical = JSON.stringify(entries.map((entry) => sortKeys(entry as unknown)));\n return createHash('sha256').update(canonical, 'utf8').digest('hex');\n}\n\nfunction sortKeys(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map(sortKeys);\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>).sort()) {\n out[key] = sortKeys((value as Record<string, unknown>)[key]);\n }\n return out;\n}\n\n/**\n * The bundled snapshot. The `sha256` digest is computed over the\n * canonical JSON form of `entries` at module load time so consumers\n * can verify integrity without trusting the package metadata.\n *\n * @stable\n */\nexport const BUNDLED_SNAPSHOT: PricingSnapshot = Object.freeze({\n version: 'graphorin/0.1',\n source:\n 'https://github.com/o-stepper/graphorin/tree/main/packages/pricing/src/snapshot/bundled.ts',\n snapshotDate: SNAPSHOT_DATE,\n currency: 'USD',\n sha256: computeEntriesDigest(ENTRIES),\n entries: ENTRIES,\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,MAAa,gBAAgB;AAE7B,MAAMA,UAAqC,OAAO,OAAO;CAIvD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,KAAK;EACxB,uBAAuB,KAAM;EAC9B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAM;EACxB,mBAAmB,IAAI;EACvB,uBAAuB,MAAO;EAC/B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAK;EACvB,mBAAmB,KAAK;EACxB,uBAAuB,MAAM;EAC9B;CAID;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAM;EACxB,mBAAmB,KAAK;EACxB,uBAAuB,OAAO;EAC/B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAO;EACzB,mBAAmB,KAAM;EACzB,uBAAuB,OAAQ;EAChC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAK;EACvB,mBAAmB,KAAK;EACxB,uBAAuB,MAAM;EAC9B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAM;EACxB,mBAAmB,MAAM;EACzB,uBAAuB,MAAO;EAC/B;CAID;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,OAAO;EACzB,mBAAmB,IAAI;EACvB,uBAAuB,QAAS;EACjC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,OAAQ;EAC1B,mBAAmB,KAAM;EACzB,uBAAuB,SAAU;EAClC;CAID;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,IAAI;EACxB;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAM;EACxB,mBAAmB,KAAM;EAC1B;CAID;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAM;EACxB,mBAAmB,KAAK;EACzB;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAO;EACzB,mBAAmB,KAAM;EAC1B;CAKD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB;EAClB,mBAAmB;EACnB,OAAO;EACR;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB;EAClB,mBAAmB;EACnB,OAAO;EACR;CACF,CAAU;;;;;;;;AASX,SAAgB,qBAAqB,SAA4C;CAC/E,MAAM,YAAY,KAAK,UAAU,QAAQ,KAAK,UAAU,SAAS,MAAiB,CAAC,CAAC;AACpF,QAAO,WAAW,SAAS,CAAC,OAAO,WAAW,OAAO,CAAC,OAAO,MAAM;;AAGrE,SAAS,SAAS,OAAyB;AACzC,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,IAAI,SAAS;CACpD,MAAMC,MAA+B,EAAE;AACvC,MAAK,MAAM,OAAO,OAAO,KAAK,MAAiC,CAAC,MAAM,CACpE,KAAI,OAAO,SAAU,MAAkC,KAAK;AAE9D,QAAO;;;;;;;;;AAUT,MAAaC,mBAAoC,OAAO,OAAO;CAC7D,SAAS;CACT,QACE;CACF,cAAc;CACd,UAAU;CACV,QAAQ,qBAAqB,QAAQ;CACrC,SAAS;CACV,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Cost } from "@graphorin/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Per-model pricing entry. All amounts are in **USD per token** unless
|
|
7
|
+
* the snapshot declares an alternative currency. Reasoning tokens
|
|
8
|
+
* (when supported) follow the same pricing as completion tokens unless
|
|
9
|
+
* the entry declares an explicit `reasoningUsdPerToken`.
|
|
10
|
+
*
|
|
11
|
+
* @stable
|
|
12
|
+
*/
|
|
13
|
+
interface ModelPrice {
|
|
14
|
+
/** Lower-case provider id (e.g. `'anthropic'`, `'openai'`, `'ollama'`). */
|
|
15
|
+
readonly provider: string;
|
|
16
|
+
/** Lower-case model id, e.g. `'claude-3-5-sonnet-20241022'`. */
|
|
17
|
+
readonly model: string;
|
|
18
|
+
/** Price per input token, in USD. */
|
|
19
|
+
readonly inputUsdPerToken: number;
|
|
20
|
+
/** Price per output token, in USD. */
|
|
21
|
+
readonly outputUsdPerToken: number;
|
|
22
|
+
/** Optional cached-read price (Anthropic / OpenAI prompt caching). */
|
|
23
|
+
readonly cachedReadUsdPerToken?: number;
|
|
24
|
+
/** Optional reasoning-token price (OpenAI o1 / Gemini 2 thinking). */
|
|
25
|
+
readonly reasoningUsdPerToken?: number;
|
|
26
|
+
/** Optional region label (e.g. `'us-east-1'`). */
|
|
27
|
+
readonly region?: string;
|
|
28
|
+
/** Free-form notes for tooling / docs. */
|
|
29
|
+
readonly notes?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Single bundled snapshot.
|
|
33
|
+
*
|
|
34
|
+
* @stable
|
|
35
|
+
*/
|
|
36
|
+
interface PricingSnapshot {
|
|
37
|
+
readonly version: string;
|
|
38
|
+
readonly source: string;
|
|
39
|
+
readonly snapshotDate: string;
|
|
40
|
+
readonly currency: 'USD';
|
|
41
|
+
readonly sha256: string;
|
|
42
|
+
readonly entries: ReadonlyArray<ModelPrice>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Lookup criteria.
|
|
46
|
+
*
|
|
47
|
+
* @stable
|
|
48
|
+
*/
|
|
49
|
+
interface LookupPriceArgs {
|
|
50
|
+
readonly provider: string;
|
|
51
|
+
readonly model: string;
|
|
52
|
+
readonly region?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Result of {@link lookupPrice} when the model is known.
|
|
56
|
+
*
|
|
57
|
+
* @stable
|
|
58
|
+
*/
|
|
59
|
+
interface LookupPriceResult {
|
|
60
|
+
readonly inputUsdPerToken: number;
|
|
61
|
+
readonly outputUsdPerToken: number;
|
|
62
|
+
readonly cachedReadUsdPerToken?: number;
|
|
63
|
+
readonly reasoningUsdPerToken?: number;
|
|
64
|
+
readonly source: string;
|
|
65
|
+
readonly snapshotDate: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Result row reported by {@link diffPricing}.
|
|
69
|
+
*
|
|
70
|
+
* @stable
|
|
71
|
+
*/
|
|
72
|
+
interface PricingDiffEntry {
|
|
73
|
+
readonly provider: string;
|
|
74
|
+
readonly model: string;
|
|
75
|
+
readonly kind: 'added' | 'removed' | 'changed';
|
|
76
|
+
readonly before?: ModelPrice;
|
|
77
|
+
readonly after?: ModelPrice;
|
|
78
|
+
readonly changedFields?: ReadonlyArray<keyof ModelPrice>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Span-shape input accepted by {@link listMissingModels}. Lightweight
|
|
82
|
+
* subset of `SpanRecord` from `@graphorin/observability` so the
|
|
83
|
+
* pricing package stays free of an observability dependency.
|
|
84
|
+
*
|
|
85
|
+
* @stable
|
|
86
|
+
*/
|
|
87
|
+
interface PricingTraceSpanLike {
|
|
88
|
+
readonly attributes: Readonly<Record<string, unknown>>;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { type Cost, LookupPriceArgs, LookupPriceResult, ModelPrice, PricingDiffEntry, PricingSnapshot, PricingTraceSpanLike };
|
|
92
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;AAiEA;AAcA;;;;;;AAgBA;UA/EiB,UAAA;;;;;;;;;;;;;;;;;;;;;;;UAwBA,eAAA;;;;;;oBAMG,cAAc;;;;;;;UAQjB,eAAA;;;;;;;;;;UAWA,iBAAA;;;;;;;;;;;;;UAcA,gBAAA;;;;oBAIG;mBACD;2BACQ,oBAAoB;;;;;;;;;UAU9B,oBAAA;uBACM,SAAS"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphorin/pricing",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Bundled LLM pricing snapshot for the Graphorin framework. Ships per-provider, per-model input / output / cached-read prices alongside provenance metadata, a `lookupPrice(...)` resolver, snapshot diffing utilities, an opt-in `refreshPricing(...)` hook (network gated; never automatic), and `listMissingModels(...)` for trace audits.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Oleksiy Stepurenko",
|
|
7
|
+
"homepage": "https://github.com/o-stepper/graphorin/tree/main/packages/pricing",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/o-stepper/graphorin.git",
|
|
11
|
+
"directory": "packages/pricing"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/o-stepper/graphorin/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"graphorin",
|
|
18
|
+
"ai",
|
|
19
|
+
"agents",
|
|
20
|
+
"framework",
|
|
21
|
+
"pricing",
|
|
22
|
+
"tokens",
|
|
23
|
+
"cost",
|
|
24
|
+
"llm"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22.0.0"
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.js",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./snapshot": {
|
|
39
|
+
"types": "./dist/snapshot/index.d.ts",
|
|
40
|
+
"import": "./dist/snapshot/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"README.md",
|
|
47
|
+
"CHANGELOG.md",
|
|
48
|
+
"LICENSE"
|
|
49
|
+
],
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@graphorin/core": "0.5.0"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public",
|
|
55
|
+
"provenance": true
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsdown",
|
|
60
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"lint": "biome check .",
|
|
63
|
+
"clean": "rimraf dist .turbo *.tsbuildinfo"
|
|
64
|
+
}
|
|
65
|
+
}
|