@absolutejs/ai 0.1.3 → 0.2.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,6 +6,13 @@ This file is generated by `absolute-changelog` from the entries in
6
6
  `changelog/`. Edit an entry, not this file — and add new ones under
7
7
  `changelog/unreleased/`.
8
8
 
9
+ ## 0.2.0 — 2026-09-16
10
+
11
+ ### Added
12
+
13
+ - **Expose each tool-loop turn response metadata so consumers can price the actual served processing tier** (`streamAIWithTools`, `StreamAIWithToolsEvent`)
14
+ - **Route exact model IDs to the same provider for token counting, capacity checks, preparation and generation** (`createAIProviderRouter`)
15
+
9
16
  ## 0.1.3 — 2026-09-16
10
17
 
11
18
  ### Fixed
package/README.md CHANGED
@@ -496,3 +496,21 @@ its explicit `false` remains an opt-out unless `contextPolicy` is supplied.
496
496
  Applications should surface typed failures with saved-input retry UI. These
497
497
  changes do not guarantee detection of every provider-side accounting discrepancy
498
498
  or provide exactly-once external execution after a process crash.
499
+
500
+ ### Models from different providers
501
+
502
+ `createAIProviderRouter` routes exact model IDs for generation, token counting and
503
+ capacity checks. It snapshots the route map and fails explicitly for unknown
504
+ models or a route without counting support. It does not choose a model or add a
505
+ fallback. For example, preparation and final answering can use different vendors:
506
+
507
+ ```ts
508
+ const provider = createAIProviderRouter({
509
+ "claude-haiku-4-5-20251001": anthropicProvider,
510
+ "gpt-5.6-luna": openaiResponsesProvider,
511
+ });
512
+ ```
513
+
514
+ `streamAIWithTools` includes each completed turn's provider response `metadata`
515
+ on its `turn` event. Price that turn using the returned `serviceTier`, when
516
+ available, rather than assuming that the requested processing tier was served.
package/changelog.json CHANGED
@@ -2,6 +2,27 @@
2
2
  "contract": 1,
3
3
  "name": "@absolutejs/ai",
4
4
  "releases": [
5
+ {
6
+ "changes": [
7
+ {
8
+ "kind": "added",
9
+ "summary": "Expose each tool-loop turn response metadata so consumers can price the actual served processing tier",
10
+ "symbols": [
11
+ "streamAIWithTools",
12
+ "StreamAIWithToolsEvent"
13
+ ]
14
+ },
15
+ {
16
+ "kind": "added",
17
+ "summary": "Route exact model IDs to the same provider for token counting, capacity checks, preparation and generation",
18
+ "symbols": [
19
+ "createAIProviderRouter"
20
+ ]
21
+ }
22
+ ],
23
+ "date": "2026-09-16",
24
+ "version": "0.2.0"
25
+ },
5
26
  {
6
27
  "changes": [
7
28
  {
package/dist/ai/index.js CHANGED
@@ -5832,6 +5832,7 @@ var streamAIWithTools = async function* (options) {
5832
5832
  let thinking = null;
5833
5833
  let stopReason;
5834
5834
  let turnUsage;
5835
+ let turnMetadata;
5835
5836
  let completed = false;
5836
5837
  for await (const chunk of stream) {
5837
5838
  if (base.signal?.aborted)
@@ -5877,19 +5878,30 @@ var streamAIWithTools = async function* (options) {
5877
5878
  thinking = flushThinking3(blocks, thinking);
5878
5879
  stopReason = chunk.stopReason;
5879
5880
  turnUsage = chunk.usage;
5881
+ turnMetadata = chunk.metadata;
5880
5882
  completed = true;
5881
5883
  }
5882
5884
  }
5883
5885
  thinking = flushThinking3(blocks, thinking);
5884
5886
  if (policy !== false && !completed)
5885
5887
  throw new AIInputError("capacity_unavailable", "The model response was interrupted before completion. Preserve the original input for retry.");
5886
- return { blocks, pending, stopReason, usage: turnUsage };
5888
+ return {
5889
+ blocks,
5890
+ pending,
5891
+ stopReason,
5892
+ usage: turnUsage,
5893
+ metadata: turnMetadata
5894
+ };
5887
5895
  };
5888
5896
  while (turn < maxTurns) {
5889
5897
  turn += 1;
5890
5898
  const outcome = yield* streamOneTurn();
5891
5899
  usage = mergeUsage(usage, outcome.usage);
5892
- yield { type: "turn", usage: outcome.usage };
5900
+ yield {
5901
+ type: "turn",
5902
+ usage: outcome.usage,
5903
+ ...outcome.metadata ? { metadata: outcome.metadata } : {}
5904
+ };
5893
5905
  const { blocks, pending } = outcome;
5894
5906
  allToolCalls.push(...pending);
5895
5907
  if (outcome.stopReason === "pause_turn" && turn < maxTurns && base.signal?.aborted !== true) {
@@ -5939,7 +5951,11 @@ var streamAIWithTools = async function* (options) {
5939
5951
  turn += 1;
5940
5952
  const final = yield* streamOneTurn("none");
5941
5953
  usage = mergeUsage(usage, final.usage);
5942
- yield { type: "turn", usage: final.usage };
5954
+ yield {
5955
+ type: "turn",
5956
+ usage: final.usage,
5957
+ ...final.metadata ? { metadata: final.metadata } : {}
5958
+ };
5943
5959
  allToolCalls.push(...final.pending);
5944
5960
  break;
5945
5961
  }
@@ -8597,6 +8613,30 @@ var createAIStoredToolResultRecovery = (toolNames) => async ({ params }) => {
8597
8613
  })
8598
8614
  }));
8599
8615
  };
8616
+ // src/ai/providerRouter.ts
8617
+ var createAIProviderRouter = (routes) => {
8618
+ const providers = new Map(Object.entries(routes));
8619
+ const resolve = (params) => {
8620
+ params.signal?.throwIfAborted();
8621
+ const provider = providers.get(params.model);
8622
+ if (!provider)
8623
+ throw new AIInputError("capacity_unavailable", `No provider route for model ${params.model}.`);
8624
+ return provider;
8625
+ };
8626
+ const capacity = (params) => {
8627
+ const result = resolve(params).inputCapacity;
8628
+ if (!result)
8629
+ throw new AIInputError("capacity_unavailable", `The provider for ${params.model} does not expose model capacity and token counting.`);
8630
+ return result;
8631
+ };
8632
+ return {
8633
+ inputCapacity: {
8634
+ getLimits: async (params) => capacity(params).getLimits(params),
8635
+ countTokens: async (params) => capacity(params).countTokens(params)
8636
+ },
8637
+ stream: (params) => resolve(params).stream(params)
8638
+ };
8639
+ };
8600
8640
  export {
8601
8641
  AIInputError,
8602
8642
  BUILTIN_UI_CARDS,
@@ -8630,6 +8670,7 @@ export {
8630
8670
  configureProviderResilience,
8631
8671
  confirmCard,
8632
8672
  createAIConnection,
8673
+ createAIProviderRouter,
8633
8674
  createAIStoredToolResultRecovery,
8634
8675
  createAIStream,
8635
8676
  createAITextSource,
@@ -8706,5 +8747,5 @@ export {
8706
8747
  xai
8707
8748
  };
8708
8749
 
8709
- //# debugId=7535B0C2EE1EFEA864756E2164756E21
8750
+ //# debugId=0CA4631EC8525EB564756E2164756E21
8710
8751
  //# sourceMappingURL=index.js.map