nexus-ai-pro 1.8.0 → 1.9.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 CHANGED
@@ -6,19 +6,46 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.9.0] - 2026-09-15
10
+
11
+ Install weight, made measurable and enforced. Every export subpath now has a size budget that
12
+ CI holds it to, the README publishes what each import costs, and enforcing a cost budget no
13
+ longer loads the model catalogue. No public API changed.
14
+
15
+ ### Added
16
+
17
+ - **A per-subpath size budget, enforced in CI.** `npm run size:check` measures the transitive import
18
+ graph of every one of the 70 export subpaths and fails when an entry point grows past its recorded
19
+ budget. The package already budgeted the whole tarball, which is exactly why per-entry growth went
20
+ unnoticed across three releases while the total budget was raised three times: a shared module
21
+ pulled into an otherwise small entry point does not move the tarball at all, it only moves what a
22
+ consumer pays to import one piece. `npm run size:update` rewrites the budgets and regenerates the
23
+ README table.
24
+ - **A published size table.** The README now carries what each import actually costs, generated from
25
+ the build rather than written by hand, and the check fails if it goes stale. The claim is now
26
+ verifiable before installing.
27
+
28
+ ### Changed
29
+
30
+ - **Enforcing a cost budget no longer loads the model catalogue.** `assertWithinCostBudget`,
31
+ `CostBudgetError`, `formatCost`, and `DEFAULT_CURRENCY` moved to `optimizer/cost-budget.ts`, which
32
+ has no registry dependency; `optimizer/cost.ts` re-exports all four, so no import changes and the
33
+ error classes stay identical rather than becoming copies. Comparing two numbers had been dragging
34
+ 30 KB of chat-model data along with it.
35
+ - **`nexus-ai-pro/embeddings` is 29% smaller**, 129 KB down to 91 KB, and no longer reaches
36
+ `types/providers.js` at all. A test pins that, not just the budget.
37
+
9
38
  ## [1.8.0] - 2026-09-14
10
39
 
11
40
  Graphs. Nodes, edges, cycles, fan-out and subgraphs over typed state, with every superstep
12
41
  checkpointed so a run is resumable, inspectable and interruptible by construction rather than
13
- after opting in. This closes the five capability rows where LangGraph led, on a subpath that
14
- costs 5% of the root import.
42
+ after opting in, on a subpath that costs 5% of the root import.
15
43
 
16
44
  ### Added
17
45
 
18
46
  - **Typed state graphs.** `nexus-ai-pro/graph` adds nodes, edges, conditional edges, cycles, fan-out,
19
- and subgraphs over typed state channels, closing the five capability rows where LangGraph led:
20
- explicit nodes and edges, persistent graph state, checkpoint and resume, human-in-the-loop
21
- interrupts, and complex branching.
47
+ and subgraphs over typed state channels: explicit nodes and edges, persistent graph state,
48
+ checkpoint and resume, human-in-the-loop interrupts, and complex branching.
22
49
  - **Durable by construction.** Every superstep is checkpointed, so a run is resumable without opting
23
50
  into a checkpointer first. `MemoryGraphCheckpointer` covers a single process;
24
51
  `OperationStoreCheckpointer` persists through the `OperationStore` that already backs durable
@@ -430,7 +457,8 @@ shared policy vocabulary is in place for a future release that revisits this.
430
457
  - URL fetching rejects unsafe private-network targets and limits response reads.
431
458
  - CLI and audit findings no longer disclose detected secret values.
432
459
 
