@absolutejs/ai 0.1.1 → 0.1.3

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,18 @@ 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.1.3 — 2026-09-16
10
+
11
+ ### Fixed
12
+
13
+ - **Exclude OpenAI cache-write tokens from ordinary input so cache-aware billing never charges the same tokens twice** (`openai`, `openaiResponses`)
14
+
15
+ ## 0.1.2 — 2026-09-16
16
+
17
+ ### Fixed
18
+
19
+ - **Preserve every parallel OpenAI tool result and subsequent conversation turn without overwriting expanded messages** (`openai`)
20
+
9
21
  ## 0.1.1 — 2026-09-16
10
22
 
11
23
  ### Added
package/changelog.json CHANGED
@@ -1,121 +1,148 @@
1
1
  {
2
- "contract": 1,
3
- "name": "@absolutejs/ai",
4
- "releases": [
5
- {
6
- "changes": [
7
- {
8
- "kind": "added",
9
- "summary": "Allow a separate preparation model with its own verified capacity while preserving the final answer model and invalidating notes when the preparation model changes",
10
- "symbols": [
11
- "PrepareAITextInputOptions",
12
- "prepareAITextInput",
13
- "prepareAITextInputStep"
14
- ]
15
- },
16
- {
17
- "kind": "fixed",
18
- "summary": "Fill preparation sections with bounded tokenizer-checked refinement, avoid duplicate final counting, and honor cancellation before generation",
19
- "symbols": [
20
- "prepareAITextInput",
21
- "prepareAITextInputStep"
22
- ]
23
- }
24
- ],
25
- "date": "2026-09-16",
26
- "version": "0.1.1"
27
- },
28
- {
29
- "changes": [
30
- {
31
- "kind": "changed",
32
- "summary": "Validate model capacity by default across generation, structured output, WebSocket and SSE entry points; reserve token headroom and support one protocol-preserving context recovery attempt. Providers without counting capability now fail explicitly; contextPolicy: false opts into raw behavior.",
33
- "symbols": [
34
- "generateAI",
35
- "generateAIWithTools",
36
- "generateObjectAI",
37
- "streamAI",
38
- "streamAIToSSE",
39
- "streamAIWithTools",
40
- "inspectAIContext",
41
- "createAIStoredToolResultRecovery",
42
- "StreamAIWithToolsOptions",
43
- "GenerateAIOptions",
44
- "GenerateAIResult",
45
- "GenerateObjectAIOptions",
46
- "AIProviderConfig",
47
- "StreamAIOptions",
48
- "AIChatPluginConfig"
49
- ]
50
- },
51
- {
52
- "kind": "added",
53
- "summary": "Add one-section text preparation steps for durable workers, with explicit pending/ready outcomes and checkpoint persistence before yielding",
54
- "symbols": [
55
- "prepareAITextInputStep",
56
- "AITextPreparationStep"
57
- ]
58
- },
59
- {
60
- "kind": "added",
61
- "summary": "Budget final retrieval instructions and tools before compacting text, with matching checkpoint invalidation",
62
- "symbols": [
63
- "PrepareAITextInputOptions",
64
- "prepareAITextInput"
65
- ]
66
- },
67
- {
68
- "kind": "fixed",
69
- "summary": "Preserve conversation origin across long-text preparation sections, distinguish source facts from embedded instructions, reserve more of the available context for factual notes, and invalidate checkpoints from the previous preparation policy",
70
- "symbols": [
71
- "prepareAITextInput",
72
- "prepareAITextInputStep"
73
- ]
74
- }
75
- ],
76
- "date": "2026-09-16",
77
- "version": "0.1.0"
78
- },
79
- {
80
- "changes": [
81
- {
82
- "kind": "added",
83
- "summary": "Keep compacted text useful with read-only original-source search, checkpointed ingestion, and capacity-checked terminal tool loops",
84
- "symbols": [
85
- "createAITextSource",
86
- "prepareAITextInput",
87
- "PrepareAITextInputOptions",
88
- "streamAIWithTools",
89
- "StreamAIWithToolsOptions"
90
- ]
91
- }
92
- ],
93
- "date": "2026-09-15",
94
- "version": "0.0.58"
95
- },
96
- {
97
- "changes": [
98
- {
99
- "kind": "internal",
100
- "summary": "Regenerate the OpenRouter client declaration with equivalent reordered response union types",
101
- "symbols": [
102
- "createOpenRouterClient"
103
- ]
104
- },
105
- {
106
- "kind": "added",
107
- "summary": "Add model-specific token capacity, optional provider capacity configuration, and recoverable long-text preparation",
108
- "symbols": [
109
- "inspectAIInput",
110
- "prepareAITextInput",
111
- "AIProviderConfig",
112
- "OpenAIConfig",
113
- "OpenAIResponsesConfig"
114
- ]
115
- }
116
- ],
117
- "date": "2026-09-15",
118
- "version": "0.0.57"
119
- }
120
- ]
2
+ "contract": 1,
3
+ "name": "@absolutejs/ai",
4
+ "releases": [
5
+ {
6
+ "changes": [
7
+ {
8
+ "kind": "fixed",
9
+ "summary": "Exclude OpenAI cache-write tokens from ordinary input so cache-aware billing never charges the same tokens twice",
10
+ "symbols": [
11
+ "openai",
12
+ "openaiResponses"
13
+ ]
14
+ }
15
+ ],
16
+ "date": "2026-09-16",
17
+ "version": "0.1.3"
18
+ },
19
+ {
20
+ "changes": [
21
+ {
22
+ "kind": "fixed",
23
+ "summary": "Preserve every parallel OpenAI tool result and subsequent conversation turn without overwriting expanded messages",
24
+ "symbols": [
25
+ "openai"
26
+ ]
27
+ }
28
+ ],
29
+ "date": "2026-09-16",
30
+ "version": "0.1.2"
31
+ },
32
+ {
33
+ "changes": [
34
+ {
35
+ "kind": "added",
36
+ "summary": "Allow a separate preparation model with its own verified capacity while preserving the final answer model and invalidating notes when the preparation model changes",
37
+ "symbols": [
38
+ "PrepareAITextInputOptions",
39
+ "prepareAITextInput",
40
+ "prepareAITextInputStep"
41
+ ]
42
+ },
43
+ {
44
+ "kind": "fixed",
45
+ "summary": "Fill preparation sections with bounded tokenizer-checked refinement, avoid duplicate final counting, and honor cancellation before generation",
46
+ "symbols": [
47
+ "prepareAITextInput",
48
+ "prepareAITextInputStep"
49
+ ]
50
+ }
51
+ ],
52
+ "date": "2026-09-16",
53
+ "version": "0.1.1"
54
+ },
55
+ {
56
+ "changes": [
57
+ {
58
+ "kind": "changed",
59
+ "summary": "Validate model capacity by default across generation, structured output, WebSocket and SSE entry points; reserve token headroom and support one protocol-preserving context recovery attempt. Providers without counting capability now fail explicitly; contextPolicy: false opts into raw behavior.",
60
+ "symbols": [
61
+ "generateAI",
62
+ "generateAIWithTools",
63
+ "generateObjectAI",
64
+ "streamAI",
65
+ "streamAIToSSE",
66
+ "streamAIWithTools",
67
+ "inspectAIContext",
68
+ "createAIStoredToolResultRecovery",
69
+ "StreamAIWithToolsOptions",
70
+ "GenerateAIOptions",
71
+ "GenerateAIResult",
72
+ "GenerateObjectAIOptions",
73
+ "AIProviderConfig",
74
+ "StreamAIOptions",
75
+ "AIChatPluginConfig"
76
+ ]
77
+ },
78
+ {
79
+ "kind": "added",
80
+ "summary": "Add one-section text preparation steps for durable workers, with explicit pending/ready outcomes and checkpoint persistence before yielding",
81
+ "symbols": [
82
+ "prepareAITextInputStep",
83
+ "AITextPreparationStep"
84
+ ]
85
+ },
86
+ {
87
+ "kind": "added",
88
+ "summary": "Budget final retrieval instructions and tools before compacting text, with matching checkpoint invalidation",
89
+ "symbols": [
90
+ "PrepareAITextInputOptions",
91
+ "prepareAITextInput"
92
+ ]
93
+ },
94
+ {
95
+ "kind": "fixed",
96
+ "summary": "Preserve conversation origin across long-text preparation sections, distinguish source facts from embedded instructions, reserve more of the available context for factual notes, and invalidate checkpoints from the previous preparation policy",
97
+ "symbols": [
98
+ "prepareAITextInput",
99
+ "prepareAITextInputStep"
100
+ ]
101
+ }
102
+ ],
103
+ "date": "2026-09-16",
104
+ "version": "0.1.0"
105
+ },
106
+ {
107
+ "changes": [
108
+ {
109
+ "kind": "added",
110
+ "summary": "Keep compacted text useful with read-only original-source search, checkpointed ingestion, and capacity-checked terminal tool loops",
111
+ "symbols": [
112
+ "createAITextSource",
113
+ "prepareAITextInput",
114
+ "PrepareAITextInputOptions",
115
+ "streamAIWithTools",
116
+ "StreamAIWithToolsOptions"
117
+ ]
118
+ }
119
+ ],
120
+ "date": "2026-09-15",
121
+ "version": "0.0.58"
122
+ },
123
+ {
124
+ "changes": [
125
+ {
126
+ "kind": "internal",
127
+ "summary": "Regenerate the OpenRouter client declaration with equivalent reordered response union types",
128
+ "symbols": [
129
+ "createOpenRouterClient"
130
+ ]
131
+ },
132
+ {
133
+ "kind": "added",
134
+ "summary": "Add model-specific token capacity, optional provider capacity configuration, and recoverable long-text preparation",
135
+ "symbols": [
136
+ "inspectAIInput",
137
+ "prepareAITextInput",
138
+ "AIProviderConfig",
139
+ "OpenAIConfig",
140
+ "OpenAIResponsesConfig"
141
+ ]
142
+ }
143
+ ],
144
+ "date": "2026-09-15",
145
+ "version": "0.0.57"
146
+ }
147
+ ]
121
148
  }
