veryfront 0.1.966 → 0.1.969

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.
Files changed (56) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/extensions/ext-llm-anthropic/src/anthropic-stream.d.ts +5 -1
  3. package/esm/extensions/ext-llm-anthropic/src/anthropic-stream.d.ts.map +1 -1
  4. package/esm/extensions/ext-llm-anthropic/src/anthropic-stream.js +282 -180
  5. package/esm/extensions/ext-llm-openai/src/openai-chat-stream.d.ts.map +1 -1
  6. package/esm/extensions/ext-llm-openai/src/openai-chat-stream.js +3 -1
  7. package/esm/extensions/ext-llm-openai/src/openai-provider.d.ts.map +1 -1
  8. package/esm/extensions/ext-llm-openai/src/openai-provider.js +3 -1
  9. package/esm/extensions/ext-llm-openai/src/openai-responses-stream.d.ts +1 -22
  10. package/esm/extensions/ext-llm-openai/src/openai-responses-stream.d.ts.map +1 -1
  11. package/esm/extensions/ext-llm-openai/src/openai-responses-stream.js +3 -1
  12. package/esm/src/agent/ag-ui/browser-chunk-encoder.d.ts.map +1 -1
  13. package/esm/src/agent/ag-ui/browser-chunk-encoder.js +5 -0
  14. package/esm/src/agent/ag-ui/browser-encoder.d.ts +1 -0
  15. package/esm/src/agent/ag-ui/browser-encoder.d.ts.map +1 -1
  16. package/esm/src/agent/ag-ui/browser-encoder.js +11 -0
  17. package/esm/src/agent/ag-ui/browser-finalize-tracker.d.ts.map +1 -1
  18. package/esm/src/agent/ag-ui/browser-finalize-tracker.js +6 -0
  19. package/esm/src/agent/runtime/chat-stream-handler.d.ts +2 -0
  20. package/esm/src/agent/runtime/chat-stream-handler.d.ts.map +1 -1
  21. package/esm/src/agent/runtime/chat-stream-handler.js +3 -0
  22. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  23. package/esm/src/agent/runtime/index.js +1 -0
  24. package/esm/src/agent/runtime/input-utils.d.ts +2 -0
  25. package/esm/src/agent/runtime/input-utils.d.ts.map +1 -1
  26. package/esm/src/agent/runtime/input-utils.js +5 -0
  27. package/esm/src/agent/runtime/runtime-tool-types.d.ts +2 -0
  28. package/esm/src/agent/runtime/runtime-tool-types.d.ts.map +1 -1
  29. package/esm/src/agent/schemas/agent.schema.d.ts +1 -0
  30. package/esm/src/agent/schemas/agent.schema.d.ts.map +1 -1
  31. package/esm/src/agent/schemas/agent.schema.js +1 -0
  32. package/esm/src/eval/agent-service.d.ts.map +1 -1
  33. package/esm/src/eval/agent-service.js +5 -0
  34. package/esm/src/eval/model-comparison.d.ts.map +1 -1
  35. package/esm/src/eval/model-comparison.js +16 -12
  36. package/esm/src/eval/report.d.ts.map +1 -1
  37. package/esm/src/eval/report.js +14 -0
  38. package/esm/src/eval/studio.d.ts +2 -0
  39. package/esm/src/eval/studio.d.ts.map +1 -1
  40. package/esm/src/eval/studio.js +1 -0
  41. package/esm/src/eval/types.d.ts +5 -0
  42. package/esm/src/eval/types.d.ts.map +1 -1
  43. package/esm/src/provider/runtime-loader/provider-usage.d.ts +5 -0
  44. package/esm/src/provider/runtime-loader/provider-usage.d.ts.map +1 -1
  45. package/esm/src/provider/runtime-loader/provider-usage.js +8 -0
  46. package/esm/src/provider/runtime-loader.d.ts +2 -2
  47. package/esm/src/provider/runtime-loader.d.ts.map +1 -1
  48. package/esm/src/provider/runtime-loader.js +2 -2
  49. package/esm/src/provider/shared/index.d.ts +1 -1
  50. package/esm/src/provider/shared/index.d.ts.map +1 -1
  51. package/esm/src/provider/shared/index.js +1 -1
  52. package/esm/src/runtime/runtime-bridge.d.ts.map +1 -1
  53. package/esm/src/runtime/runtime-bridge.js +4 -0
  54. package/esm/src/utils/version-constant.d.ts +1 -1
  55. package/esm/src/utils/version-constant.js +1 -1
  56. package/package.json +1 -1
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.966",
3
+ "version": "0.1.969",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -1,8 +1,12 @@
1
1
  import type { RuntimeUsage } from "../../../src/provider/shared/index.js";
