@graphorin/pricing 0.5.0 → 0.6.1
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 +39 -0
- package/README.md +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +1 -0
- package/dist/diff.js.map +1 -1
- package/dist/index.d.ts +9 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/lookup.d.ts +9 -5
- package/dist/lookup.d.ts.map +1 -1
- package/dist/lookup.js +15 -5
- package/dist/lookup.js.map +1 -1
- package/dist/missing.js +6 -2
- package/dist/missing.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/refresh.d.ts +2 -2
- package/dist/refresh.js +2 -1
- package/dist/refresh.js.map +1 -1
- package/dist/snapshot/bundled.d.ts +2 -2
- package/dist/snapshot/bundled.d.ts.map +1 -1
- package/dist/snapshot/bundled.js +122 -8
- package/dist/snapshot/bundled.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @graphorin/pricing
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#142](https://github.com/o-stepper/graphorin/pull/142) [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430) Thanks [@o-stepper](https://github.com/o-stepper)! - Version constants and version-bearing strings now derive from each package's manifest at build time (`VERSION = pkg.version`; writer ids, client/server info, OTLP framework attributes, build-info metrics interpolate it). No behavioral change at the current version: the rendered strings are byte-identical. A release bump no longer edits source; the new `check-version-consistency` gate fails any reintroduced hardcoded framework version.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430)]:
|
|
10
|
+
- @graphorin/core@0.6.1
|
|
11
|
+
|
|
12
|
+
## 0.6.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Prompt-cache economics end-to-end (audit 2026-07-04 Wave C, cluster C1).
|
|
17
|
+
|
|
18
|
+
- `Usage` gains `cachedReadTokens` / `cacheWriteTokens` (subsets of `promptTokens`), mapped by the vercel adapter (v7 `inputTokenDetails`; reasoning split kept exclusive of `completionTokens`) and the OpenAI-compatible adapter (`prompt_tokens_details.cached_tokens`); the fields flow through step/run aggregation, `usageByModel`, run-state (de)serialization and `withCostTracking` (new `cachedReadPerMtok`/`cacheWritePerMtok` lookup rates, full-input-rate fallback).
|
|
19
|
+
- New opt-in `ProviderRequest.cachePolicy` / `AgentConfig.cachePolicy` (`{ breakpoints: 'auto', ttl? }`): the vercel adapter anchors Anthropic `cache_control` on the first and last conversation messages so the stable prefix is written once and read at the discounted rate every later step.
|
|
20
|
+
- `ModelPrice` gains `cacheWriteUsdPerToken`; `calculateCost` bills cache writes; the bundled pricing snapshot is regenerated (2026-07-04) with current Anthropic 4.x / OpenAI gpt-5 + gpt-4.1 + o3/o4-mini / Gemini 2.5 families (legacy ids retained), `lookupPrice` resolves dated ids via a date-suffix fallback, and a new snapshot-coverage release gate cross-checks the model-tier classifier against the snapshot with an explicit known-unpriced allowlist (post-cutoff models report null cost + WARN instead of invented numbers).
|
|
21
|
+
- Cache-friendly catalogue: handoff tools serialize BEFORE the growing promoted section (byte-stable prefix under `tool_search` promotions), and new `toolPromotion: 'run-boundary'` freezes the advertised catalogue for a whole run while still persisting discoveries.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- [#138](https://github.com/o-stepper/graphorin/pull/138) [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a) Thanks [@o-stepper](https://github.com/o-stepper)! - OTel GenAI alignment + honest eval statistics (audit 2026-07-04 Wave E, cluster E8).
|
|
26
|
+
|
|
27
|
+
- Span names follow the GenAI semconv `{operation} {target}` shape when attributes carry the target (`chat <model>`, `execute_tool <tool>`, `invoke_agent <agent>`); the operation-mapping table and `GenAIOperationName` gain `invoke_agent` to match what the runtime actually emits; `agent.step` spans carry `gen_ai.agent.name`; pricing's `listMissingModels` reads the current `gen_ai.provider.name` attribute first (deprecated `gen_ai.system` kept as fallback).
|
|
28
|
+
- New `@graphorin/evals` stats module: `mean`/`sampleStddev`, Wilson 95% interval, `passHatK` over `-iter-N` outcomes, and McNemar paired significance. `runEvals` summaries always carry `passRateCi` and (under `iterations > 1`) `passHatK`; `detectRegressions` annotates pass-rate-drop findings with the paired regressed/improved counts + p-value, and opt-in `requireSignificance` vetoes drops the paired test cannot distinguish from noise - the fixed tolerance alone was sample-size blind.
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [[`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627)]:
|
|
31
|
+
- @graphorin/core@0.6.0
|
|
32
|
+
|
|
33
|
+
## 0.5.0
|
|
34
|
+
|
|
35
|
+
First version published to the npm registry (with Sigstore build
|
|
36
|
+
provenance). The 0.2.0, 0.3.0, and 0.4.0 versions were internal lockstep
|
|
37
|
+
milestones and were never published. All `@graphorin/*` packages release
|
|
38
|
+
lockstep at the same version; the full release notes for 0.2.0-0.5.0 live
|
|
39
|
+
in the repository-level
|
|
40
|
+
[CHANGELOG](https://github.com/o-stepper/graphorin/blob/main/CHANGELOG.md).
|
|
41
|
+
|
|
3
42
|
## 0.1.0
|
|
4
43
|
|
|
5
44
|
Initial release. See the workspace root `CHANGELOG.md` for the full
|
package/README.md
CHANGED
|
@@ -66,4 +66,4 @@ MIT © 2026 [Oleksiy Stepurenko](https://github.com/o-stepper).
|
|
|
66
66
|
|
|
67
67
|
---
|
|
68
68
|
|
|
69
|
-
**Project Graphorin** · v0.
|
|
69
|
+
**Project Graphorin** · v0.6.1 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
package/dist/diff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","names":[],"sources":["../src/diff.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","names":[],"sources":["../src/diff.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;iBA2BgB,WAAA,SACN,wBACD,kBACN,cAAc"}
|
package/dist/diff.js
CHANGED
package/dist/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","names":["COMPARED_FIELDS: ReadonlyArray<keyof ModelPrice>","out: PricingDiffEntry[]"],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * `diffPricing(...)`
|
|
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 'cacheWriteUsdPerToken',\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;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
CHANGED
|
@@ -9,33 +9,32 @@ import { BUNDLED_SNAPSHOT, SNAPSHOT_DATE, computeEntriesDigest } from "./snapsho
|
|
|
9
9
|
//#region src/index.d.ts
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @graphorin/pricing
|
|
12
|
+
* @graphorin/pricing - bundled LLM pricing snapshot for the Graphorin
|
|
13
13
|
* framework.
|
|
14
14
|
*
|
|
15
15
|
* Highlights:
|
|
16
16
|
*
|
|
17
|
-
* - {@link BUNDLED_SNAPSHOT}
|
|
17
|
+
* - {@link BUNDLED_SNAPSHOT} - bundled snapshot covering Anthropic,
|
|
18
18
|
* OpenAI, Google, Mistral, Cohere + the local providers
|
|
19
19
|
* (Ollama / llama.cpp). Each entry carries a per-token price and
|
|
20
20
|
* the canonical SHA-256 digest of the `entries` array.
|
|
21
|
-
* - {@link lookupPrice}
|
|
21
|
+
* - {@link lookupPrice} - resolve a `(provider, model)` pair against a
|
|
22
22
|
* snapshot. Returns `null` for unknown entries plus emits one WARN
|
|
23
23
|
* per process-lifetime per unknown pair.
|
|
24
|
-
* - {@link calculateCost}
|
|
24
|
+
* - {@link calculateCost} - multiply a price by token counts for a
|
|
25
25
|
* single LLM call.
|
|
26
|
-
* - {@link diffPricing}
|
|
27
|
-
* - {@link refreshPricing}
|
|
28
|
-
* - {@link listMissingModels}
|
|
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
29
|
* used by `graphorin pricing missing` (Phase 15).
|
|
30
30
|
*
|
|
31
|
-
* The bundled snapshot is intentionally small
|
|
31
|
+
* The bundled snapshot is intentionally small - operators wanting the
|
|
32
32
|
* full upstream catalogue should run `refreshPricing(...)` and
|
|
33
33
|
* persist the result to disk.
|
|
34
34
|
*
|
|
35
35
|
* @packageDocumentation
|
|
36
36
|
*/
|
|
37
|
-
|
|
38
|
-
declare const VERSION = "0.5.0";
|
|
37
|
+
declare const VERSION: string;
|
|
39
38
|
//#endregion
|
|
40
39
|
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
40
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;AA8BA;;;;;;;;;;;;;;;;;cAAa"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "./package.js";
|
|
1
2
|
import { DEFAULT_PRICING_AUTO_REFRESH } from "./config.js";
|
|
2
3
|
import { diffPricing } from "./diff.js";
|
|
3
4
|
import { BUNDLED_SNAPSHOT, SNAPSHOT_DATE, computeEntriesDigest } from "./snapshot/bundled.js";
|
|
@@ -8,33 +9,33 @@ import "./snapshot/index.js";
|
|
|
8
9
|
|
|
9
10
|
//#region src/index.ts
|
|
10
11
|
/**
|
|
11
|
-
* @graphorin/pricing
|
|
12
|
+
* @graphorin/pricing - bundled LLM pricing snapshot for the Graphorin
|
|
12
13
|
* framework.
|
|
13
14
|
*
|
|
14
15
|
* Highlights:
|
|
15
16
|
*
|
|
16
|
-
* - {@link BUNDLED_SNAPSHOT}
|
|
17
|
+
* - {@link BUNDLED_SNAPSHOT} - bundled snapshot covering Anthropic,
|
|
17
18
|
* OpenAI, Google, Mistral, Cohere + the local providers
|
|
18
19
|
* (Ollama / llama.cpp). Each entry carries a per-token price and
|
|
19
20
|
* the canonical SHA-256 digest of the `entries` array.
|
|
20
|
-
* - {@link lookupPrice}
|
|
21
|
+
* - {@link lookupPrice} - resolve a `(provider, model)` pair against a
|
|
21
22
|
* snapshot. Returns `null` for unknown entries plus emits one WARN
|
|
22
23
|
* per process-lifetime per unknown pair.
|
|
23
|
-
* - {@link calculateCost}
|
|
24
|
+
* - {@link calculateCost} - multiply a price by token counts for a
|
|
24
25
|
* single LLM call.
|
|
25
|
-
* - {@link diffPricing}
|
|
26
|
-
* - {@link refreshPricing}
|
|
27
|
-
* - {@link listMissingModels}
|
|
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;
|
|
28
29
|
* used by `graphorin pricing missing` (Phase 15).
|
|
29
30
|
*
|
|
30
|
-
* The bundled snapshot is intentionally small
|
|
31
|
+
* The bundled snapshot is intentionally small - operators wanting the
|
|
31
32
|
* full upstream catalogue should run `refreshPricing(...)` and
|
|
32
33
|
* persist the result to disk.
|
|
33
34
|
*
|
|
34
35
|
* @packageDocumentation
|
|
35
36
|
*/
|
|
36
|
-
/** Canonical version constant
|
|
37
|
-
const VERSION =
|
|
37
|
+
/** Canonical version constant, derived from `package.json` at build time. */
|
|
38
|
+
const VERSION = version;
|
|
38
39
|
|
|
39
40
|
//#endregion
|
|
40
41
|
export { BUNDLED_SNAPSHOT, DEFAULT_PRICING_AUTO_REFRESH, SNAPSHOT_DATE, VERSION, _resetLookupWarningsForTesting, calculateCost, computeEntriesDigest, diffPricing, listMissingModels, lookupPrice, refreshPricing, setLookupWarnSink };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/pricing
|
|
1
|
+
{"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"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, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAaA,UAAkBC"}
|
package/dist/lookup.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { LookupPriceArgs, LookupPriceResult, PricingSnapshot } from "./types.js"
|
|
|
10
10
|
*/
|
|
11
11
|
declare function setLookupWarnSink(sink: (line: string) => void): void;
|
|
12
12
|
/**
|
|
13
|
-
* @internal
|
|
13
|
+
* @internal - exposed for tests so the WARN-once cache can be reset.
|
|
14
14
|
*/
|
|
15
15
|
declare function _resetLookupWarningsForTesting(): void;
|
|
16
16
|
/**
|
|
@@ -29,19 +29,23 @@ declare function lookupPrice(args: LookupPriceArgs, snapshot?: PricingSnapshot):
|
|
|
29
29
|
* for a single LLM call without instantiating the cost tracker.
|
|
30
30
|
*
|
|
31
31
|
* Token-count contract (PS-19):
|
|
32
|
-
* - `inputTokens` **excludes** `cachedReadTokens`
|
|
33
|
-
* separately at
|
|
34
|
-
* to avoid double-billing.
|
|
32
|
+
* - `inputTokens` **excludes** `cachedReadTokens` and `cacheWriteTokens` -
|
|
33
|
+
* the cache legs are billed separately at their own rates, so pass the
|
|
34
|
+
* non-cached prompt count to avoid double-billing.
|
|
35
35
|
* - `reasoningTokens` are billed at `outputUsdPerToken` unless the model entry
|
|
36
36
|
* declares an explicit `reasoningUsdPerToken`.
|
|
37
|
+
* - `cacheWriteTokens` are billed at `cacheWriteUsdPerToken` when the entry
|
|
38
|
+
* declares one, else at the full input rate (a cache write is at minimum a
|
|
39
|
+
* normal input token - the fallback never under-bills relative to no cache).
|
|
37
40
|
*
|
|
38
41
|
* @stable
|
|
39
42
|
*/
|
|
40
43
|
declare function calculateCost(args: LookupPriceArgs & {
|
|
41
|
-
/** Non-cached prompt tokens (excludes
|
|
44
|
+
/** Non-cached prompt tokens (excludes cache reads AND cache writes). */
|
|
42
45
|
readonly inputTokens: number;
|
|
43
46
|
readonly outputTokens: number;
|
|
44
47
|
readonly cachedReadTokens?: number;
|
|
48
|
+
readonly cacheWriteTokens?: number;
|
|
45
49
|
readonly reasoningTokens?: number;
|
|
46
50
|
}, snapshot?: PricingSnapshot): {
|
|
47
51
|
readonly amount: number;
|
package/dist/lookup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lookup.d.ts","names":[],"sources":["../src/lookup.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"lookup.d.ts","names":[],"sources":["../src/lookup.ts"],"sourcesContent":[],"mappings":";;;;;AA8HA;;;;;iBA1GgB,iBAAA;;;;iBASA,8BAAA,CAAA;;;;;;;;;;iBAaA,WAAA,OACR,4BACI,kBACT;;;;;;;;;;;;;;;;;;iBAiFa,aAAA,OACR;;;;;;;cAQI"}
|
package/dist/lookup.js
CHANGED
|
@@ -2,7 +2,7 @@ import { BUNDLED_SNAPSHOT } from "./snapshot/bundled.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/lookup.ts
|
|
4
4
|
/**
|
|
5
|
-
* `lookupPrice(...)`
|
|
5
|
+
* `lookupPrice(...)` - resolve a per-token price for a given
|
|
6
6
|
* (provider, model) pair against a snapshot. Returns `null` when the
|
|
7
7
|
* model is unknown and emits one WARN per process-lifetime per
|
|
8
8
|
* unknown (provider, model) pair.
|
|
@@ -21,7 +21,7 @@ function setLookupWarnSink(sink) {
|
|
|
21
21
|
}
|
|
22
22
|
let WARN_SINK = (line) => console.warn(line);
|
|
23
23
|
/**
|
|
24
|
-
* @internal
|
|
24
|
+
* @internal - exposed for tests so the WARN-once cache can be reset.
|
|
25
25
|
*/
|
|
26
26
|
function _resetLookupWarningsForTesting() {
|
|
27
27
|
WARNED.clear();
|
|
@@ -38,6 +38,11 @@ function _resetLookupWarningsForTesting() {
|
|
|
38
38
|
function lookupPrice(args, snapshot = BUNDLED_SNAPSHOT) {
|
|
39
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
40
|
if (exact !== void 0) return entryToResult(exact, snapshot);
|
|
41
|
+
const dateless = args.model.replace(/-\d{8}$/, "");
|
|
42
|
+
if (dateless !== args.model) {
|
|
43
|
+
const alias = snapshot.entries.find((entry) => entry.provider === args.provider && entry.model === dateless && (args.region === void 0 || entry.region === void 0 || entry.region === args.region));
|
|
44
|
+
if (alias !== void 0) return entryToResult(alias, snapshot);
|
|
45
|
+
}
|
|
41
46
|
const wildcard = snapshot.entries.find((entry) => entry.provider === args.provider && entry.model === "*");
|
|
42
47
|
if (wildcard !== void 0) return entryToResult(wildcard, snapshot);
|
|
43
48
|
warnOnce(args);
|
|
@@ -48,6 +53,7 @@ function entryToResult(entry, snapshot) {
|
|
|
48
53
|
inputUsdPerToken: entry.inputUsdPerToken,
|
|
49
54
|
outputUsdPerToken: entry.outputUsdPerToken,
|
|
50
55
|
...entry.cachedReadUsdPerToken === void 0 ? {} : { cachedReadUsdPerToken: entry.cachedReadUsdPerToken },
|
|
56
|
+
...entry.cacheWriteUsdPerToken === void 0 ? {} : { cacheWriteUsdPerToken: entry.cacheWriteUsdPerToken },
|
|
51
57
|
...entry.reasoningUsdPerToken === void 0 ? {} : { reasoningUsdPerToken: entry.reasoningUsdPerToken },
|
|
52
58
|
source: snapshot.source,
|
|
53
59
|
snapshotDate: snapshot.snapshotDate
|
|
@@ -65,11 +71,14 @@ function warnOnce(args) {
|
|
|
65
71
|
* for a single LLM call without instantiating the cost tracker.
|
|
66
72
|
*
|
|
67
73
|
* Token-count contract (PS-19):
|
|
68
|
-
* - `inputTokens` **excludes** `cachedReadTokens`
|
|
69
|
-
* separately at
|
|
70
|
-
* to avoid double-billing.
|
|
74
|
+
* - `inputTokens` **excludes** `cachedReadTokens` and `cacheWriteTokens` -
|
|
75
|
+
* the cache legs are billed separately at their own rates, so pass the
|
|
76
|
+
* non-cached prompt count to avoid double-billing.
|
|
71
77
|
* - `reasoningTokens` are billed at `outputUsdPerToken` unless the model entry
|
|
72
78
|
* declares an explicit `reasoningUsdPerToken`.
|
|
79
|
+
* - `cacheWriteTokens` are billed at `cacheWriteUsdPerToken` when the entry
|
|
80
|
+
* declares one, else at the full input rate (a cache write is at minimum a
|
|
81
|
+
* normal input token - the fallback never under-bills relative to no cache).
|
|
73
82
|
*
|
|
74
83
|
* @stable
|
|
75
84
|
*/
|
|
@@ -80,6 +89,7 @@ function calculateCost(args, snapshot = BUNDLED_SNAPSHOT) {
|
|
|
80
89
|
amount += price.inputUsdPerToken * args.inputTokens;
|
|
81
90
|
amount += price.outputUsdPerToken * args.outputTokens;
|
|
82
91
|
if (args.cachedReadTokens !== void 0 && price.cachedReadUsdPerToken !== void 0) amount += price.cachedReadUsdPerToken * args.cachedReadTokens;
|
|
92
|
+
if (args.cacheWriteTokens !== void 0) amount += (price.cacheWriteUsdPerToken ?? price.inputUsdPerToken) * args.cacheWriteTokens;
|
|
83
93
|
if (args.reasoningTokens !== void 0) amount += (price.reasoningUsdPerToken ?? price.outputUsdPerToken) * args.reasoningTokens;
|
|
84
94
|
return {
|
|
85
95
|
amount,
|
package/dist/lookup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lookup.js","names":["WARN_SINK: (line: string) => void"],"sources":["../src/lookup.ts"],"sourcesContent":["/**\n * `lookupPrice(...)`
|
|
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 // Date-suffix fallback (core-provider-03): dated ids like\n // `claude-haiku-4-5-20251001` resolve to their dateless alias entry\n // (`claude-haiku-4-5`) so the snapshot does not need one row per\n // dated release.\n const dateless = args.model.replace(/-\\d{8}$/, '');\n if (dateless !== args.model) {\n const alias = snapshot.entries.find(\n (entry) =>\n entry.provider === args.provider &&\n entry.model === dateless &&\n (args.region === undefined || entry.region === undefined || entry.region === args.region),\n );\n if (alias !== undefined) return entryToResult(alias, snapshot);\n }\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.cacheWriteUsdPerToken === undefined\n ? {}\n : { cacheWriteUsdPerToken: entry.cacheWriteUsdPerToken }),\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` and `cacheWriteTokens` -\n * the cache legs are billed separately at their own rates, so pass the\n * non-cached prompt count to avoid double-billing.\n * - `reasoningTokens` are billed at `outputUsdPerToken` unless the model entry\n * declares an explicit `reasoningUsdPerToken`.\n * - `cacheWriteTokens` are billed at `cacheWriteUsdPerToken` when the entry\n * declares one, else at the full input rate (a cache write is at minimum a\n * normal input token - the fallback never under-bills relative to no cache).\n *\n * @stable\n */\nexport function calculateCost(\n args: LookupPriceArgs & {\n /** Non-cached prompt tokens (excludes cache reads AND cache writes). */\n readonly inputTokens: number;\n readonly outputTokens: number;\n readonly cachedReadTokens?: number;\n readonly cacheWriteTokens?: 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.cacheWriteTokens !== undefined) {\n amount += (price.cacheWriteUsdPerToken ?? price.inputUsdPerToken) * args.cacheWriteTokens;\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;CAM9D,MAAM,WAAW,KAAK,MAAM,QAAQ,WAAW,GAAG;AAClD,KAAI,aAAa,KAAK,OAAO;EAC3B,MAAM,QAAQ,SAAS,QAAQ,MAC5B,UACC,MAAM,aAAa,KAAK,YACxB,MAAM,UAAU,aACf,KAAK,WAAW,UAAa,MAAM,WAAW,UAAa,MAAM,WAAW,KAAK,QACrF;AACD,MAAI,UAAU,OAAW,QAAO,cAAc,OAAO,SAAS;;CAIhE,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,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;;;;;;;;;;;;;;;;;;;AAoBH,SAAgB,cACd,MAQA,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,qBAAqB,OAC5B,YAAW,MAAM,yBAAyB,MAAM,oBAAoB,KAAK;AAE3E,KAAI,KAAK,oBAAoB,OAI3B,YAAW,MAAM,wBAAwB,MAAM,qBAAqB,KAAK;AAE3E,QAAO;EAAE;EAAQ,UAAU;EAAO"}
|
package/dist/missing.js
CHANGED
|
@@ -3,7 +3,7 @@ import { lookupPrice } from "./lookup.js";
|
|
|
3
3
|
|
|
4
4
|
//#region src/missing.ts
|
|
5
5
|
/**
|
|
6
|
-
* `listMissingModels(...)`
|
|
6
|
+
* `listMissingModels(...)` - scan a sequence of trace spans and return
|
|
7
7
|
* the unique (provider, model) pairs that the snapshot does not
|
|
8
8
|
* recognise.
|
|
9
9
|
*
|
|
@@ -23,7 +23,11 @@ import { lookupPrice } from "./lookup.js";
|
|
|
23
23
|
function listMissingModels(spans, snapshot = BUNDLED_SNAPSHOT) {
|
|
24
24
|
const seen = /* @__PURE__ */ new Map();
|
|
25
25
|
for (const span of spans) {
|
|
26
|
-
const provider = stringAttr(span, [
|
|
26
|
+
const provider = stringAttr(span, [
|
|
27
|
+
"gen_ai.provider.name",
|
|
28
|
+
"gen_ai.system",
|
|
29
|
+
"graphorin.provider.id"
|
|
30
|
+
]);
|
|
27
31
|
const model = stringAttr(span, [
|
|
28
32
|
"gen_ai.request.model",
|
|
29
33
|
"gen_ai.response.model",
|
package/dist/missing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"missing.js","names":[],"sources":["../src/missing.ts"],"sourcesContent":["/**\n * `listMissingModels(...)`
|
|
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 // E8: `gen_ai.provider.name` is the current OTel GenAI attribute (what\n // withTracing emits); `gen_ai.system` is its deprecated predecessor kept\n // for older recorded traces.\n const provider = stringAttr(span, [\n 'gen_ai.provider.name',\n 'gen_ai.system',\n 'graphorin.provider.id',\n ]);\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;EAIxB,MAAM,WAAW,WAAW,MAAM;GAChC;GACA;GACA;GACD,CAAC;EACF,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"}
|
package/dist/package.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@graphorin/pricing\",\n \"version\": \"0.6.1\",\n \"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.\",\n \"license\": \"MIT\",\n \"author\": \"Oleksiy Stepurenko\",\n \"homepage\": \"https://github.com/o-stepper/graphorin/tree/main/packages/pricing\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/o-stepper/graphorin.git\",\n \"directory\": \"packages/pricing\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/o-stepper/graphorin/issues\"\n },\n \"keywords\": [\n \"graphorin\",\n \"ai\",\n \"agents\",\n \"framework\",\n \"pricing\",\n \"tokens\",\n \"cost\",\n \"llm\"\n ],\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./snapshot\": {\n \"types\": \"./dist/snapshot/index.d.ts\",\n \"import\": \"./dist/snapshot/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"CHANGELOG.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit && tsc -p tsconfig.tests.json\",\n \"test\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"clean\": \"rimraf dist .turbo *.tsbuildinfo\"\n },\n \"dependencies\": {\n \"@graphorin/core\": \"workspace:*\"\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"provenance\": true\n },\n \"devDependencies\": {}\n}\n"],"mappings":";cAEa"}
|
package/dist/refresh.d.ts
CHANGED
|
@@ -10,11 +10,11 @@ import { PricingSnapshot } from "./types.js";
|
|
|
10
10
|
* @stable
|
|
11
11
|
*/
|
|
12
12
|
interface RefreshPricingOptions {
|
|
13
|
-
/** Snapshot URL
|
|
13
|
+
/** Snapshot URL - typically the upstream pricing JSON. */
|
|
14
14
|
readonly url: string;
|
|
15
15
|
/** Optional headers (auth, conditional GET, etc.). */
|
|
16
16
|
readonly headers?: Readonly<Record<string, string>>;
|
|
17
|
-
/** Optional fetch override
|
|
17
|
+
/** Optional fetch override - useful in tests. */
|
|
18
18
|
readonly fetchImpl?: typeof fetch;
|
|
19
19
|
/** Override the snapshot date stamped on the result. Defaults to today. */
|
|
20
20
|
readonly snapshotDate?: string;
|
package/dist/refresh.js
CHANGED
|
@@ -2,7 +2,7 @@ import { computeEntriesDigest } from "./snapshot/bundled.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/refresh.ts
|
|
4
4
|
/**
|
|
5
|
-
* `refreshPricing(...)`
|
|
5
|
+
* `refreshPricing(...)` - opt-in network call. The framework never
|
|
6
6
|
* invokes this automatically; it exists so the CLI (`graphorin
|
|
7
7
|
* pricing refresh`) and operator scripts can pull a fresh snapshot
|
|
8
8
|
* without re-installing the package.
|
|
@@ -72,6 +72,7 @@ function parseEntry(raw) {
|
|
|
72
72
|
inputUsdPerToken: e.inputUsdPerToken,
|
|
73
73
|
outputUsdPerToken: e.outputUsdPerToken,
|
|
74
74
|
...typeof e.cachedReadUsdPerToken === "number" ? { cachedReadUsdPerToken: e.cachedReadUsdPerToken } : {},
|
|
75
|
+
...typeof e.cacheWriteUsdPerToken === "number" ? { cacheWriteUsdPerToken: e.cacheWriteUsdPerToken } : {},
|
|
75
76
|
...typeof e.reasoningUsdPerToken === "number" ? { reasoningUsdPerToken: e.reasoningUsdPerToken } : {},
|
|
76
77
|
...typeof e.region === "string" ? { region: e.region } : {},
|
|
77
78
|
...typeof e.notes === "string" ? { notes: e.notes } : {}
|
package/dist/refresh.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh.js","names":["res: Awaited<ReturnType<typeof fetchImpl>>","raw: unknown"],"sources":["../src/refresh.ts"],"sourcesContent":["/**\n * `refreshPricing(...)`
|
|
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.cacheWriteUsdPerToken === 'number'\n ? { cacheWriteUsdPerToken: e.cacheWriteUsdPerToken }\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,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"}
|
|
@@ -2,8 +2,8 @@ import { ModelPrice, PricingSnapshot } from "../types.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/snapshot/bundled.d.ts
|
|
4
4
|
|
|
5
|
-
/** @internal
|
|
6
|
-
declare const SNAPSHOT_DATE = "2026-04
|
|
5
|
+
/** @internal - used for `lookupPrice` defaults. */
|
|
6
|
+
declare const SNAPSHOT_DATE = "2026-07-04";
|
|
7
7
|
/**
|
|
8
8
|
* Compute a deterministic SHA-256 of the entries. Sorting the entry
|
|
9
9
|
* keys before serialisation keeps the digest stable across Node
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundled.d.ts","names":[],"sources":["../../src/snapshot/bundled.ts"],"sourcesContent":[],"mappings":";;;;;cAkBa,aAAA;;;;;;;;
|
|
1
|
+
{"version":3,"file":"bundled.d.ts","names":[],"sources":["../../src/snapshot/bundled.ts"],"sourcesContent":[],"mappings":";;;;;cAkBa,aAAA;;;;;;;;iBAiQG,oBAAA,UAA8B,cAAc;;;;;;;;cAsB/C,kBAAkB"}
|
package/dist/snapshot/bundled.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
|
|
3
3
|
//#region src/snapshot/bundled.ts
|
|
4
4
|
/**
|
|
5
|
-
* Bundled pricing snapshot. The catalogue is intentionally small
|
|
5
|
+
* Bundled pricing snapshot. The catalogue is intentionally small -
|
|
6
6
|
* operators wanting the full upstream catalogue should run the opt-in
|
|
7
7
|
* `refreshPricing(...)` flow at build time and persist the result to
|
|
8
8
|
* a custom snapshot file.
|
|
@@ -13,29 +13,121 @@ import { createHash } from "node:crypto";
|
|
|
13
13
|
*
|
|
14
14
|
* @packageDocumentation
|
|
15
15
|
*/
|
|
16
|
-
/** @internal
|
|
17
|
-
const SNAPSHOT_DATE = "2026-04
|
|
16
|
+
/** @internal - used for `lookupPrice` defaults. */
|
|
17
|
+
const SNAPSHOT_DATE = "2026-07-04";
|
|
18
18
|
const ENTRIES = Object.freeze([
|
|
19
|
+
{
|
|
20
|
+
provider: "anthropic",
|
|
21
|
+
model: "claude-opus-4-5",
|
|
22
|
+
inputUsdPerToken: 5 / 1e6,
|
|
23
|
+
outputUsdPerToken: 25 / 1e6,
|
|
24
|
+
cachedReadUsdPerToken: .5 / 1e6,
|
|
25
|
+
cacheWriteUsdPerToken: 6.25 / 1e6
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
provider: "anthropic",
|
|
29
|
+
model: "claude-opus-4-1",
|
|
30
|
+
inputUsdPerToken: 15 / 1e6,
|
|
31
|
+
outputUsdPerToken: 75 / 1e6,
|
|
32
|
+
cachedReadUsdPerToken: 1.5 / 1e6,
|
|
33
|
+
cacheWriteUsdPerToken: 18.75 / 1e6
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
provider: "anthropic",
|
|
37
|
+
model: "claude-sonnet-4-5",
|
|
38
|
+
inputUsdPerToken: 3 / 1e6,
|
|
39
|
+
outputUsdPerToken: 15 / 1e6,
|
|
40
|
+
cachedReadUsdPerToken: .3 / 1e6,
|
|
41
|
+
cacheWriteUsdPerToken: 3.75 / 1e6,
|
|
42
|
+
notes: "Prompts <= 200k tokens; the long-context tier is priced higher."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
provider: "anthropic",
|
|
46
|
+
model: "claude-haiku-4-5",
|
|
47
|
+
inputUsdPerToken: 1 / 1e6,
|
|
48
|
+
outputUsdPerToken: 5 / 1e6,
|
|
49
|
+
cachedReadUsdPerToken: .1 / 1e6,
|
|
50
|
+
cacheWriteUsdPerToken: 1.25 / 1e6
|
|
51
|
+
},
|
|
19
52
|
{
|
|
20
53
|
provider: "anthropic",
|
|
21
54
|
model: "claude-3-5-sonnet-20241022",
|
|
22
55
|
inputUsdPerToken: 3 / 1e6,
|
|
23
56
|
outputUsdPerToken: 15 / 1e6,
|
|
24
|
-
cachedReadUsdPerToken: .3 / 1e6
|
|
57
|
+
cachedReadUsdPerToken: .3 / 1e6,
|
|
58
|
+
cacheWriteUsdPerToken: 3.75 / 1e6
|
|
25
59
|
},
|
|
26
60
|
{
|
|
27
61
|
provider: "anthropic",
|
|
28
62
|
model: "claude-3-5-haiku-20241022",
|
|
29
63
|
inputUsdPerToken: .8 / 1e6,
|
|
30
64
|
outputUsdPerToken: 4 / 1e6,
|
|
31
|
-
cachedReadUsdPerToken: .08 / 1e6
|
|
65
|
+
cachedReadUsdPerToken: .08 / 1e6,
|
|
66
|
+
cacheWriteUsdPerToken: 1 / 1e6
|
|
32
67
|
},
|
|
33
68
|
{
|
|
34
69
|
provider: "anthropic",
|
|
35
70
|
model: "claude-3-opus-20240229",
|
|
36
71
|
inputUsdPerToken: 15 / 1e6,
|
|
37
72
|
outputUsdPerToken: 75 / 1e6,
|
|
38
|
-
cachedReadUsdPerToken: 1.5 / 1e6
|
|
73
|
+
cachedReadUsdPerToken: 1.5 / 1e6,
|
|
74
|
+
cacheWriteUsdPerToken: 18.75 / 1e6
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
provider: "openai",
|
|
78
|
+
model: "gpt-5",
|
|
79
|
+
inputUsdPerToken: 1.25 / 1e6,
|
|
80
|
+
outputUsdPerToken: 10 / 1e6,
|
|
81
|
+
cachedReadUsdPerToken: .125 / 1e6
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
provider: "openai",
|
|
85
|
+
model: "gpt-5-mini",
|
|
86
|
+
inputUsdPerToken: .25 / 1e6,
|
|
87
|
+
outputUsdPerToken: 2 / 1e6,
|
|
88
|
+
cachedReadUsdPerToken: .025 / 1e6
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
provider: "openai",
|
|
92
|
+
model: "gpt-5-nano",
|
|
93
|
+
inputUsdPerToken: .05 / 1e6,
|
|
94
|
+
outputUsdPerToken: .4 / 1e6,
|
|
95
|
+
cachedReadUsdPerToken: .005 / 1e6
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
provider: "openai",
|
|
99
|
+
model: "gpt-4.1",
|
|
100
|
+
inputUsdPerToken: 2 / 1e6,
|
|
101
|
+
outputUsdPerToken: 8 / 1e6,
|
|
102
|
+
cachedReadUsdPerToken: .5 / 1e6
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
provider: "openai",
|
|
106
|
+
model: "gpt-4.1-mini",
|
|
107
|
+
inputUsdPerToken: .4 / 1e6,
|
|
108
|
+
outputUsdPerToken: 1.6 / 1e6,
|
|
109
|
+
cachedReadUsdPerToken: .1 / 1e6
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
provider: "openai",
|
|
113
|
+
model: "gpt-4.1-nano",
|
|
114
|
+
inputUsdPerToken: .1 / 1e6,
|
|
115
|
+
outputUsdPerToken: .4 / 1e6,
|
|
116
|
+
cachedReadUsdPerToken: .025 / 1e6
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
provider: "openai",
|
|
120
|
+
model: "o3",
|
|
121
|
+
inputUsdPerToken: 2 / 1e6,
|
|
122
|
+
outputUsdPerToken: 8 / 1e6,
|
|
123
|
+
cachedReadUsdPerToken: .5 / 1e6
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
provider: "openai",
|
|
127
|
+
model: "o4-mini",
|
|
128
|
+
inputUsdPerToken: 1.1 / 1e6,
|
|
129
|
+
outputUsdPerToken: 4.4 / 1e6,
|
|
130
|
+
cachedReadUsdPerToken: .275 / 1e6
|
|
39
131
|
},
|
|
40
132
|
{
|
|
41
133
|
provider: "openai",
|
|
@@ -65,6 +157,28 @@ const ENTRIES = Object.freeze([
|
|
|
65
157
|
outputUsdPerToken: 4.4 / 1e6,
|
|
66
158
|
cachedReadUsdPerToken: .55 / 1e6
|
|
67
159
|
},
|
|
160
|
+
{
|
|
161
|
+
provider: "google",
|
|
162
|
+
model: "gemini-2.5-pro",
|
|
163
|
+
inputUsdPerToken: 1.25 / 1e6,
|
|
164
|
+
outputUsdPerToken: 10 / 1e6,
|
|
165
|
+
cachedReadUsdPerToken: .3125 / 1e6,
|
|
166
|
+
notes: "Prompts <= 200k tokens; the long-context tier is priced higher."
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
provider: "google",
|
|
170
|
+
model: "gemini-2.5-flash",
|
|
171
|
+
inputUsdPerToken: .3 / 1e6,
|
|
172
|
+
outputUsdPerToken: 2.5 / 1e6,
|
|
173
|
+
cachedReadUsdPerToken: .075 / 1e6
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
provider: "google",
|
|
177
|
+
model: "gemini-2.5-flash-lite",
|
|
178
|
+
inputUsdPerToken: .1 / 1e6,
|
|
179
|
+
outputUsdPerToken: .4 / 1e6,
|
|
180
|
+
cachedReadUsdPerToken: .025 / 1e6
|
|
181
|
+
},
|
|
68
182
|
{
|
|
69
183
|
provider: "google",
|
|
70
184
|
model: "gemini-1.5-pro-002",
|
|
@@ -108,14 +222,14 @@ const ENTRIES = Object.freeze([
|
|
|
108
222
|
model: "*",
|
|
109
223
|
inputUsdPerToken: 0,
|
|
110
224
|
outputUsdPerToken: 0,
|
|
111
|
-
notes: "Local Ollama deployment
|
|
225
|
+
notes: "Local Ollama deployment - operator-priced."
|
|
112
226
|
},
|
|
113
227
|
{
|
|
114
228
|
provider: "graphorin-llamacpp",
|
|
115
229
|
model: "*",
|
|
116
230
|
inputUsdPerToken: 0,
|
|
117
231
|
outputUsdPerToken: 0,
|
|
118
|
-
notes: "Local llama.cpp deployment
|
|
232
|
+
notes: "Local llama.cpp deployment - operator-priced."
|
|
119
233
|
}
|
|
120
234
|
]);
|
|
121
235
|
/**
|
|
@@ -1 +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"}
|
|
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-07-04';\n\nconst ENTRIES: ReadonlyArray<ModelPrice> = Object.freeze([\n // -----------------------------------------------------------------\n // Anthropic - current families (core-provider-03). Dateless alias ids;\n // dated ids (`claude-haiku-4-5-20251001`) resolve through the lookup's\n // date-suffix fallback. Cache-write = 1.25x input (5-minute cache).\n // NOTE: the Claude 5 family (fable/mythos) and any tier released after\n // this snapshot date have NO entry on purpose - cost tracking reports\n // null + one WARN instead of an invented number. Refresh via\n // `refreshPricing(...)` or contribute the entry when pricing is public.\n // -----------------------------------------------------------------\n {\n provider: 'anthropic',\n model: 'claude-opus-4-5',\n inputUsdPerToken: 5 / 1_000_000,\n outputUsdPerToken: 25 / 1_000_000,\n cachedReadUsdPerToken: 0.5 / 1_000_000,\n cacheWriteUsdPerToken: 6.25 / 1_000_000,\n },\n {\n provider: 'anthropic',\n model: 'claude-opus-4-1',\n inputUsdPerToken: 15 / 1_000_000,\n outputUsdPerToken: 75 / 1_000_000,\n cachedReadUsdPerToken: 1.5 / 1_000_000,\n cacheWriteUsdPerToken: 18.75 / 1_000_000,\n },\n {\n provider: 'anthropic',\n model: 'claude-sonnet-4-5',\n inputUsdPerToken: 3 / 1_000_000,\n outputUsdPerToken: 15 / 1_000_000,\n cachedReadUsdPerToken: 0.3 / 1_000_000,\n cacheWriteUsdPerToken: 3.75 / 1_000_000,\n notes: 'Prompts <= 200k tokens; the long-context tier is priced higher.',\n },\n {\n provider: 'anthropic',\n model: 'claude-haiku-4-5',\n inputUsdPerToken: 1 / 1_000_000,\n outputUsdPerToken: 5 / 1_000_000,\n cachedReadUsdPerToken: 0.1 / 1_000_000,\n cacheWriteUsdPerToken: 1.25 / 1_000_000,\n },\n // Retired / legacy ids retained for historical cost attribution.\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 cacheWriteUsdPerToken: 3.75 / 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 cacheWriteUsdPerToken: 1 / 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 cacheWriteUsdPerToken: 18.75 / 1_000_000,\n },\n // -----------------------------------------------------------------\n // OpenAI - cache reads are automatic (no breakpoints) and there is no\n // cache-write charge, so entries carry only `cachedReadUsdPerToken`.\n // -----------------------------------------------------------------\n {\n provider: 'openai',\n model: 'gpt-5',\n inputUsdPerToken: 1.25 / 1_000_000,\n outputUsdPerToken: 10 / 1_000_000,\n cachedReadUsdPerToken: 0.125 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'gpt-5-mini',\n inputUsdPerToken: 0.25 / 1_000_000,\n outputUsdPerToken: 2 / 1_000_000,\n cachedReadUsdPerToken: 0.025 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'gpt-5-nano',\n inputUsdPerToken: 0.05 / 1_000_000,\n outputUsdPerToken: 0.4 / 1_000_000,\n cachedReadUsdPerToken: 0.005 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'gpt-4.1',\n inputUsdPerToken: 2 / 1_000_000,\n outputUsdPerToken: 8 / 1_000_000,\n cachedReadUsdPerToken: 0.5 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'gpt-4.1-mini',\n inputUsdPerToken: 0.4 / 1_000_000,\n outputUsdPerToken: 1.6 / 1_000_000,\n cachedReadUsdPerToken: 0.1 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'gpt-4.1-nano',\n inputUsdPerToken: 0.1 / 1_000_000,\n outputUsdPerToken: 0.4 / 1_000_000,\n cachedReadUsdPerToken: 0.025 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'o3',\n inputUsdPerToken: 2 / 1_000_000,\n outputUsdPerToken: 8 / 1_000_000,\n cachedReadUsdPerToken: 0.5 / 1_000_000,\n },\n {\n provider: 'openai',\n model: 'o4-mini',\n inputUsdPerToken: 1.1 / 1_000_000,\n outputUsdPerToken: 4.4 / 1_000_000,\n cachedReadUsdPerToken: 0.275 / 1_000_000,\n },\n // Retired / legacy ids retained for historical cost attribution.\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 - implicit-caching read discount; context-cache storage is\n // billed per hour upstream and is not modelled here.\n // -----------------------------------------------------------------\n {\n provider: 'google',\n model: 'gemini-2.5-pro',\n inputUsdPerToken: 1.25 / 1_000_000,\n outputUsdPerToken: 10 / 1_000_000,\n cachedReadUsdPerToken: 0.3125 / 1_000_000,\n notes: 'Prompts <= 200k tokens; the long-context tier is priced higher.',\n },\n {\n provider: 'google',\n model: 'gemini-2.5-flash',\n inputUsdPerToken: 0.3 / 1_000_000,\n outputUsdPerToken: 2.5 / 1_000_000,\n cachedReadUsdPerToken: 0.075 / 1_000_000,\n },\n {\n provider: 'google',\n model: 'gemini-2.5-flash-lite',\n inputUsdPerToken: 0.1 / 1_000_000,\n outputUsdPerToken: 0.4 / 1_000_000,\n cachedReadUsdPerToken: 0.025 / 1_000_000,\n },\n // Retired / legacy ids retained for historical cost attribution.\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;CAUvD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,KAAK;EACxB,uBAAuB,KAAM;EAC7B,uBAAuB,OAAO;EAC/B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAK;EACvB,mBAAmB,KAAK;EACxB,uBAAuB,MAAM;EAC7B,uBAAuB,QAAQ;EAChC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,KAAK;EACxB,uBAAuB,KAAM;EAC7B,uBAAuB,OAAO;EAC9B,OAAO;EACR;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,IAAI;EACvB,uBAAuB,KAAM;EAC7B,uBAAuB,OAAO;EAC/B;CAED;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,KAAK;EACxB,uBAAuB,KAAM;EAC7B,uBAAuB,OAAO;EAC/B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAM;EACxB,mBAAmB,IAAI;EACvB,uBAAuB,MAAO;EAC9B,uBAAuB,IAAI;EAC5B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAK;EACvB,mBAAmB,KAAK;EACxB,uBAAuB,MAAM;EAC7B,uBAAuB,QAAQ;EAChC;CAKD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,OAAO;EACzB,mBAAmB,KAAK;EACxB,uBAAuB,OAAQ;EAChC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAO;EACzB,mBAAmB,IAAI;EACvB,uBAAuB,OAAQ;EAChC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAO;EACzB,mBAAmB,KAAM;EACzB,uBAAuB,OAAQ;EAChC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,IAAI;EACvB,uBAAuB,KAAM;EAC9B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAM;EACxB,mBAAmB,MAAM;EACzB,uBAAuB,KAAM;EAC9B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAM;EACxB,mBAAmB,KAAM;EACzB,uBAAuB,OAAQ;EAChC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,IAAI;EACtB,mBAAmB,IAAI;EACvB,uBAAuB,KAAM;EAC9B;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,MAAM;EACxB,mBAAmB,MAAM;EACzB,uBAAuB,OAAQ;EAChC;CAED;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;CAKD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,OAAO;EACzB,mBAAmB,KAAK;EACxB,uBAAuB,QAAS;EAChC,OAAO;EACR;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAM;EACxB,mBAAmB,MAAM;EACzB,uBAAuB,OAAQ;EAChC;CACD;EACE,UAAU;EACV,OAAO;EACP,kBAAkB,KAAM;EACxB,mBAAmB,KAAM;EACzB,uBAAuB,OAAQ;EAChC;CAED;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
CHANGED
|
@@ -21,6 +21,12 @@ interface ModelPrice {
|
|
|
21
21
|
readonly outputUsdPerToken: number;
|
|
22
22
|
/** Optional cached-read price (Anthropic / OpenAI prompt caching). */
|
|
23
23
|
readonly cachedReadUsdPerToken?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Optional cache-write (cache-creation) price. Anthropic bills prompt
|
|
26
|
+
* tokens written to the 5-minute cache at 1.25x the input rate; OpenAI
|
|
27
|
+
* does not charge (or report) cache writes, so its entries omit this.
|
|
28
|
+
*/
|
|
29
|
+
readonly cacheWriteUsdPerToken?: number;
|
|
24
30
|
/** Optional reasoning-token price (OpenAI o1 / Gemini 2 thinking). */
|
|
25
31
|
readonly reasoningUsdPerToken?: number;
|
|
26
32
|
/** Optional region label (e.g. `'us-east-1'`). */
|
|
@@ -60,6 +66,7 @@ interface LookupPriceResult {
|
|
|
60
66
|
readonly inputUsdPerToken: number;
|
|
61
67
|
readonly outputUsdPerToken: number;
|
|
62
68
|
readonly cachedReadUsdPerToken?: number;
|
|
69
|
+
readonly cacheWriteUsdPerToken?: number;
|
|
63
70
|
readonly reasoningUsdPerToken?: number;
|
|
64
71
|
readonly source: string;
|
|
65
72
|
readonly snapshotDate: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;AAuEA;AAeA;;;;;;AAgBA;UAtFiB,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA8BA,eAAA;;;;;;oBAMG,cAAc;;;;;;;UAQjB,eAAA;;;;;;;;;;UAWA,iBAAA;;;;;;;;;;;;;;UAeA,gBAAA;;;;oBAIG;mBACD;2BACQ,oBAAoB;;;;;;;;;UAU9B,oBAAA;uBACM,SAAS"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphorin/pricing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"author": "Oleksiy Stepurenko",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"LICENSE"
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@graphorin/core": "0.
|
|
51
|
+
"@graphorin/core": "0.6.1"
|
|
52
52
|
},
|
|
53
53
|
"publishConfig": {
|
|
54
54
|
"access": "public",
|