package/dist/ai/index.js CHANGED
@@ -877,11 +877,12 @@ var extractUsage = (response) => {
877
877
  const outputDetails = isRecord(usage.output_tokens_details) ? usage.output_tokens_details : undefined;
878
878
  const inputDetails = isRecord(usage.input_tokens_details) ? usage.input_tokens_details : undefined;
879
879
  const costDetails = isRecord(usage.cost_details) ? usage.cost_details : undefined;
880
+ const written = inputDetails && typeof inputDetails.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : 0;
880
881
  const normalized = {
881
882
  cacheReadInputTokens: cached,
882
- cacheWriteInputTokens: inputDetails && typeof inputDetails.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : undefined,
883
+ cacheWriteInputTokens: written || undefined,
883
884
  costCredits: typeof usage.cost === "number" ? usage.cost : undefined,
884
- inputTokens: Math.max(0, input - cached),
885
+ inputTokens: Math.max(0, input - cached - written),
885
886
  outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
886
887
  reasoningTokens: outputDetails && typeof outputDetails.reasoning_tokens === "number" ? outputDetails.reasoning_tokens : undefined,
887
888
  upstreamInferenceCostCredits: costDetails && typeof costDetails.upstream_inference_cost === "number" ? costDetails.upstream_inference_cost : undefined
@@ -1262,30 +1263,6 @@ var buildToolMessages = (blocks) => {
1262
1263
  }
1263
1264
  return messages;
1264
1265
  };
1265
- var processMessageAtIndex = (result, msg, idx) => {
1266
- if (!hasArrayContent(msg)) {
1267
- return;
1268
- }
1269
- const hasToolBlocks2 = msg.content.some((block) => block.type === "tool_use" || block.type === "tool_result");
1270
- if (!hasToolBlocks2) {
1271
- return;
1272
- }
1273
- const toolMessages = buildToolMessages(msg.content);
1274
- result.splice(idx, 1, ...toolMessages);
1275
- };
1276
- var convertSingleMessage = (result, msg, idx) => {
1277
- if (!msg) {
1278
- return;
1279
- }
1280
- processMessageAtIndex(result, msg, idx);
1281
- };
1282
- var convertToolResultMessages = (messages, params) => {
1283
- const result = [...messages];
1284
- for (let idx = 0;idx < params.messages.length; idx++) {
1285
- convertSingleMessage(result, params.messages[idx], idx);
1286
- }
1287
- return result;
1288
- };
1289
1266
  var mapToolDefinitions = (tools) => tools.map((tool) => ({
1290
1267
  function: {
1291
1268
  description: tool.description,
@@ -1342,10 +1319,11 @@ var mapOpenAIContent = (msg) => {
1342
1319
  return msg.content.map(mapContentBlockToOpenAI).filter((mapped) => mapped !== null);
1343
1320
  };
1344
1321
  var buildRequestBody = (params, capabilityModel = params.model) => {
1345
- const messages = convertToolResultMessages(params.messages.map((msg) => ({
1346
- content: mapOpenAIContent(msg),
1347
- role: msg.role
1348
- })), params);
1322
+ const messages = params.messages.flatMap((msg) => {
1323
+ if (hasArrayContent(msg) && msg.content.some((block) => block.type === "tool_use" || block.type === "tool_result"))
1324
+ return buildToolMessages(msg.content);
1325
+ return [{ content: mapOpenAIContent(msg), role: msg.role }];
1326
+ });
1349
1327
  if (params.systemPrompt) {
1350
1328
  messages.unshift({ content: params.systemPrompt, role: "system" });
1351
1329
  }
@@ -1433,11 +1411,12 @@ var flushPendingToolCalls = function* (pendingToolCalls) {
1433
1411
  var extractUsage2 = (parsedUsage) => {
1434
1412
  const prompt = parsedUsage.prompt_tokens ?? 0;
1435
1413
  const cached = parsedUsage.cached_tokens ?? 0;
1414
+ const written = parsedUsage.cache_write_tokens ?? 0;
1436
1415
  return {
1437
1416
  cacheReadInputTokens: cached,
1438
- cacheWriteInputTokens: parsedUsage.cache_write_tokens || undefined,
1417
+ cacheWriteInputTokens: written || undefined,
1439
1418
  costCredits: parsedUsage.cost,
1440
- inputTokens: Math.max(0, prompt - cached),
1419
+ inputTokens: Math.max(0, prompt - cached - written),
1441
1420
  outputTokens: parsedUsage.completion_tokens ?? 0,
1442
1421
  reasoningTokens: parsedUsage.reasoning_tokens || undefined,
1443
1422
  upstreamInferenceCostCredits: parsedUsage.upstream_inference_cost || undefined
@@ -1882,7 +1861,7 @@ var convertMessageContent = (content) => {
1882
1861
  return content.map(mapContentBlock).filter((mapped) => mapped !== null);
1883
1862
  };
1884
1863
  var hasFunctionResponse = (content) => content.some((block) => block.type === "tool_result");
1885
- var convertSingleMessage2 = (msg) => {
1864
+ var convertSingleMessage = (msg) => {
1886
1865
  if (typeof msg.content !== "string" && hasFunctionResponse(msg.content)) {
1887
1866
  return { parts: convertMessageContent(msg.content), role: "user" };
1888
1867
  }
@@ -1891,7 +1870,7 @@ var convertSingleMessage2 = (msg) => {
1891
1870
  role: mapRole(msg.role)
1892
1871
  };
1893
1872
  };
1894
- var convertMessages = (messages) => messages.map(convertSingleMessage2);
1873
+ var convertMessages = (messages) => messages.map(convertSingleMessage);
1895
1874
  var mapToolDefinitions2 = (tools) => tools.map((tool) => ({
1896
1875
  description: tool.description,
1897
1876
  name: tool.name,
@@ -8727,5 +8706,5 @@ export {
8727
8706
  xai
8728
8707
  };
8729
8708
 
8730
- //# debugId=0E6BB4F28F8F0BB064756E2164756E21
8709
+ //# debugId=7535B0C2EE1EFEA864756E2164756E21
8731
8710
  //# sourceMappingURL=index.js.map