2
+ type AnthropicStreamOptions = {
3
+ clientToolUseTrailingUsageGraceMs?: number;
4
+ };
2
5
  export declare function normalizeAnthropicFinishReason(raw: unknown): string | {
3
6
  unified: string;
4
7
  raw: string;
5
8
  } | null;
6
9
  export declare function extractAnthropicUsage(payload: unknown): RuntimeUsage | undefined;
7
- export declare function streamAnthropicCompatibleParts(stream: ReadableStream<Uint8Array>): AsyncIterable<unknown>;
10
+ export declare function streamAnthropicCompatibleParts(stream: ReadableStream<Uint8Array>, options?: AnthropicStreamOptions): AsyncIterable<unknown>;
11
+ export {};
8
12
  //# sourceMappingURL=anthropic-stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-stream.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-anthropic/src/anthropic-stream.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAyB1E,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,OAAO,GACX,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgBlD;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CA6DhF;AAED,wBAAuB,8BAA8B,CACnD,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GACjC,aAAa,CAAC,OAAO,CAAC,CA6QxB"}
1
+ {"version":3,"file":"anthropic-stream.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-anthropic/src/anthropic-stream.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAgB1E,KAAK,sBAAsB,GAAG;IAC5B,iCAAiC,CAAC,EAAE,MAAM,CAAC;CAC5C,CAAC;AAmBF,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,OAAO,GACX,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgBlD;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CA+DhF;AAqED,wBAAuB,8BAA8B,CACnD,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,GAAE,sBAA2B,GACnC,aAAa,CAAC,OAAO,CAAC,CAoUxB"}
@@ -1,4 +1,7 @@
1
- import { mergeUsage, parseSseChunk, readRecord, stringifyJsonValue, } from "../../../src/provider/shared/index.js";
1
+ import * as dntShim from "../../../_dnt.shims.js";
2
+ import { mergeUsage, parseSseChunk, readGatewayBillingMode, readRecord, stringifyJsonValue, } from "../../../src/provider/shared/index.js";
3
+ const CLIENT_TOOL_USE_FINISH_REASON = { unified: "tool-calls", raw: "tool_use" };
4
+ const DEFAULT_CLIENT_TOOL_USE_TRAILING_USAGE_GRACE_MS = 100;
2
5
  function isEmptyRecord(value) {
3
6
  return Boolean(value &&
4
7
  typeof value === "object" &&
@@ -33,6 +36,7 @@ export function extractAnthropicUsage(payload) {
33
36
  const cacheReadInputTokens = usage.cache_read_input_tokens;
34
37
  const veryfront = readRecord(usage.veryfront);
35
38
  const costSource = veryfront?.cost_source;
39
+ const billingMode = readGatewayBillingMode(veryfront?.billing_mode);
36
40
  const usageCaptureStatus = veryfront?.usage_capture_status;
37
41
  return {
38
42
  inputTokens: typeof inputTokens === "number" ? inputTokens : undefined,
@@ -74,6 +78,7 @@ export function extractAnthropicUsage(payload) {
74
78
  ...(costSource === "gateway" || costSource === "missing" || costSource === "partial"
75
79
  ? { costSource }
76
80
  : {}),
81
+ ...(billingMode !== undefined ? { billingMode } : {}),
77
82
  ...(usageCaptureStatus === "complete" ||
78
83
  usageCaptureStatus === "missing" ||
79
84
  usageCaptureStatus === "partial"
@@ -81,231 +86,328 @@ export function extractAnthropicUsage(payload) {
81
86
  : {}),
82
87
  };
83
88
  }
84
- export async function* streamAnthropicCompatibleParts(stream) {
89
+ function isToolCallsFinishReason(finishReason) {
90
+ return finishReason === "tool-calls" ||
91
+ (typeof finishReason === "object" && finishReason?.unified === "tool-calls");
92
+ }
93
+ function hasGatewayUsageMetadata(usage) {
94
+ return usage?.billableInputTokens !== undefined ||
95
+ usage?.billableOutputTokens !== undefined ||
96
+ usage?.providerInputCostUsd !== undefined ||
97
+ usage?.providerOutputCostUsd !== undefined ||
98
+ usage?.providerCostUsd !== undefined ||
99
+ usage?.veryfrontInputChargeUsd !== undefined ||
100
+ usage?.veryfrontOutputChargeUsd !== undefined ||
101
+ usage?.veryfrontChargeUsd !== undefined ||
102
+ usage?.veryfrontBilledUsd !== undefined ||
103
+ usage?.costCredits !== undefined ||
104
+ usage?.costSource !== undefined ||
105
+ usage?.billingMode !== undefined ||
106
+ usage?.usageCaptureStatus !== undefined;
107
+ }
108
+ async function readStreamChunk(reader, timeoutMs) {
109
+ if (timeoutMs === undefined) {
110
+ const read = await reader.read();
111
+ return read.done ? { kind: "done" } : { kind: "chunk", chunk: read.value };
112
+ }
113
+ let timeoutId;
114
+ const readPromise = reader.read().then((read) => read.done ? { kind: "done" } : { kind: "chunk", chunk: read.value });
115
+ const timeoutPromise = new Promise((resolve) => {
116
+ timeoutId = dntShim.setTimeout(() => resolve({ kind: "timeout" }), Math.max(1, timeoutMs));
117
+ });
118
+ try {
119
+ const result = await Promise.race([readPromise, timeoutPromise]);
120
+ if (result.kind === "timeout") {
121
+ await cancelStreamReader(reader, "Timed out waiting for trailing Anthropic tool-use usage metadata");
122
+ }
123
+ return result;
124
+ }
125
+ finally {
126
+ if (timeoutId) {
127
+ clearTimeout(timeoutId);
128
+ }
129
+ }
130
+ }
131
+ async function cancelStreamReader(reader, reason) {
132
+ try {
133
+ await reader.cancel(reason);
134
+ }
135
+ catch {
136
+ // The upstream body may already be closed or canceled by the runtime.
137
+ }
138
+ }
139
+ export async function* streamAnthropicCompatibleParts(stream, options = {}) {
85
140
  const decoder = new TextDecoder();
141
+ const reader = stream.getReader();
142
+ const trailingUsageGraceMs = options.clientToolUseTrailingUsageGraceMs ??
143
+ DEFAULT_CLIENT_TOOL_USE_TRAILING_USAGE_GRACE_MS;
86
144
  let buffer = "";
87
145
  const toolCalls = new Map();
88
146
  const reasoningBlocks = new Map();
89
147
  let finishReason = null;
90
148
  let usage;
91
149
  let completedClientToolUseStep = false;
92
- for await (const chunk of stream) {
93
- buffer += decoder.decode(chunk, { stream: true });
94
- const parsed = parseSseChunk(buffer);
150
+ let clientToolUseIdleDeadlineMs = null;
151
+ let clientToolUseTerminalDeadlineMs = null;
152
+ const mergeTrailingBufferUsage = () => {
153
+ if (buffer.trim().length === 0) {
154
+ return;
155
+ }
156
+ const parsed = parseSseChunk(`${buffer}\n\n`);
95
157
  buffer = parsed.remainder;
96
158
  for (const event of parsed.events) {
97
159
  if (event === "[DONE]") {
98
160
  continue;
99
161
  }
100
162
  const record = readRecord(event);
101
- const eventType = typeof record?.type === "string" ? record.type : undefined;
102
163
  usage = mergeUsage(usage, extractAnthropicUsage(record));
103
- if (eventType === "message_start") {
104
- usage = mergeUsage(usage, extractAnthropicUsage(record?.message));
105
- continue;
164
+ }
165
+ };
166
+ const getClientToolUseReadTimeoutMs = () => {
167
+ if (!completedClientToolUseStep || toolCalls.size > 0) {
168
+ return undefined;
169
+ }
170
+ const deadline = isToolCallsFinishReason(finishReason)
171
+ ? clientToolUseTerminalDeadlineMs
172
+ : clientToolUseIdleDeadlineMs;
173
+ return deadline === null ? undefined : Math.max(1, deadline - Date.now());
174
+ };
175
+ const buildFinishPart = () => ({
176
+ type: "finish",
177
+ finishReason: finishReason ??
178
+ (completedClientToolUseStep ? CLIENT_TOOL_USE_FINISH_REASON : null),
179
+ ...(usage ? { usage } : {}),
180
+ });
181
+ try {
182
+ while (true) {
183
+ const read = await readStreamChunk(reader, getClientToolUseReadTimeoutMs());
184
+ if (read.kind === "timeout") {
185
+ mergeTrailingBufferUsage();
186
+ finishReason ??= CLIENT_TOOL_USE_FINISH_REASON;
187
+ yield buildFinishPart();
188
+ return;
189
+ }
190
+ if (read.kind === "done") {
191
+ break;
106
192
  }
107
- if (eventType === "content_block_start") {
108
- const index = typeof record?.index === "number" ? record.index : 0;
109
- const contentBlock = readRecord(record?.content_block);
110
- const blockType = typeof contentBlock?.type === "string" ? contentBlock.type : undefined;
111
- if (blockType === "text" && typeof contentBlock?.text === "string" &&
112
- contentBlock.text.length > 0) {
113
- yield { type: "text-delta", delta: contentBlock.text };
193
+ buffer += decoder.decode(read.chunk, { stream: true });
194
+ const parsed = parseSseChunk(buffer);
195
+ buffer = parsed.remainder;
196
+ for (const event of parsed.events) {
197
+ if (event === "[DONE]") {
114
198
  continue;
115
199
  }
116
- if (blockType === "thinking") {
117
- const reasoningId = `thinking-${index}`;
118
- reasoningBlocks.set(index, { id: reasoningId, text: "" });
119
- yield {
120
- type: "reasoning-start",
121
- id: reasoningId,
122
- };
123
- if (typeof contentBlock?.thinking === "string" && contentBlock.thinking.length > 0) {
124
- const current = reasoningBlocks.get(index);
125
- if (current) {
126
- current.text += contentBlock.thinking;
200
+ const record = readRecord(event);
201
+ const eventType = typeof record?.type === "string" ? record.type : undefined;
202
+ usage = mergeUsage(usage, extractAnthropicUsage(record));
203
+ if (eventType === "message_start") {
204
+ usage = mergeUsage(usage, extractAnthropicUsage(record?.message));
205
+ continue;
206
+ }
207
+ if (eventType === "content_block_start") {
208
+ const index = typeof record?.index === "number" ? record.index : 0;
209
+ const contentBlock = readRecord(record?.content_block);
210
+ const blockType = typeof contentBlock?.type === "string" ? contentBlock.type : undefined;
211
+ if (blockType === "text" && typeof contentBlock?.text === "string" &&
212
+ contentBlock.text.length > 0) {
213
+ yield { type: "text-delta", delta: contentBlock.text };
214
+ continue;
215
+ }
216
+ if (blockType === "thinking") {
217
+ const reasoningId = `thinking-${index}`;
218
+ reasoningBlocks.set(index, { id: reasoningId, text: "" });
219
+ yield {
220
+ type: "reasoning-start",
221
+ id: reasoningId,
222
+ };
223
+ if (typeof contentBlock?.thinking === "string" && contentBlock.thinking.length > 0) {
224
+ const current = reasoningBlocks.get(index);
225
+ if (current) {
226
+ current.text += contentBlock.thinking;
227
+ }
228
+ yield {
229
+ type: "reasoning-delta",
230
+ id: reasoningId,
231
+ delta: contentBlock.thinking,
232
+ };
127
233
  }
234
+ continue;
235
+ }
236
+ // Redacted thinking blocks arrive as opaque encrypted payloads when
237
+ // Claude's safety classifier flags the reasoning trace. Surface them
238
+ // as a zero-length reasoning block so callers know thinking happened
239
+ // without leaking the (legitimately hidden) contents.
240
+ if (blockType === "redacted_thinking") {
241
+ const reasoningId = `thinking-${index}`;
242
+ reasoningBlocks.set(index, {
243
+ id: reasoningId,
244
+ text: "",
245
+ ...(typeof contentBlock?.data === "string"
246
+ ? { redactedData: contentBlock.data }
247
+ : {}),
248
+ });
128
249
  yield {
129
- type: "reasoning-delta",
250
+ type: "reasoning-start",
130
251
  id: reasoningId,
131
- delta: contentBlock.thinking,
132
252
  };
253
+ continue;
133
254
  }
134
- continue;
135
- }
136
- // Redacted thinking blocks arrive as opaque encrypted payloads when
137
- // Claude's safety classifier flags the reasoning trace. Surface them
138
- // as a zero-length reasoning block so callers know thinking happened
139
- // without leaking the (legitimately hidden) contents.
140
- if (blockType === "redacted_thinking") {
141
- const reasoningId = `thinking-${index}`;
142
- reasoningBlocks.set(index, {
143
- id: reasoningId,
144
- text: "",
145
- ...(typeof contentBlock?.data === "string" ? { redactedData: contentBlock.data } : {}),
146
- });
147
- yield {
148
- type: "reasoning-start",
149
- id: reasoningId,
150
- };
151
- continue;
152
- }
153
- if ((blockType === "tool_use" || blockType === "server_tool_use") &&
154
- typeof contentBlock?.id === "string" &&
155
- typeof contentBlock?.name === "string") {
156
- const providerExecuted = blockType === "server_tool_use" ? true : undefined;
157
- const current = {
158
- id: contentBlock.id,
159
- name: contentBlock.name,
160
- input: "",
161
- ...(providerExecuted ? { providerExecuted } : {}),
162
- };
163
- toolCalls.set(index, current);
164
- yield {
165
- type: "tool-input-start",
166
- id: current.id,
167
- toolName: current.name,
168
- ...(providerExecuted ? { providerExecuted } : {}),
169
- };
170
- const initialInput = contentBlock.input;
171
- if (initialInput !== undefined && !isEmptyRecord(initialInput)) {
172
- const serializedInput = stringifyJsonValue(initialInput);
173
- current.input += serializedInput;
255
+ if ((blockType === "tool_use" || blockType === "server_tool_use") &&
256
+ typeof contentBlock?.id === "string" &&
257
+ typeof contentBlock?.name === "string") {
258
+ const providerExecuted = blockType === "server_tool_use" ? true : undefined;
259
+ const current = {
260
+ id: contentBlock.id,
261
+ name: contentBlock.name,
262
+ input: "",
263
+ ...(providerExecuted ? { providerExecuted } : {}),
264
+ };
265
+ toolCalls.set(index, current);
266
+ clientToolUseIdleDeadlineMs = null;
267
+ clientToolUseTerminalDeadlineMs = null;
174
268
  yield {
175
- type: "tool-input-delta",
269
+ type: "tool-input-start",
176
270
  id: current.id,
177
- delta: serializedInput,
271
+ toolName: current.name,
272
+ ...(providerExecuted ? { providerExecuted } : {}),
273
+ };
274
+ const initialInput = contentBlock.input;
275
+ if (initialInput !== undefined && !isEmptyRecord(initialInput)) {
276
+ const serializedInput = stringifyJsonValue(initialInput);
277
+ current.input += serializedInput;
278
+ yield {
279
+ type: "tool-input-delta",
280
+ id: current.id,
281
+ delta: serializedInput,
282
+ };
283
+ }
284
+ continue;
285
+ }
286
+ if (blockType === "web_search_tool_result" &&
287
+ typeof contentBlock?.tool_use_id === "string" &&
288
+ Array.isArray(contentBlock?.content)) {
289
+ yield {
290
+ type: "tool-result",
291
+ toolCallId: contentBlock.tool_use_id,
292
+ toolName: "web_search",
293
+ result: contentBlock.content,
294
+ providerExecuted: true,
295
+ };
296
+ }
297
+ if (blockType === "web_fetch_tool_result" &&
298
+ typeof contentBlock?.tool_use_id === "string" &&
299
+ readRecord(contentBlock?.content)) {
300
+ yield {
301
+ type: "tool-result",
302
+ toolCallId: contentBlock.tool_use_id,
303
+ toolName: "web_fetch",
304
+ result: contentBlock.content,
305
+ providerExecuted: true,
178
306
  };
179
307
  }
180
308
  continue;
181
309
  }
182
- if (blockType === "web_search_tool_result" &&
183
- typeof contentBlock?.tool_use_id === "string" &&
184
- Array.isArray(contentBlock?.content)) {
185
- yield {
186
- type: "tool-result",
187
- toolCallId: contentBlock.tool_use_id,
188
- toolName: "web_search",
189
- result: contentBlock.content,
190
- providerExecuted: true,
191
- };
192
- }
193
- if (blockType === "web_fetch_tool_result" &&
194
- typeof contentBlock?.tool_use_id === "string" &&
195
- readRecord(contentBlock?.content)) {
196
- yield {
197
- type: "tool-result",
198
- toolCallId: contentBlock.tool_use_id,
199
- toolName: "web_fetch",
200
- result: contentBlock.content,
201
- providerExecuted: true,
202
- };
203
- }
204
- continue;
205
- }
206
- if (eventType === "content_block_delta") {
207
- const index = typeof record?.index === "number" ? record.index : 0;
208
- const delta = readRecord(record?.delta);
209
- const deltaType = typeof delta?.type === "string" ? delta.type : undefined;
210
- if (deltaType === "text_delta" && typeof delta?.text === "string" && delta.text.length > 0) {
211
- yield { type: "text-delta", delta: delta.text };
212
- continue;
213
- }
214
- if (deltaType === "thinking_delta" && typeof delta?.thinking === "string" &&
215
- delta.thinking.length > 0) {
216
- const current = reasoningBlocks.get(index);
217
- if (!current) {
310
+ if (eventType === "content_block_delta") {
311
+ const index = typeof record?.index === "number" ? record.index : 0;
312
+ const delta = readRecord(record?.delta);
313
+ const deltaType = typeof delta?.type === "string" ? delta.type : undefined;
314
+ if (deltaType === "text_delta" && typeof delta?.text === "string" && delta.text.length > 0) {
315
+ yield { type: "text-delta", delta: delta.text };
218
316
  continue;
219
317
  }
220
- current.text += delta.thinking;
221
- yield {
222
- type: "reasoning-delta",
223
- id: current.id,
224
- delta: delta.thinking,
225
- };
226
- continue;
227
- }
228
- if (deltaType === "signature_delta" && typeof delta?.signature === "string") {
229
- const current = reasoningBlocks.get(index);
230
- if (current) {
231
- current.signature = delta.signature;
318
+ if (deltaType === "thinking_delta" && typeof delta?.thinking === "string" &&
319
+ delta.thinking.length > 0) {
320
+ const current = reasoningBlocks.get(index);
321
+ if (!current) {
322
+ continue;
323
+ }
324
+ current.text += delta.thinking;
325
+ yield {
326
+ type: "reasoning-delta",
327
+ id: current.id,
328
+ delta: delta.thinking,
329
+ };
330
+ continue;
331
+ }
332
+ if (deltaType === "signature_delta" && typeof delta?.signature === "string") {
333
+ const current = reasoningBlocks.get(index);
334
+ if (current) {
335
+ current.signature = delta.signature;
336
+ }
337
+ continue;
338
+ }
339
+ if (deltaType === "input_json_delta" && typeof delta?.partial_json === "string") {
340
+ const current = toolCalls.get(index);
341
+ if (!current) {
342
+ continue;
343
+ }
344
+ current.input += delta.partial_json;
345
+ yield {
346
+ type: "tool-input-delta",
347
+ id: current.id,
348
+ delta: delta.partial_json,
349
+ };
232
350
  }
233
351
  continue;
234
352
  }
235
- if (deltaType === "input_json_delta" && typeof delta?.partial_json === "string") {
353
+ if (eventType === "content_block_stop") {
354
+ const index = typeof record?.index === "number" ? record.index : 0;
355
+ const reasoning = reasoningBlocks.get(index);
356
+ if (reasoning) {
357
+ yield {
358
+ type: "reasoning-end",
359
+ id: reasoning.id,
360
+ ...(reasoning.signature ? { signature: reasoning.signature } : {}),
361
+ ...(reasoning.redactedData ? { redactedData: reasoning.redactedData } : {}),
362
+ };
363
+ reasoningBlocks.delete(index);
364
+ continue;
365
+ }
236
366
  const current = toolCalls.get(index);
237
367
  if (!current) {
238
368
  continue;
239
369
  }
240
- current.input += delta.partial_json;
241
370
  yield {
242
- type: "tool-input-delta",
243
- id: current.id,
244
- delta: delta.partial_json,
245
- };
246
- }
247
- continue;
248
- }
249
- if (eventType === "content_block_stop") {
250
- const index = typeof record?.index === "number" ? record.index : 0;
251
- const reasoning = reasoningBlocks.get(index);
252
- if (reasoning) {
253
- yield {
254
- type: "reasoning-end",
255
- id: reasoning.id,
256
- ...(reasoning.signature ? { signature: reasoning.signature } : {}),
257
- ...(reasoning.redactedData ? { redactedData: reasoning.redactedData } : {}),
371
+ type: "tool-call",
372
+ toolCallId: current.id,
373
+ toolName: current.name,
374
+ input: current.input.length > 0 ? current.input : "{}",
375
+ ...(current.providerExecuted ? { providerExecuted: true } : {}),
258
376
  };
259
- reasoningBlocks.delete(index);
260
- continue;
261
- }
262
- const current = toolCalls.get(index);
263
- if (!current) {
377
+ if (!current.providerExecuted) {
378
+ completedClientToolUseStep = true;
379
+ clientToolUseIdleDeadlineMs = null;
380
+ }
381
+ toolCalls.delete(index);
264
382
  continue;
265
383
  }
266
- yield {
267
- type: "tool-call",
268
- toolCallId: current.id,
269
- toolName: current.name,
270
- input: current.input.length > 0 ? current.input : "{}",
271
- ...(current.providerExecuted ? { providerExecuted: true } : {}),
272
- };
273
- if (!current.providerExecuted) {
274
- completedClientToolUseStep = true;
384
+ if (eventType === "message_delta") {
385
+ const delta = readRecord(record?.delta);
386
+ const normalizedFinishReason = normalizeAnthropicFinishReason(delta?.stop_reason);
387
+ if (normalizedFinishReason) {
388
+ finishReason = normalizedFinishReason;
389
+ }
275
390
  }
276
- toolCalls.delete(index);
277
- continue;
278
391
  }
279
- if (eventType === "message_delta") {
280
- const delta = readRecord(record?.delta);
281
- const normalizedFinishReason = normalizeAnthropicFinishReason(delta?.stop_reason);
282
- if (normalizedFinishReason) {
283
- finishReason = normalizedFinishReason;
392
+ if (completedClientToolUseStep && toolCalls.size === 0) {
393
+ if (isToolCallsFinishReason(finishReason)) {
394
+ clientToolUseIdleDeadlineMs = null;
395
+ clientToolUseTerminalDeadlineMs ??= Date.now() + trailingUsageGraceMs;
396
+ if (hasGatewayUsageMetadata(usage)) {
397
+ await cancelStreamReader(reader, "Finished Anthropic tool-use turn after gateway usage metadata");
398
+ yield buildFinishPart();
399
+ return;
400
+ }
401
+ }
402
+ else {
403
+ clientToolUseIdleDeadlineMs ??= Date.now() + trailingUsageGraceMs;
284
404
  }
285
405
  }
286
406
  }
287
- if (completedClientToolUseStep && toolCalls.size === 0) {
288
- yield {
289
- type: "finish",
290
- finishReason: { unified: "tool-calls", raw: "tool_use" },
291
- ...(usage ? { usage } : {}),
292
- };
293
- return;
294
- }
295
407
  }
296
- if (buffer.trim().length > 0) {
297
- const parsed = parseSseChunk(`${buffer}\n\n`);
298
- for (const event of parsed.events) {
299
- if (event === "[DONE]") {
300
- continue;
301
- }
302
- const record = readRecord(event);
303
- usage = mergeUsage(usage, extractAnthropicUsage(record));
304
- }
408
+ finally {
409
+ reader.releaseLock();
305
410
  }
306
- yield {
307
- type: "finish",
308
- finishReason,
309
- ...(usage ? { usage } : {}),
310
- };
411
+ mergeTrailingBufferUsage();
412
+ yield buildFinishPart();
311
413
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openai-chat-stream.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-openai/src/openai-chat-stream.ts"],"names":[],"mappings":"AA2JA,wBAAuB,2BAA2B,CAChD,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GACjC,aAAa,CAAC,OAAO,CAAC,CAsJxB"}
1
+ {"version":3,"file":"openai-chat-stream.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-openai/src/openai-chat-stream.ts"],"names":[],"mappings":"AAwIA,wBAAuB,2BAA2B,CAChD,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GACjC,aAAa,CAAC,OAAO,CAAC,CAsJxB"}
@@ -1,4 +1,4 @@
1
- import { parseSseChunk, readRecord } from "../../../src/provider/shared/index.js";
1
+ import { parseSseChunk, readGatewayBillingMode, readRecord } from "../../../src/provider/shared/index.js";
2
2
  function normalizeOpenAIFinishReason(raw) {
3
3
  if (typeof raw !== "string") {
4
4
  return null;
@@ -26,6 +26,7 @@ function extractOpenAIUsage(payload) {
26
26
  const reasoningTokens = completionTokensDetails?.reasoning_tokens;
27
27
  const veryfront = readRecord(usage.veryfront);
28
28
  const costSource = veryfront?.cost_source;
29
+ const billingMode = readGatewayBillingMode(veryfront?.billing_mode);
29
30
  const usageCaptureStatus = veryfront?.usage_capture_status;
30
31
  return {
31
32
  inputTokens: typeof inputTokens === "number" ? inputTokens : undefined,
@@ -65,6 +66,7 @@ function extractOpenAIUsage(payload) {
65
66
  ...(costSource === "gateway" || costSource === "missing" || costSource === "partial"
66
67
  ? { costSource }
67
68
  : {}),
69
+ ...(billingMode !== undefined ? { billingMode } : {}),
68
70
  ...(usageCaptureStatus === "complete" ||
69
71
  usageCaptureStatus === "missing" ||
70
72
  usageCaptureStatus === "partial"
@@ -1 +1 @@
1
- {"version":3,"file":"openai-provider.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-openai/src/openai-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EACL,kBAAkB,EAMlB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EAKpB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,uCAAuC,CAAC;AAc/C,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,+BAA+B,EAC/B,8BAA8B,GAC/B,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AA+UD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAsEd;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAsEd;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,gBAAgB,CAuClB;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,EAAE,YAAY;IAEvB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,YAAY;IAYrE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,gBAAgB;IAY7E,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,YAAY;CAW1E"}
1
+ {"version":3,"file":"openai-provider.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-llm-openai/src/openai-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACrF,OAAO,EACL,kBAAkB,EAMlB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EAMpB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,uCAAuC,CAAC;AAc/C,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,+BAA+B,EAC/B,8BAA8B,GAC/B,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAkVD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAsEd;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,YAAY,CAsEd;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,GACd,gBAAgB,CAuClB;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,EAAE,YAAY;IAEvB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,YAAY;IAYrE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,gBAAgB;IAY7E,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,YAAY;CAW1E"}
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module extensions/ext-llm-openai/openai-provider
9
9
  */
10
- import { buildProviderError, createOpenAIRequestInit, createWarningCollector, getOpenAIChatCompletionsUrl, getOpenAIEmbeddingUrl, getOpenAIResponsesUrl, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, readRecord, requestJson, requestStream, stringifyJsonValue, TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions, } from "../../../src/provider/shared/index.js";
10
+ import { buildProviderError, createOpenAIRequestInit, createWarningCollector, getOpenAIChatCompletionsUrl, getOpenAIEmbeddingUrl, getOpenAIResponsesUrl, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, readGatewayBillingMode, readRecord, requestJson, requestStream, stringifyJsonValue, TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions, } from "../../../src/provider/shared/index.js";
11
11
  import { buildOpenAIChatRequest, } from "./openai-chat-request-builder.js";
12
12
  import { streamOpenAICompatibleParts } from "./openai-chat-stream.js";
13
13
  import { buildOpenAIResponsesRequest } from "./openai-responses-request-builder.js";
@@ -70,6 +70,7 @@ function extractOpenAIUsage(payload) {
70
70
  const reasoningTokens = completionTokensDetails?.reasoning_tokens;
71
71
  const veryfront = readRecord(usage.veryfront);
72
72
  const costSource = veryfront?.cost_source;
73
+ const billingMode = readGatewayBillingMode(veryfront?.billing_mode);
73
74
  const usageCaptureStatus = veryfront?.usage_capture_status;
74
75
  return {
75
76
  inputTokens: typeof inputTokens === "number" ? inputTokens : undefined,
@@ -109,6 +110,7 @@ function extractOpenAIUsage(payload) {
109
110
  ...(costSource === "gateway" || costSource === "missing" || costSource === "partial"
110
111
  ? { costSource }
111
112
  : {}),
113
+ ...(billingMode !== undefined ? { billingMode } : {}),
112
114
  ...(usageCaptureStatus === "complete" ||
113
115
  usageCaptureStatus === "missing" ||
114
116
  usageCaptureStatus === "partial"