433
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.8.0...HEAD
460
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.9.0...HEAD
461
+ [1.9.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.8.0...v1.9.0
434
462
  [1.8.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.7.0...v1.8.0
435
463
  [1.7.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.6.0...v1.7.0
436
464
  [1.6.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.5.0...v1.6.0
package/README.md CHANGED
@@ -1624,6 +1624,90 @@ import { createTelephonyRealtimeBridge } from 'nexus-ai-pro/telephony/realtime-b
1624
1624
 
1625
1625
  Provider SDKs are optional peer dependencies. The package ships ESM and CommonJS builds, supports Node.js 22+, and is marked with `sideEffects: false`. Only the entry points listed in the package export map are public; deep imports into `dist`, `dist-cjs`, or `src` are unsupported.
1626
1626
 
1627
+ ### What each import actually costs
1628
+
1629
+ Importing one piece loads one piece. The figures below are the transitive import graph of each entry
1630
+ point, generated from the build by `npm run size:check`, which fails CI when an entry point grows
1631
+ past its budget — so the numbers stay true rather than aspirational.
1632
+
1633
+ <!-- size-table:start -->
1634
+ | Import | Size | Share of root |
1635
+ | --- | --- | --- |
1636
+ | `nexus-ai-pro` | 606 KB | 100% |
1637
+ | `nexus-ai-pro/config` | 465 KB | 77% |
1638
+ | `nexus-ai-pro/core` | 459 KB | 76% |
1639
+ | `nexus-ai-pro/realtime` | 157 KB | 26% |
1640
+ | `nexus-ai-pro/batch` | 118 KB | 19% |
1641
+ | `nexus-ai-pro/realtime/session` | 94 KB | 16% |
1642
+ | `nexus-ai-pro/embeddings` | 91 KB | 15% |
1643
+ | `nexus-ai-pro/providers/groq` | 79 KB | 13% |
1644
+ | `nexus-ai-pro/providers/mistral` | 79 KB | 13% |
1645
+ | `nexus-ai-pro/providers/azure-openai` | 78 KB | 13% |
1646
+ | `nexus-ai-pro/providers/openrouter` | 78 KB | 13% |
1647
+ | `nexus-ai-pro/providers/deepseek` | 77 KB | 13% |
1648
+ | `nexus-ai-pro/providers/llamacpp` | 77 KB | 13% |
1649
+ | `nexus-ai-pro/providers/lmstudio` | 77 KB | 13% |
1650
+ | `nexus-ai-pro/providers/openai` | 77 KB | 13% |
1651
+ | `nexus-ai-pro/providers/anthropic` | 70 KB | 12% |
1652
+ | `nexus-ai-pro/providers/google` | 66 KB | 11% |
1653
+ | `nexus-ai-pro/images` | 63 KB | 10% |
1654
+ | `nexus-ai-pro/providers/ollama` | 58 KB | 10% |
1655
+ | `nexus-ai-pro/batch/openai` | 52 KB | 9% |
1656
+ | `nexus-ai-pro/batch/anthropic` | 52 KB | 9% |
1657
+ | `nexus-ai-pro/operations` | 49 KB | 8% |
1658
+ | `nexus-ai-pro/batch/mock` | 47 KB | 8% |
1659
+ | `nexus-ai-pro/providers/cohere` | 46 KB | 8% |
1660
+ | `nexus-ai-pro/realtime/openai-webrtc` | 46 KB | 8% |
1661
+ | `nexus-ai-pro/security` | 40 KB | 7% |
1662
+ | `nexus-ai-pro/models` | 35 KB | 6% |
1663
+ | `nexus-ai-pro/graph` | 29 KB | 5% |
1664
+ | `nexus-ai-pro/realtime/openai-websocket` | 29 KB | 5% |
1665
+ | `nexus-ai-pro/evals` | 23 KB | 4% |
1666
+ | `nexus-ai-pro/images/stores` | 22 KB | 4% |
1667
+ | `nexus-ai-pro/telephony/twilio` | 21 KB | 3% |
1668
+ | `nexus-ai-pro/telephony` | 20 KB | 3% |
1669
+ | `nexus-ai-pro/realtime/mock` | 19 KB | 3% |
1670
+ | `nexus-ai-pro/voice` | 18 KB | 3% |
1671
+ | `nexus-ai-pro/images/openai` | 18 KB | 3% |
1672
+ | `nexus-ai-pro/embeddings/adapters` | 17 KB | 3% |
1673
+ | `nexus-ai-pro/realtime/conversation` | 16 KB | 3% |
1674
+ | `nexus-ai-pro/images/assets` | 14 KB | 2% |
1675
+ | `nexus-ai-pro/context` | 13 KB | 2% |
1676
+ | `nexus-ai-pro/operations/adapters` | 13 KB | 2% |
1677
+ | `nexus-ai-pro/realtime/tools` | 13 KB | 2% |
1678
+ | `nexus-ai-pro/workflows` | 13 KB | 2% |
1679
+ | `nexus-ai-pro/images/mock` | 12 KB | 2% |
1680
+ | `nexus-ai-pro/optimizer` | 11 KB | 2% |
1681
+ | `nexus-ai-pro/voice/session` | 11 KB | 2% |
1682
+ | `nexus-ai-pro/providers` | 10 KB | 2% |
1683
+ | `nexus-ai-pro/providers/base` | 10 KB | 2% |
1684
+ | `nexus-ai-pro/embeddings/models` | 10 KB | 2% |
1685
+ | `nexus-ai-pro/voice/openai` | 9 KB | 1% |
1686
+ | `nexus-ai-pro/ops/circuit-breaker` | 8 KB | 1% |
1687
+ | `nexus-ai-pro/realtime/openai-server` | 8 KB | 1% |
1688
+ | `nexus-ai-pro/capabilities` | 8 KB | 1% |
1689
+ | `nexus-ai-pro/telephony/realtime-bridge` | 7 KB | 1% |
1690
+ | `nexus-ai-pro/providers/errors` | 5 KB | 0.8% |
1691
+ | `nexus-ai-pro/embeddings/mock` | 5 KB | 0.8% |
1692
+ | `nexus-ai-pro/cache/semantic-cache` | 5 KB | 0.8% |
1693
+ | `nexus-ai-pro/evals/judge` | 5 KB | 0.8% |
1694
+ | `nexus-ai-pro/operations/webhooks` | 4 KB | 0.7% |
1695
+ | `nexus-ai-pro/ops/rate-limit-adapters` | 3 KB | 0.5% |
1696
+ | `nexus-ai-pro/cache/memory-cache` | 3 KB | 0.5% |
1697
+ | `nexus-ai-pro/cache` | 2 KB | 0.3% |
1698
+ | `nexus-ai-pro/cache/adapters` | 2 KB | 0.3% |
1699
+ | `nexus-ai-pro/rag` | 2 KB | 0.3% |
1700
+ | `nexus-ai-pro/jobs` | 2 KB | 0.3% |
1701
+ | `nexus-ai-pro/jobs/durable-adapters` | 2 KB | 0.3% |
1702
+ | `nexus-ai-pro/jobs/queue` | 2 KB | 0.3% |
1703
+ | `nexus-ai-pro/streaming` | 1 KB | 0.2% |
1704
+ | `nexus-ai-pro/providers/type-guards` | 1 KB | 0.2% |
1705
+ | `nexus-ai-pro/jobs/batch` | 1 KB | 0.2% |
1706
+ <!-- size-table:end -->
1707
+
1708
+ A capability that only works by importing the whole runtime is treated as a design problem, not an
1709
+ acceptable cost.
1710
+
1627
1711
  ## Examples
1628
1712
 
1629
1713
  ```bash
@@ -3,7 +3,7 @@ import { AuditLogger } from '../ops/audit-logger.js';
3
3
  import { MetricsCollector } from '../ops/metrics.js';
4
4
  import { RateLimiter } from '../ops/rate-limiter.js';
5
5
  import { NexusProviderError, createTimeoutProviderError, isAbortError } from '../providers/errors.js';
6
- import { assertWithinCostBudget } from '../optimizer/cost.js';
6
+ import { assertWithinCostBudget } from '../optimizer/cost-budget.js';
7
7
  import { Tokenizer } from '../utils/tokenizer.js';
8
8
  import { generateRequestId } from '../utils/ids.js';
9
9
  import { EmbeddingCapabilityError, EmbeddingError, EmbeddingProviderError, EmbeddingProviderNotFoundError, EmbeddingProviderResponseError, EmbeddingValidationError, } from './errors.js';
@@ -1,4 +1,4 @@
1
- import { DEFAULT_CURRENCY, formatCost } from '../optimizer/cost.js';
1
+ import { DEFAULT_CURRENCY, formatCost } from '../optimizer/cost-budget.js';
2
2
  /**
3
3
  * Default provenance for bundled embedding entries.
4
4
  *
@@ -0,0 +1,20 @@
1
+ import type { CostEstimate } from '../types/planning.js';
2
+ /**
3
+ * Currency and cost-budget enforcement, with no dependency on the model registry.
4
+ *
5
+ * Split out of `optimizer/cost.ts` because enforcing a budget only compares two numbers, while
6
+ * looking a price up needs the 101-model catalogue. Keeping them in one module meant every family
7
+ * that merely enforces a budget — embeddings among them — dragged 30 KB of model data into its
8
+ * import graph to do arithmetic. `optimizer/cost.ts` re-exports everything here, so no public
9
+ * import changes.
10
+ *
11
+ * Distinct from `optimizer/budget.ts`, which enforces *token* budgets on a request before it is
12
+ * sent. This module deals in money after a usage figure exists.
13
+ */
14
+ export declare const DEFAULT_CURRENCY = "USD";
15
+ export declare class CostBudgetError extends Error {
16
+ constructor(message: string);
17
+ }
18
+ /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
19
+ export declare function formatCost(amount: number): string;
20
+ export declare function assertWithinCostBudget(estimate: CostEstimate, maxEstimatedCost?: number): void;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Currency and cost-budget enforcement, with no dependency on the model registry.
3
+ *
4
+ * Split out of `optimizer/cost.ts` because enforcing a budget only compares two numbers, while
5
+ * looking a price up needs the 101-model catalogue. Keeping them in one module meant every family
6
+ * that merely enforces a budget — embeddings among them — dragged 30 KB of model data into its
7
+ * import graph to do arithmetic. `optimizer/cost.ts` re-exports everything here, so no public
8
+ * import changes.
9
+ *
10
+ * Distinct from `optimizer/budget.ts`, which enforces *token* budgets on a request before it is
11
+ * sent. This module deals in money after a usage figure exists.
12
+ */
13
+ export const DEFAULT_CURRENCY = 'USD';
14
+ export class CostBudgetError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'CostBudgetError';
18
+ }
19
+ }
20
+ /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
21
+ export function formatCost(amount) {
22
+ return `$${amount.toFixed(4)}`;
23
+ }
24
+ export function assertWithinCostBudget(estimate, maxEstimatedCost) {
25
+ if (maxEstimatedCost === undefined)
26
+ return;
27
+ if (estimate.totalCost > maxEstimatedCost) {
28
+ throw new CostBudgetError(`Estimated cost ${estimate.formatted} exceeds maxEstimatedCost $${maxEstimatedCost.toFixed(4)}`);
29
+ }
30
+ }
@@ -1,6 +1,11 @@
1
1
  import type { NexusAIConfig } from '../types/config.js';
2
2
  import type { CostEstimate } from '../types/planning.js';
3
3
  import type { CacheTtl } from '../types/providers.js';
4
+ /**
5
+ * Budget enforcement lives in `optimizer/budget.ts`, which carries no registry dependency, and is
6
+ * re-exported here so every existing import of this module keeps resolving.
7
+ */
8
+ export { CostBudgetError, DEFAULT_CURRENCY, assertWithinCostBudget, formatCost } from './cost-budget.js';
4
9
  export interface CostEstimateInput {
5
10
  model: string;
6
11
  inputTokens: number;
@@ -13,11 +18,4 @@ export interface CostEstimateInput {
13
18
  cacheTtl?: CacheTtl;
14
19
  config?: Pick<NexusAIConfig, 'models'>;
15
20
  }
16
- export declare class CostBudgetError extends Error {
17
- constructor(message: string);
18
- }
19
- export declare const DEFAULT_CURRENCY = "USD";
20
21
  export declare function estimateCost(input: CostEstimateInput): CostEstimate;
21
- /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
22
- export declare function formatCost(amount: number): string;
23
- export declare function assertWithinCostBudget(estimate: CostEstimate, maxEstimatedCost?: number): void;
@@ -1,12 +1,11 @@
1
1
  import { DEFAULT_CACHE_PRICING } from '../types/providers.js';
2
2
  import { resolveModel } from '../models/registry.js';
3
- export class CostBudgetError extends Error {
4
- constructor(message) {
5
- super(message);
6
- this.name = 'CostBudgetError';
7
- }
8
- }
9
- export const DEFAULT_CURRENCY = 'USD';
3
+ import { DEFAULT_CURRENCY, formatCost } from './cost-budget.js';
4
+ /**
5
+ * Budget enforcement lives in `optimizer/budget.ts`, which carries no registry dependency, and is
6
+ * re-exported here so every existing import of this module keeps resolving.
7
+ */
8
+ export { CostBudgetError, DEFAULT_CURRENCY, assertWithinCostBudget, formatCost } from './cost-budget.js';
10
9
  /**
11
10
  * Resolves the per-1k prices for cached reads and cache writes.
12
11
  *
@@ -70,14 +69,3 @@ export function estimateCost(input) {
70
69
  formatted: formatCost(totalCost),
71
70
  };
72
71
  }
73
- /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
74
- export function formatCost(amount) {
75
- return `$${amount.toFixed(4)}`;
76
- }
77
- export function assertWithinCostBudget(estimate, maxEstimatedCost) {
78
- if (maxEstimatedCost === undefined)
79
- return;
80
- if (estimate.totalCost > maxEstimatedCost) {
81
- throw new CostBudgetError(`Estimated cost ${estimate.formatted} exceeds maxEstimatedCost $${maxEstimatedCost.toFixed(4)}`);
82
- }
83
- }
@@ -6,7 +6,7 @@ const audit_logger_js_1 = require("../ops/audit-logger.js");
6
6
  const metrics_js_1 = require("../ops/metrics.js");
7
7
  const rate_limiter_js_1 = require("../ops/rate-limiter.js");
8
8
  const errors_js_1 = require("../providers/errors.js");
9
- const cost_js_1 = require("../optimizer/cost.js");
9
+ const cost_budget_js_1 = require("../optimizer/cost-budget.js");
10
10
  const tokenizer_js_1 = require("../utils/tokenizer.js");
11
11
  const ids_js_1 = require("../utils/ids.js");
12
12
  const errors_js_2 = require("./errors.js");
@@ -438,7 +438,7 @@ class EmbeddingManager {
438
438
  }
439
439
  return;
440
440
  }
441
- (0, cost_js_1.assertWithinCostBudget)(estimate, budget.maxEstimatedCost);
441
+ (0, cost_budget_js_1.assertWithinCostBudget)(estimate, budget.maxEstimatedCost);
442
442
  }
443
443
  // ── Cache ────────────────────────────────────────────────────────
444
444
  cacheContext(route, request) {
@@ -11,7 +11,7 @@ exports.estimateEmbeddingCost = estimateEmbeddingCost;
11
11
  exports.priceEmbeddingUsage = priceEmbeddingUsage;
12
12
  exports.embeddingDimensions = embeddingDimensions;
13
13
  exports.resolveMaxBatchSize = resolveMaxBatchSize;
14
- const cost_js_1 = require("../optimizer/cost.js");
14
+ const cost_budget_js_1 = require("../optimizer/cost-budget.js");
15
15
  /**
16
16
  * Default provenance for bundled embedding entries.
17
17
  *
@@ -228,15 +228,15 @@ function estimateEmbeddingCost(input) {
228
228
  inputCost,
229
229
  outputCost: 0,
230
230
  totalCost: inputCost,
231
- currency: cost_js_1.DEFAULT_CURRENCY,
232
- formatted: (0, cost_js_1.formatCost)(inputCost),
231
+ currency: cost_budget_js_1.DEFAULT_CURRENCY,
232
+ formatted: (0, cost_budget_js_1.formatCost)(inputCost),
233
233
  };
234
234
  }
235
235
  function priceEmbeddingUsage(model, usage, config) {
236
236
  const estimate = estimateEmbeddingCost({ model, inputTokens: usage.inputTokens, config });
237
237
  return {
238
238
  amount: estimate.totalCost,
239
- currency: estimate.currency || cost_js_1.DEFAULT_CURRENCY,
239
+ currency: estimate.currency || cost_budget_js_1.DEFAULT_CURRENCY,
240
240
  basis: 'estimated',
241
241
  input: estimate.inputCost,
242
242
  output: 0,
@@ -0,0 +1,20 @@
1
+ import type { CostEstimate } from '../types/planning.js';
2
+ /**
3
+ * Currency and cost-budget enforcement, with no dependency on the model registry.
4
+ *
5
+ * Split out of `optimizer/cost.ts` because enforcing a budget only compares two numbers, while
6
+ * looking a price up needs the 101-model catalogue. Keeping them in one module meant every family
7
+ * that merely enforces a budget — embeddings among them — dragged 30 KB of model data into its
8
+ * import graph to do arithmetic. `optimizer/cost.ts` re-exports everything here, so no public
9
+ * import changes.
10
+ *
11
+ * Distinct from `optimizer/budget.ts`, which enforces *token* budgets on a request before it is
12
+ * sent. This module deals in money after a usage figure exists.
13
+ */
14
+ export declare const DEFAULT_CURRENCY = "USD";
15
+ export declare class CostBudgetError extends Error {
16
+ constructor(message: string);
17
+ }
18
+ /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
19
+ export declare function formatCost(amount: number): string;
20
+ export declare function assertWithinCostBudget(estimate: CostEstimate, maxEstimatedCost?: number): void;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CostBudgetError = exports.DEFAULT_CURRENCY = void 0;
4
+ exports.formatCost = formatCost;
5
+ exports.assertWithinCostBudget = assertWithinCostBudget;
6
+ /**
7
+ * Currency and cost-budget enforcement, with no dependency on the model registry.
8
+ *
9
+ * Split out of `optimizer/cost.ts` because enforcing a budget only compares two numbers, while
10
+ * looking a price up needs the 101-model catalogue. Keeping them in one module meant every family
11
+ * that merely enforces a budget — embeddings among them — dragged 30 KB of model data into its
12
+ * import graph to do arithmetic. `optimizer/cost.ts` re-exports everything here, so no public
13
+ * import changes.
14
+ *
15
+ * Distinct from `optimizer/budget.ts`, which enforces *token* budgets on a request before it is
16
+ * sent. This module deals in money after a usage figure exists.
17
+ */
18
+ exports.DEFAULT_CURRENCY = 'USD';
19
+ class CostBudgetError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = 'CostBudgetError';
23
+ }
24
+ }
25
+ exports.CostBudgetError = CostBudgetError;
26
+ /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
27
+ function formatCost(amount) {
28
+ return `$${amount.toFixed(4)}`;
29
+ }
30
+ function assertWithinCostBudget(estimate, maxEstimatedCost) {
31
+ if (maxEstimatedCost === undefined)
32
+ return;
33
+ if (estimate.totalCost > maxEstimatedCost) {
34
+ throw new CostBudgetError(`Estimated cost ${estimate.formatted} exceeds maxEstimatedCost $${maxEstimatedCost.toFixed(4)}`);
35
+ }
36
+ }
@@ -1,6 +1,11 @@
1
1
  import type { NexusAIConfig } from '../types/config.js';
2
2
  import type { CostEstimate } from '../types/planning.js';
3
3
  import type { CacheTtl } from '../types/providers.js';
4
+ /**
5
+ * Budget enforcement lives in `optimizer/budget.ts`, which carries no registry dependency, and is
6
+ * re-exported here so every existing import of this module keeps resolving.
7
+ */
8
+ export { CostBudgetError, DEFAULT_CURRENCY, assertWithinCostBudget, formatCost } from './cost-budget.js';
4
9
  export interface CostEstimateInput {
5
10
  model: string;
6
11
  inputTokens: number;
@@ -13,11 +18,4 @@ export interface CostEstimateInput {
13
18
  cacheTtl?: CacheTtl;
14
19
  config?: Pick<NexusAIConfig, 'models'>;
15
20
  }
16
- export declare class CostBudgetError extends Error {
17
- constructor(message: string);
18
- }
19
- export declare const DEFAULT_CURRENCY = "USD";
20
21
  export declare function estimateCost(input: CostEstimateInput): CostEstimate;
21
- /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
22
- export declare function formatCost(amount: number): string;
23
- export declare function assertWithinCostBudget(estimate: CostEstimate, maxEstimatedCost?: number): void;
@@ -1,19 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_CURRENCY = exports.CostBudgetError = void 0;
3
+ exports.formatCost = exports.assertWithinCostBudget = exports.DEFAULT_CURRENCY = exports.CostBudgetError = void 0;
4
4
  exports.estimateCost = estimateCost;
5
- exports.formatCost = formatCost;
6
- exports.assertWithinCostBudget = assertWithinCostBudget;
7
5
  const providers_js_1 = require("../types/providers.js");
8
6
  const registry_js_1 = require("../models/registry.js");
9
- class CostBudgetError extends Error {
10
- constructor(message) {
11
- super(message);
12
- this.name = 'CostBudgetError';
13
- }
14
- }
15
- exports.CostBudgetError = CostBudgetError;
16
- exports.DEFAULT_CURRENCY = 'USD';
7
+ const cost_budget_js_1 = require("./cost-budget.js");
8
+ /**
9
+ * Budget enforcement lives in `optimizer/budget.ts`, which carries no registry dependency, and is
10
+ * re-exported here so every existing import of this module keeps resolving.
11
+ */
12
+ var cost_budget_js_2 = require("./cost-budget.js");
13
+ Object.defineProperty(exports, "CostBudgetError", { enumerable: true, get: function () { return cost_budget_js_2.CostBudgetError; } });
14
+ Object.defineProperty(exports, "DEFAULT_CURRENCY", { enumerable: true, get: function () { return cost_budget_js_2.DEFAULT_CURRENCY; } });
15
+ Object.defineProperty(exports, "assertWithinCostBudget", { enumerable: true, get: function () { return cost_budget_js_2.assertWithinCostBudget; } });
16
+ Object.defineProperty(exports, "formatCost", { enumerable: true, get: function () { return cost_budget_js_2.formatCost; } });
17
17
  /**
18
18
  * Resolves the per-1k prices for cached reads and cache writes.
19
19
  *
@@ -73,18 +73,7 @@ function estimateCost(input) {
73
73
  cachedReadCost: cachedReadTokens ? cachedReadCost : undefined,
74
74
  cachedWriteCost: cachedWriteTokens ? cachedWriteCost : undefined,
75
75
  totalCost,
76
- currency: exports.DEFAULT_CURRENCY,
77
- formatted: formatCost(totalCost),
76
+ currency: cost_budget_js_1.DEFAULT_CURRENCY,
77
+ formatted: (0, cost_budget_js_1.formatCost)(totalCost),
78
78
  };
79
79
  }
80
- /** Formats an amount the way `ResponseMeta.estimatedCost` has always presented it. */
81
- function formatCost(amount) {
82
- return `$${amount.toFixed(4)}`;
83
- }
84
- function assertWithinCostBudget(estimate, maxEstimatedCost) {
85
- if (maxEstimatedCost === undefined)
86
- return;
87
- if (estimate.totalCost > maxEstimatedCost) {
88
- throw new CostBudgetError(`Estimated cost ${estimate.formatted} exceeds maxEstimatedCost $${maxEstimatedCost.toFixed(4)}`);
89
- }
90
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexus-ai-pro",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "A typed multi-provider AI pipeline for Node.js with routing, streaming, tools, and guardrails",
5
5
  "type": "module",
6
6
  "main": "dist-cjs/index.js",
@@ -726,6 +726,8 @@
726
726
  "example:optimizer": "tsx examples/optimizer.ts",
727
727
  "example:agent": "tsx examples/agent.ts",
728
728
  "example:realtime": "tsx examples/realtime-scheduling.ts",
729
+ "size:check": "node scripts/check-subpath-sizes.mjs",
730
+ "size:update": "node scripts/check-subpath-sizes.mjs --update",
729
731
  "registry:generate": "node scripts/generate-model-registry.mjs",
730
732
  "registry:check": "node scripts/generate-model-registry.mjs --check",
731
733
  "lint": "biome lint .",
@@ -735,7 +737,7 @@
735
737
  "typecheck:tests": "tsc -p tsconfig.tests.json --noEmit",
736
738
  "typecheck:examples": "tsc -p tsconfig.examples.json --noEmit",
737
739
  "typecheck:examples:browser": "tsc -p tsconfig.examples.browser.json --noEmit",
738
- "test": "npm run build && npm run test:unit && npm run test:conformance:mock && npm run test:smoke && npm run test:api && npm run test:types",
740
+ "test": "npm run build && npm run test:unit && npm run test:conformance:mock && npm run size:check && npm run test:smoke && npm run test:api && npm run test:types",
739
741
  "test:unit": "node --import tsx --test tests/unit.ts tests/*.test.ts",
740
742
  "test:coverage": "node --import tsx --test --experimental-test-coverage --test-coverage-include=src/**/*.ts --test-coverage-lines=82 --test-coverage-branches=67 --test-coverage-functions=73 tests/unit.ts tests/*.test.ts",
741
743
  "test:smoke": "node tests/package-smoke.mjs",