@jeffusion/bungee-llms 4.6.0 → 5.1.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/dist/index.js CHANGED
@@ -7053,7 +7053,8 @@ function readNumber(record, key) {
7053
7053
  if (!record) {
7054
7054
  return;
7055
7055
  }
7056
- return asFiniteNumber(record[key]);
7056
+ const value = record[key];
7057
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
7057
7058
  }
7058
7059
  function readRecord(record, key) {
7059
7060
  if (!record) {
@@ -7126,7 +7127,7 @@ function clampTokens(value) {
7126
7127
  if (value === undefined) {
7127
7128
  return;
7128
7129
  }
7129
- return Math.max(0, Math.round(value));
7130
+ return Number.isFinite(value) ? Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.floor(value))) : undefined;
7130
7131
  }
7131
7132
  function buildBaseEvent(state, overrides) {
7132
7133
  return {
@@ -7193,7 +7194,7 @@ function assertCanonicalTokenAccountingEventV2(event) {
7193
7194
  throw new Error("Canonical token accounting event has invalid outputAuthority.");
7194
7195
  }
7195
7196
  for (const key of ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens"]) {
7196
- if (event[key] !== undefined && asFiniteNumber(event[key]) === undefined) {
7197
+ if (event[key] !== undefined && !(typeof event[key] === "number" && Number.isSafeInteger(event[key]) && event[key] >= 0)) {
7197
7198
  throw new Error(`Canonical token accounting event has invalid numeric field: ${key}`);
7198
7199
  }
7199
7200
  }
@@ -7204,14 +7205,11 @@ function assertCanonicalTokenAccountingEventV2(event) {
7204
7205
  throw new Error("Plugin aggregate fields logicalRequests/upstreamAttempts must not appear on single llms events.");
7205
7206
  }
7206
7207
  if (event.inputAuthority === "official") {
7207
- if (asFiniteNumber(event.inputTokens) === undefined) {
7208
+ if (!(typeof event.inputTokens === "number" && Number.isSafeInteger(event.inputTokens) && event.inputTokens >= 0)) {
7208
7209
  throw new Error("Official inputAuthority requires inputTokens.");
7209
7210
  }
7210
- if (event.final && (asFiniteNumber(event.cacheReadTokens) === undefined || asFiniteNumber(event.cacheWriteTokens) === undefined)) {
7211
- throw new Error("Official inputAuthority requires explicit cacheReadTokens and cacheWriteTokens, even when zero.");
7212
- }
7213
7211
  }
7214
- if (event.outputAuthority === "official" && asFiniteNumber(event.outputTokens) === undefined) {
7212
+ if (event.outputAuthority === "official" && !(typeof event.outputTokens === "number" && Number.isSafeInteger(event.outputTokens) && event.outputTokens >= 0)) {
7215
7213
  throw new Error("Official outputAuthority requires outputTokens.");
7216
7214
  }
7217
7215
  }
@@ -7271,6 +7269,444 @@ function getProviderTokenAccountingCapabilities(provider) {
7271
7269
  return capabilities;
7272
7270
  }
7273
7271
  // src/token-accounting/session.ts
7272
+ var DEFERRED_MEDIA_KEYS = new Set([
7273
+ "image_url",
7274
+ "imageUrl",
7275
+ "inline_data",
7276
+ "inlineData",
7277
+ "file_data",
7278
+ "fileData",
7279
+ "file_uri",
7280
+ "fileUri",
7281
+ "file_url",
7282
+ "fileUrl",
7283
+ "file_id",
7284
+ "fileId",
7285
+ "input_audio",
7286
+ "inputAudio",
7287
+ "audio_url",
7288
+ "audioUrl",
7289
+ "input_video",
7290
+ "inputVideo",
7291
+ "video_url",
7292
+ "videoUrl"
7293
+ ]);
7294
+ var DEFERRED_MEDIA_TYPES = new Set([
7295
+ "image",
7296
+ "input_image",
7297
+ "image_url",
7298
+ "input_audio",
7299
+ "audio",
7300
+ "input_video",
7301
+ "video",
7302
+ "input_file",
7303
+ "file"
7304
+ ]);
7305
+ var DEFERRED_METADATA_KEYS = new Set([
7306
+ "model",
7307
+ "role",
7308
+ "type",
7309
+ "status",
7310
+ "index",
7311
+ "id",
7312
+ "mime_type",
7313
+ "mimeType",
7314
+ "detail"
7315
+ ]);
7316
+ var MAX_STREAM_ESTIMATE_CHARS = 64 * 1024;
7317
+ var MAX_STREAM_ESTIMATE_FRAGMENTS = 4096;
7318
+ var MAX_INPUT_ESTIMATE_NODES = 4096;
7319
+ var MAX_DEFERRED_MODEL_CHARS = 256;
7320
+ function isDeferred(state) {
7321
+ return state.deferFinalization === true;
7322
+ }
7323
+ function setSessionModel(state, value) {
7324
+ const model = asString2(value) ?? state.model ?? "unknown";
7325
+ if (isDeferred(state) && model.length > MAX_DEFERRED_MODEL_CHARS) {
7326
+ state.model = model.slice(0, MAX_DEFERRED_MODEL_CHARS);
7327
+ state.requestEstimateTruncated = true;
7328
+ return;
7329
+ }
7330
+ state.model = model;
7331
+ }
7332
+ function observeOutput(state, value) {
7333
+ if (state.outputAuthority === "official")
7334
+ return;
7335
+ if (isDeferred(state) && typeof value === "string" && isMediaDataUri(value)) {
7336
+ state.estimateMediaSeen = true;
7337
+ return;
7338
+ }
7339
+ const text = typeof value === "string" ? value : JSON.stringify(value);
7340
+ if (!text)
7341
+ return;
7342
+ const estimateState = state;
7343
+ const remaining = MAX_STREAM_ESTIMATE_CHARS - (estimateState.estimateChars ?? 0);
7344
+ if (remaining <= 0) {
7345
+ estimateState.estimateTruncated = true;
7346
+ return;
7347
+ }
7348
+ if ((estimateState.estimateFragments?.length ?? 0) >= MAX_STREAM_ESTIMATE_FRAGMENTS) {
7349
+ estimateState.estimateTruncated = true;
7350
+ return;
7351
+ }
7352
+ const fragment = text.slice(0, remaining);
7353
+ (estimateState.estimateFragments ??= []).push(fragment);
7354
+ estimateState.estimateChars = (estimateState.estimateChars ?? 0) + fragment.length;
7355
+ if (fragment.length < text.length)
7356
+ estimateState.estimateTruncated = true;
7357
+ }
7358
+ function observeBoundedOutput(state, value) {
7359
+ if (state.outputAuthority === "official")
7360
+ return;
7361
+ if (!isDeferred(state)) {
7362
+ if (typeof value === "string") {
7363
+ observeOutput(state, value);
7364
+ return;
7365
+ }
7366
+ const bounded = boundedRequestSnapshot({ value });
7367
+ if (bounded.truncated)
7368
+ state.estimateTruncated = true;
7369
+ observeOutput(state, bounded.value.value);
7370
+ return;
7371
+ }
7372
+ const estimateState = state;
7373
+ const nodesLeft = MAX_INPUT_ESTIMATE_NODES - (estimateState.estimateNodes ?? 0);
7374
+ if (nodesLeft < 3) {
7375
+ estimateState.estimateTruncated = true;
7376
+ return;
7377
+ }
7378
+ const bounded = typeof value === "string" ? { value: { value: value.slice(0, MAX_STREAM_ESTIMATE_CHARS) }, truncated: value.length > MAX_STREAM_ESTIMATE_CHARS, nodesCopied: 0 } : boundedRequestSnapshot({ value }, Math.max(1, Math.floor(nodesLeft / 3)));
7379
+ estimateState.estimateNodes = (estimateState.estimateNodes ?? 0) + bounded.nodesCopied * 3;
7380
+ const filtered = stripDeferredMedia(bounded.value.value);
7381
+ if (bounded.truncated)
7382
+ estimateState.estimateTruncated = true;
7383
+ if (filtered.sawMedia)
7384
+ estimateState.estimateMediaSeen = true;
7385
+ if (filtered.hasContent)
7386
+ observeOutput(state, filtered.value);
7387
+ }
7388
+ function isMediaDataUri(value) {
7389
+ return /^data:(?:image|audio|video)\//i.test(value);
7390
+ }
7391
+ function stripDeferredMedia(value, key = "") {
7392
+ if (key === "tools" || key === "tool_calls" || key === "tool_use" || key === "functionCall" || key === "function_call" || key === "arguments" || key === "args") {
7393
+ return { value, sawMedia: false, hasContent: Array.isArray(value) ? value.length > 0 : value !== null && value !== undefined };
7394
+ }
7395
+ if (DEFERRED_MEDIA_KEYS.has(key))
7396
+ return { value: undefined, sawMedia: true, hasContent: false };
7397
+ if (typeof value === "string") {
7398
+ const dataUriPattern = /data:(?:image|audio|video)\/[^\s"'<>)}\]]+/gi;
7399
+ const stripped = value.replace(dataUriPattern, "");
7400
+ const sawMedia = stripped.length !== value.length;
7401
+ return { value: stripped || undefined, sawMedia, hasContent: stripped.length > 0 && !DEFERRED_METADATA_KEYS.has(key) };
7402
+ }
7403
+ if (typeof value === "number" || typeof value === "boolean") {
7404
+ return { value, sawMedia: false, hasContent: !DEFERRED_METADATA_KEYS.has(key) };
7405
+ }
7406
+ if (Array.isArray(value)) {
7407
+ let sawMedia = false;
7408
+ let hasContent = false;
7409
+ const result = [];
7410
+ for (const item of value) {
7411
+ const filtered = stripDeferredMedia(item);
7412
+ sawMedia ||= filtered.sawMedia;
7413
+ hasContent ||= filtered.hasContent;
7414
+ if (filtered.value !== undefined)
7415
+ result.push(filtered.value);
7416
+ }
7417
+ return { value: result, sawMedia, hasContent };
7418
+ }
7419
+ if (!isRecord6(value))
7420
+ return { value, sawMedia: false, hasContent: false };
7421
+ if (asString2(value.type) === "tool_use" || asString2(value.type) === "function_call")
7422
+ return { value, sawMedia: false, hasContent: true };
7423
+ if (DEFERRED_MEDIA_TYPES.has(asString2(value.type) ?? ""))
7424
+ return { value: undefined, sawMedia: true, hasContent: false };
7425
+ const result = {};
7426
+ let sawMedia = false;
7427
+ let hasContent = false;
7428
+ for (const [childKey, child] of Object.entries(value)) {
7429
+ const filtered = stripDeferredMedia(child, childKey);
7430
+ sawMedia ||= filtered.sawMedia;
7431
+ hasContent ||= filtered.hasContent;
7432
+ if (filtered.value !== undefined)
7433
+ result[childKey] = filtered.value;
7434
+ }
7435
+ return { value: result, sawMedia, hasContent };
7436
+ }
7437
+ function clearObservedOutput(state) {
7438
+ const estimateState = state;
7439
+ estimateState.estimateFragments = undefined;
7440
+ estimateState.estimateChars = undefined;
7441
+ estimateState.estimateTruncated = false;
7442
+ estimateState.estimateMediaSeen = false;
7443
+ estimateState.estimateNodes = 0;
7444
+ }
7445
+ function estimatedStreamOutput(state) {
7446
+ const estimateState = state;
7447
+ const estimateChars = estimateState.estimateChars ?? 0;
7448
+ if (estimateChars === 0)
7449
+ return { authority: "none" };
7450
+ if (isDeferred(state)) {
7451
+ const tokens = clampTokens(countTextTokens("x".repeat(estimateChars)));
7452
+ return tokens === undefined ? { authority: "none" } : { tokens, authority: estimateState.estimateTruncated || estimateState.estimateMediaSeen ? "partial" : "heuristic" };
7453
+ }
7454
+ const tokens = clampTokens(countModelTextTokens(estimateState.estimateFragments.join(""), state.model));
7455
+ return tokens === undefined ? { authority: "none" } : { tokens, authority: estimateState.estimateTruncated ? "partial" : "local" };
7456
+ }
7457
+ function finalizeMissingStreamOutput(state, interrupted = false) {
7458
+ if (state.outputAuthority === "official")
7459
+ return;
7460
+ const estimate = estimatedStreamOutput(state);
7461
+ state.outputTokens = estimate.tokens;
7462
+ state.outputAuthority = interrupted && estimate.tokens !== undefined ? "partial" : estimate.authority;
7463
+ }
7464
+ function boundedRequestSnapshot(body, maxNodes = MAX_INPUT_ESTIMATE_NODES) {
7465
+ let remaining = MAX_STREAM_ESTIMATE_CHARS;
7466
+ let nodes = maxNodes;
7467
+ let truncated = false;
7468
+ const copy = (value) => {
7469
+ if (nodes <= 0 || remaining <= 0) {
7470
+ truncated = true;
7471
+ return;
7472
+ }
7473
+ nodes -= 1;
7474
+ if (typeof value === "string") {
7475
+ if (value.length > remaining) {
7476
+ truncated = true;
7477
+ const clipped = value.slice(0, remaining);
7478
+ remaining = 0;
7479
+ return clipped;
7480
+ }
7481
+ remaining -= value.length;
7482
+ return value;
7483
+ }
7484
+ if (value === null || typeof value === "number" || typeof value === "boolean")
7485
+ return value;
7486
+ if (Array.isArray(value)) {
7487
+ const result = [];
7488
+ for (let index = 0;index < value.length; index += 1) {
7489
+ if (nodes <= 0 || remaining <= 0) {
7490
+ truncated = true;
7491
+ break;
7492
+ }
7493
+ result.push(copy(value[index]));
7494
+ }
7495
+ if (result.length < value.length)
7496
+ truncated = true;
7497
+ return result;
7498
+ }
7499
+ if (!isRecord6(value))
7500
+ return;
7501
+ const result = {};
7502
+ for (const key in value) {
7503
+ if (!Object.prototype.hasOwnProperty.call(value, key))
7504
+ continue;
7505
+ if (nodes <= 0 || remaining <= key.length) {
7506
+ truncated = true;
7507
+ break;
7508
+ }
7509
+ remaining -= key.length;
7510
+ result[key] = copy(value[key]);
7511
+ }
7512
+ return result;
7513
+ };
7514
+ return { value: copy(body), truncated, nodesCopied: maxNodes - nodes };
7515
+ }
7516
+ function storeRequestForEstimate(state, body, fields) {
7517
+ const relevant = {};
7518
+ if (body.model !== undefined)
7519
+ relevant.model = isDeferred(state) && typeof body.model === "string" ? body.model.slice(0, MAX_DEFERRED_MODEL_CHARS) : body.model;
7520
+ for (const field of fields) {
7521
+ if (Object.prototype.hasOwnProperty.call(body, field))
7522
+ relevant[field] = body[field];
7523
+ }
7524
+ const bounded = boundedRequestSnapshot(relevant);
7525
+ state.requestBody = bounded.value;
7526
+ state.requestEstimateTruncated = bounded.truncated || typeof body.model === "string" && isDeferred(state) && body.model.length > MAX_DEFERRED_MODEL_CHARS;
7527
+ }
7528
+ function estimateMissingInput(state) {
7529
+ if (state.estimatedInputAuthority === "official")
7530
+ return;
7531
+ const requestBody = state.requestBody;
7532
+ if (!requestBody) {
7533
+ state.estimatedInputTokens = undefined;
7534
+ state.estimatedInputAuthority = "none";
7535
+ return;
7536
+ }
7537
+ const hasInput = state.provider === "openai" ? requestBody.input !== undefined || requestBody.messages !== undefined : state.provider === "anthropic" ? requestBody.system !== undefined || requestBody.messages !== undefined : requestBody.contents !== undefined || requestBody.systemInstruction !== undefined;
7538
+ if (!hasInput) {
7539
+ state.estimatedInputTokens = undefined;
7540
+ state.estimatedInputAuthority = "none";
7541
+ return;
7542
+ }
7543
+ const bounded = isDeferred(state) ? { value: requestBody, truncated: false } : boundedRequestSnapshot(requestBody);
7544
+ if (isDeferred(state)) {
7545
+ const filtered = stripDeferredMedia(bounded.value);
7546
+ const estimateBody = { ...filtered.value };
7547
+ delete estimateBody.model;
7548
+ const hasToolSchema = Array.isArray(estimateBody.tools) && estimateBody.tools.length > 0;
7549
+ const hasContent = filtered.hasContent || hasToolSchema;
7550
+ if (!hasContent) {
7551
+ state.estimatedInputTokens = undefined;
7552
+ state.estimatedInputAuthority = "none";
7553
+ return;
7554
+ }
7555
+ state.estimatedInputTokens = clampTokens(countJsonLikeTokens(estimateBody));
7556
+ state.estimatedInputAuthority = bounded.truncated || state.requestEstimateTruncated || filtered.sawMedia ? "partial" : "heuristic";
7557
+ return;
7558
+ }
7559
+ const estimate = state.provider === "openai" ? estimateOpenAIInputTokens(bounded.value) : state.provider === "anthropic" ? estimateAnthropicInputTokens(bounded.value) : estimateGeminiInputTokens(bounded.value);
7560
+ state.estimatedInputTokens = estimate;
7561
+ state.estimatedInputAuthority = bounded.truncated || state.requestEstimateTruncated ? "partial" : "local";
7562
+ }
7563
+ function hasOpenAIOutput(body) {
7564
+ if (Array.isArray(body.output) && body.output.length > 0)
7565
+ return true;
7566
+ return Array.isArray(body.choices) && body.choices.some((choice) => {
7567
+ if (!isRecord6(choice))
7568
+ return false;
7569
+ const message = readRecord(choice, "message") ?? readRecord(choice, "delta");
7570
+ return !!message && (message.content !== undefined && message.content !== null || Array.isArray(message.tool_calls) && message.tool_calls.length > 0);
7571
+ });
7572
+ }
7573
+ function hasAnthropicOutput(body) {
7574
+ return Array.isArray(body.content) && body.content.length > 0;
7575
+ }
7576
+ function observeAnthropicOutput(state, body) {
7577
+ const blocks = Array.isArray(body.content) ? body.content : [];
7578
+ for (let index = 0;index < blocks.length && index < MAX_INPUT_ESTIMATE_NODES; index += 1) {
7579
+ observeBoundedOutput(state, blocks[index]);
7580
+ }
7581
+ if (blocks.length > MAX_INPUT_ESTIMATE_NODES && isDeferred(state))
7582
+ state.estimateTruncated = true;
7583
+ }
7584
+ function hasGeminiOutput(body) {
7585
+ return Array.isArray(body.candidates) && body.candidates.some((candidate) => {
7586
+ const parts = readRecord(candidate, "content")?.parts;
7587
+ return Array.isArray(parts) && parts.length > 0;
7588
+ });
7589
+ }
7590
+ function isErrorResponse(body) {
7591
+ const status = asString2(body.status)?.toLowerCase();
7592
+ const type = asString2(body.type)?.toLowerCase();
7593
+ return body.error !== undefined || status === "error" || status === "failed" || type === "error" || type?.endsWith(".error") === true || type?.endsWith(".failed") === true;
7594
+ }
7595
+ function isDeferredFailure(body) {
7596
+ const status = asString2(body.status)?.toLowerCase();
7597
+ const type = asString2(body.type)?.toLowerCase();
7598
+ return isErrorResponse(body) || status === "incomplete" || type === "incomplete" || type?.endsWith(".incomplete") === true;
7599
+ }
7600
+ function observeOpenAIOutput(state, chunk, includeResponseSnapshot = true) {
7601
+ if (state.outputAuthority === "official")
7602
+ return;
7603
+ let nodes = MAX_INPUT_ESTIMATE_NODES;
7604
+ const truncated = () => {
7605
+ if (isDeferred(state))
7606
+ state.estimateTruncated = true;
7607
+ };
7608
+ const type = asString2(chunk.type);
7609
+ if (type === "response.output_text.delta") {
7610
+ if (isDeferred(state))
7611
+ observeBoundedOutput(state, chunk.delta);
7612
+ else
7613
+ observeOutput(state, chunk.delta);
7614
+ }
7615
+ if (type === "response.function_call_arguments.delta") {
7616
+ if (isDeferred(state))
7617
+ observeBoundedOutput(state, chunk.delta);
7618
+ else
7619
+ observeOutput(state, chunk.delta);
7620
+ }
7621
+ if (includeResponseSnapshot) {
7622
+ const response = readRecord(chunk, "response");
7623
+ const responseOutput = Array.isArray(response?.output) ? response.output : Array.isArray(chunk.output) ? chunk.output : undefined;
7624
+ if (responseOutput !== undefined) {
7625
+ if (isDeferred(state)) {
7626
+ const limit = Math.min(responseOutput.length, nodes);
7627
+ for (let index = 0;index < limit; index += 1, nodes -= 1) {
7628
+ observeBoundedOutput(state, responseOutput[index]);
7629
+ }
7630
+ if (responseOutput.length > limit)
7631
+ truncated();
7632
+ } else
7633
+ observeOutput(state, responseOutput);
7634
+ }
7635
+ }
7636
+ const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
7637
+ const choiceLimit = Math.min(choices.length, nodes);
7638
+ for (let index = 0;index < choiceLimit; index += 1, nodes -= 1) {
7639
+ const choice = choices[index];
7640
+ if (!isRecord6(choice))
7641
+ continue;
7642
+ const delta = readRecord(choice, "delta") ?? (isDeferred(state) ? readRecord(choice, "message") : undefined);
7643
+ if (delta?.content !== undefined) {
7644
+ if (isDeferred(state))
7645
+ observeBoundedOutput(state, delta.content);
7646
+ else
7647
+ observeOutput(state, delta.content);
7648
+ }
7649
+ const calls = Array.isArray(delta?.tool_calls) ? delta.tool_calls : [];
7650
+ const callLimit = Math.min(calls.length, nodes);
7651
+ for (let callIndex = 0;callIndex < callLimit; callIndex += 1, nodes -= 1) {
7652
+ const call = calls[callIndex];
7653
+ const fn = readRecord(call, "function");
7654
+ if (fn?.arguments !== undefined) {
7655
+ if (isDeferred(state))
7656
+ observeBoundedOutput(state, fn.arguments);
7657
+ else
7658
+ observeOutput(state, fn.arguments);
7659
+ }
7660
+ }
7661
+ if (calls.length > callLimit)
7662
+ truncated();
7663
+ }
7664
+ if (choices.length > choiceLimit)
7665
+ truncated();
7666
+ }
7667
+ function replaceObservedOutput(state) {
7668
+ const estimateState = state;
7669
+ estimateState.estimateFragments = [];
7670
+ estimateState.estimateChars = 0;
7671
+ estimateState.estimateTruncated = false;
7672
+ estimateState.estimateMediaSeen = false;
7673
+ estimateState.estimateNodes = 0;
7674
+ }
7675
+ function observeGeminiOutput(state, chunk) {
7676
+ let nodes = MAX_INPUT_ESTIMATE_NODES;
7677
+ const candidates = Array.isArray(chunk.candidates) ? chunk.candidates : [];
7678
+ const candidateLimit = Math.min(candidates.length, nodes);
7679
+ for (let candidateIndex = 0;candidateIndex < candidateLimit; candidateIndex += 1, nodes -= 1) {
7680
+ const candidate = candidates[candidateIndex];
7681
+ const content = readRecord(candidate, "content");
7682
+ const parts = Array.isArray(content?.parts) ? content.parts : [];
7683
+ const partLimit = Math.min(parts.length, nodes);
7684
+ for (let partIndex = 0;partIndex < partLimit; partIndex += 1, nodes -= 1) {
7685
+ const part = parts[partIndex];
7686
+ if (!isRecord6(part))
7687
+ continue;
7688
+ if (isDeferred(state) && (part.inlineData !== undefined || part.inline_data !== undefined || part.fileData !== undefined || part.file_data !== undefined)) {
7689
+ state.estimateMediaSeen = true;
7690
+ }
7691
+ if (typeof part.text === "string") {
7692
+ if (isDeferred(state))
7693
+ observeBoundedOutput(state, part.text);
7694
+ else
7695
+ observeOutput(state, part.text);
7696
+ }
7697
+ if (part.functionCall !== undefined) {
7698
+ if (isDeferred(state))
7699
+ observeBoundedOutput(state, part.functionCall);
7700
+ else
7701
+ observeOutput(state, part.functionCall);
7702
+ }
7703
+ }
7704
+ if (parts.length > partLimit && isDeferred(state))
7705
+ state.estimateTruncated = true;
7706
+ }
7707
+ if (candidates.length > candidateLimit && isDeferred(state))
7708
+ state.estimateTruncated = true;
7709
+ }
7274
7710
  function countOpenAIStringTokens(value, model) {
7275
7711
  return countModelTextTokens(asString2(value) ?? "", model);
7276
7712
  }
@@ -7317,12 +7753,9 @@ function estimateOpenAIMessageTokens(message, model) {
7317
7753
  const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
7318
7754
  return 4 + countOpenAIStringTokens(message.role, model) + countOpenAIStringTokens(message.name, model) + countOpenAIStringTokens(message.tool_call_id, model) + estimateOpenAIMessageContent(content, model) + toolCalls.reduce((total, toolCall) => total + estimateOpenAIToolCallTokens(toolCall, model), 0);
7319
7755
  }
7320
- function sum(values) {
7321
- let total = 0;
7322
- for (const value of values) {
7323
- total += value ?? 0;
7324
- }
7325
- return total;
7756
+ function safeTokenSum(...values) {
7757
+ const total = values.reduce((sum, value) => sum + value, 0);
7758
+ return Number.isSafeInteger(total) && total >= 0 ? total : undefined;
7326
7759
  }
7327
7760
  function estimateGeminiInlineDataTokens(inlineData) {
7328
7761
  const mimeType = asString2(inlineData.mimeType)?.toLowerCase() ?? "";
@@ -7343,53 +7776,6 @@ function estimateGeminiFunctionCallTokens(functionCall) {
7343
7776
  function estimateGeminiFunctionResponseTokens(functionResponse) {
7344
7777
  return 12 + countTextTokens(asString2(functionResponse.name) ?? "") + countJsonLikeTokens(functionResponse.response);
7345
7778
  }
7346
- function getAnthropicSessionState(state) {
7347
- const anthropicState = state;
7348
- if (!anthropicState.anthropicContentBlocks) {
7349
- anthropicState.anthropicContentBlocks = new Map;
7350
- }
7351
- return anthropicState;
7352
- }
7353
- function tryParseJsonRecord(value) {
7354
- if (!value) {
7355
- return;
7356
- }
7357
- try {
7358
- const parsed = JSON.parse(value);
7359
- return isRecord6(parsed) ? parsed : undefined;
7360
- } catch {
7361
- return;
7362
- }
7363
- }
7364
- function estimateAnthropicContentBlockTokens(block) {
7365
- if (block.type === "text") {
7366
- return countTextTokens(block.text ?? "");
7367
- }
7368
- if (block.type === "tool_use") {
7369
- const parsedInput = tryParseJsonRecord(block.toolInputJson);
7370
- return countJsonLikeTokens({
7371
- type: "tool_use",
7372
- id: block.toolUseId,
7373
- name: block.toolName,
7374
- input: parsedInput ?? (block.toolInputJson ? { partial_json: block.toolInputJson } : {})
7375
- });
7376
- }
7377
- if (block.start) {
7378
- return countJsonLikeTokens(block.start);
7379
- }
7380
- return 0;
7381
- }
7382
- function updateAnthropicEstimatedOutput(state) {
7383
- const anthropicState = getAnthropicSessionState(state);
7384
- anthropicState.anthropicEstimatedOutputTokens = clampTokens(Array.from(anthropicState.anthropicContentBlocks?.values() ?? []).reduce((total, block) => {
7385
- return total + estimateAnthropicContentBlockTokens(block);
7386
- }, 0)) ?? 0;
7387
- if (state.outputAuthority === "partial" || state.outputAuthority === "official") {
7388
- return;
7389
- }
7390
- state.outputTokens = anthropicState.anthropicEstimatedOutputTokens;
7391
- state.outputAuthority = anthropicState.anthropicEstimatedOutputTokens > 0 ? "local" : "none";
7392
- }
7393
7779
  function buildAnthropicStreamingEvent(state, overrides = {}) {
7394
7780
  return createPartialTokenAccountingEvent(state, {
7395
7781
  inputTokens: state.estimatedInputTokens,
@@ -7424,6 +7810,9 @@ function estimateOpenAIMessageContent(content, model) {
7424
7810
  }
7425
7811
  function estimateOpenAIInputTokens(body) {
7426
7812
  const model = asString2(body.model);
7813
+ if (body.input !== undefined) {
7814
+ return clampTokens(countOpenAIJsonLikeTokens(body.input, model) + 8) ?? 0;
7815
+ }
7427
7816
  const messages = Array.isArray(body.messages) ? body.messages : [];
7428
7817
  const messageTokens = messages.reduce((total, message) => total + estimateOpenAIMessageTokens(message, model), 0);
7429
7818
  const tools = Array.isArray(body.tools) ? body.tools : [];
@@ -7442,12 +7831,24 @@ function estimateOpenAIResponseOutputTokens(body, model) {
7442
7831
  return total + countOpenAIStringTokens(message?.role ?? delta?.role, model) + countOpenAIStringTokens(message?.tool_call_id, model) + estimateOpenAIMessageContent(message?.content ?? delta?.content, model) + toolCalls.reduce((toolTotal, toolCall) => toolTotal + estimateOpenAIToolCallTokens(toolCall, model), 0) + (asString2(choice.finish_reason) === "tool_calls" ? 4 : 0);
7443
7832
  }, 0)) ?? 0;
7444
7833
  }
7445
- function isOpenAIFinalUsageOnlyChunk(chunk) {
7446
- if (!readRecord(chunk, "usage")) {
7447
- return false;
7448
- }
7449
- const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
7450
- return choices.length === 0;
7834
+ function openAIUsage(body) {
7835
+ return readRecord(readRecord(body, "response"), "usage") ?? readRecord(body, "usage");
7836
+ }
7837
+ function estimateOpenAIResponsesOutputTokens(response, model) {
7838
+ const output = Array.isArray(response.output) ? response.output : [];
7839
+ return clampTokens(output.reduce((total, item) => {
7840
+ if (!isRecord6(item))
7841
+ return total + countOpenAIJsonLikeTokens(item, model);
7842
+ const content = Array.isArray(item.content) ? item.content : [];
7843
+ return total + content.reduce((contentTotal, part) => {
7844
+ if (isRecord6(part) && typeof part.text === "string")
7845
+ return contentTotal + countOpenAIStringTokens(part.text, model);
7846
+ return contentTotal + countOpenAIJsonLikeTokens(part, model);
7847
+ }, 0) + countOpenAIStringTokens(item.arguments, model);
7848
+ }, 0)) ?? 0;
7849
+ }
7850
+ function isDoneSentinel(chunk) {
7851
+ return chunk.data === "[DONE]" || chunk.type === "[DONE]";
7451
7852
  }
7452
7853
  function estimateAnthropicInputTokens(body) {
7453
7854
  const system = asString2(body.system) ?? "";
@@ -7505,71 +7906,193 @@ function createOpenAIAdapter() {
7505
7906
  provider: "openai",
7506
7907
  capabilities: getProviderTokenAccountingCapabilities("openai"),
7507
7908
  consumeRequest(state, body) {
7508
- state.requestBody = body;
7509
- state.model = asString2(body.model) ?? state.model ?? "unknown";
7510
- state.estimatedInputTokens = estimateOpenAIInputTokens(body);
7511
- state.estimatedInputAuthority = "local";
7909
+ storeRequestForEstimate(state, body, ["input", "messages", "tools"]);
7910
+ setSessionModel(state, body.model);
7512
7911
  },
7513
7912
  consumeResponse(state, body) {
7514
- const usage = readRecord(body, "usage");
7515
- const promptTokens = readNumber(usage, "prompt_tokens");
7516
- const completionTokens = readNumber(usage, "completion_tokens");
7517
- const promptDetails = readRecord(usage, "prompt_tokens_details");
7518
- const cacheReadTokens = readNumber(promptDetails, "cached_tokens") ?? 0;
7913
+ const usage = openAIUsage(body);
7914
+ const promptTokens = readNumber(usage, "prompt_tokens") ?? readNumber(usage, "input_tokens");
7915
+ const completionTokens = readNumber(usage, "completion_tokens") ?? readNumber(usage, "output_tokens");
7916
+ const responseStatus = asString2(readRecord(body, "response")?.status) ?? asString2(body.status);
7917
+ const isIncomplete = responseStatus === "incomplete" || asString2(body.type) === "response.incomplete";
7918
+ const isFailed = responseStatus === "failed" || asString2(body.type) === "response.failed";
7919
+ const promptDetails = readRecord(usage, "prompt_tokens_details") ?? readRecord(usage, "input_tokens_details");
7920
+ const cacheReadTokens = readNumber(promptDetails, "cached_tokens");
7921
+ if (isDeferred(state)) {
7922
+ if (promptTokens !== undefined) {
7923
+ state.estimatedInputTokens = promptTokens;
7924
+ state.estimatedInputAuthority = "official";
7925
+ }
7926
+ if (completionTokens !== undefined) {
7927
+ state.outputTokens = completionTokens;
7928
+ state.outputAuthority = "official";
7929
+ clearObservedOutput(state);
7930
+ }
7931
+ if (cacheReadTokens !== undefined)
7932
+ state.cacheReadTokens = cacheReadTokens;
7933
+ if (!isIncomplete && !isFailed && !isErrorResponse(body))
7934
+ observeOpenAIOutput(state, body);
7935
+ state.protocolTerminalSeen = true;
7936
+ state.deferredOutcome = isIncomplete || isFailed || isErrorResponse(body) ? "failed" : "completed";
7937
+ return createPartialTokenAccountingEvent(state, {
7938
+ inputTokens: state.estimatedInputTokens,
7939
+ outputTokens: state.outputTokens,
7940
+ cacheReadTokens: state.cacheReadTokens,
7941
+ cacheWriteTokens: state.cacheWriteTokens,
7942
+ inputAuthority: state.estimatedInputAuthority ?? "none",
7943
+ outputAuthority: state.outputAuthority ?? "none"
7944
+ });
7945
+ }
7946
+ if (promptTokens === undefined)
7947
+ estimateMissingInput(state);
7948
+ const responseHasOutput = hasOpenAIOutput(body) && !isIncomplete && !isFailed && !isErrorResponse(body);
7949
+ const estimatedOutput = completionTokens === undefined && responseHasOutput ? Array.isArray(body.output) ? estimateOpenAIResponsesOutputTokens(body, state.model) : estimateOpenAIResponseOutputTokens(body, state.model) : undefined;
7519
7950
  if (promptTokens !== undefined || completionTokens !== undefined) {
7520
7951
  state.finalReceived = true;
7521
7952
  return finalizeTokenAccountingEvent(state, {
7522
7953
  inputTokens: promptTokens ?? state.estimatedInputTokens,
7523
- outputTokens: completionTokens ?? estimateOpenAIResponseOutputTokens(body, state.model),
7954
+ outputTokens: completionTokens ?? estimatedOutput,
7524
7955
  cacheReadTokens,
7525
- cacheWriteTokens: 0,
7956
+ cacheWriteTokens: undefined,
7526
7957
  inputAuthority: promptTokens !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
7527
- outputAuthority: completionTokens !== undefined ? "official" : "heuristic"
7528
- });
7958
+ outputAuthority: completionTokens !== undefined ? "official" : estimatedOutput === undefined ? "none" : "local"
7959
+ }, isIncomplete || isFailed ? "failed" : "completed");
7529
7960
  }
7530
- const outputTokens = estimateOpenAIResponseOutputTokens(body, state.model);
7961
+ const outputTokens = estimatedOutput;
7962
+ state.finalReceived = true;
7531
7963
  state.outputTokens = outputTokens;
7532
- state.outputAuthority = "local";
7964
+ state.outputAuthority = outputTokens === undefined ? "none" : "local";
7533
7965
  return finalizeTokenAccountingEvent(state, {
7534
7966
  inputTokens: state.estimatedInputTokens,
7535
7967
  outputTokens,
7536
- inputAuthority: state.estimatedInputAuthority ?? "heuristic",
7537
- outputAuthority: "local"
7538
- });
7968
+ inputAuthority: state.estimatedInputAuthority ?? "none",
7969
+ outputAuthority: state.outputAuthority
7970
+ }, isIncomplete || isFailed ? "failed" : "completed");
7539
7971
  },
7540
7972
  consumeStreamChunk(state, chunk) {
7541
- const usage = readRecord(chunk, "usage");
7542
- const promptTokens = readNumber(usage, "prompt_tokens");
7543
- const completionTokens = readNumber(usage, "completion_tokens");
7544
- if ((promptTokens !== undefined || completionTokens !== undefined) && isOpenAIFinalUsageOnlyChunk(chunk)) {
7973
+ if (state.finalReceived || isDoneSentinel(chunk))
7974
+ return null;
7975
+ const response = readRecord(chunk, "response");
7976
+ const usage = openAIUsage(chunk);
7977
+ const promptTokens = readNumber(usage, "prompt_tokens") ?? readNumber(usage, "input_tokens");
7978
+ const completionTokens = readNumber(usage, "completion_tokens") ?? readNumber(usage, "output_tokens");
7979
+ const promptDetails = readRecord(usage, "prompt_tokens_details") ?? readRecord(usage, "input_tokens_details");
7980
+ if (promptTokens !== undefined) {
7981
+ state.estimatedInputTokens = promptTokens;
7982
+ state.estimatedInputAuthority = "official";
7983
+ }
7984
+ if (completionTokens !== undefined) {
7985
+ state.outputTokens = completionTokens;
7986
+ state.outputAuthority = "official";
7987
+ clearObservedOutput(state);
7988
+ }
7989
+ const reportedCache = readNumber(promptDetails, "cached_tokens");
7990
+ if (reportedCache !== undefined)
7991
+ state.cacheReadTokens = reportedCache;
7992
+ const responseType = asString2(chunk.type);
7993
+ const responseStatus = asString2(response?.status);
7994
+ const responseCompleted = responseType === "response.completed" || responseStatus === "completed";
7995
+ observeOpenAIOutput(state, chunk, !responseCompleted);
7996
+ const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
7997
+ if (choices.some((choice) => isRecord6(choice) && asString2(choice.finish_reason) !== undefined && choice.finish_reason !== null)) {
7998
+ state.protocolTerminalSeen = true;
7999
+ if (isDeferred(state))
8000
+ state.deferredOutcome = "completed";
8001
+ }
8002
+ if (isDeferred(state)) {
8003
+ if (responseCompleted) {
8004
+ state.deferredOutcome = "completed";
8005
+ state.protocolTerminalSeen = true;
8006
+ if (completionTokens === undefined && state.outputAuthority !== "official" && Array.isArray(response?.output)) {
8007
+ replaceObservedOutput(state);
8008
+ observeOpenAIOutput(state, { response });
8009
+ }
8010
+ } else if (responseType === "response.incomplete" || responseType === "response.failed" || responseStatus === "incomplete" || responseStatus === "failed") {
8011
+ state.deferredOutcome = "failed";
8012
+ state.protocolTerminalSeen = true;
8013
+ }
8014
+ return createPartialTokenAccountingEvent(state, {
8015
+ inputTokens: state.estimatedInputTokens,
8016
+ outputTokens: state.outputTokens,
8017
+ cacheReadTokens: state.cacheReadTokens,
8018
+ cacheWriteTokens: state.cacheWriteTokens,
8019
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8020
+ outputAuthority: state.outputAuthority ?? "none"
8021
+ });
8022
+ }
8023
+ if (responseCompleted) {
8024
+ if (completionTokens === undefined && state.outputAuthority !== "official" && Array.isArray(response?.output)) {
8025
+ replaceObservedOutput(state);
8026
+ observeOpenAIOutput(state, { response });
8027
+ }
8028
+ if (completionTokens === undefined)
8029
+ finalizeMissingStreamOutput(state);
8030
+ if (promptTokens === undefined)
8031
+ estimateMissingInput(state);
7545
8032
  state.finalReceived = true;
7546
8033
  return finalizeTokenAccountingEvent(state, {
7547
8034
  inputTokens: promptTokens ?? state.estimatedInputTokens,
7548
- outputTokens: completionTokens ?? state.outputTokens ?? 0,
7549
- cacheReadTokens: readNumber(readRecord(usage, "prompt_tokens_details"), "cached_tokens") ?? 0,
7550
- cacheWriteTokens: 0,
8035
+ outputTokens: completionTokens ?? state.outputTokens,
8036
+ cacheReadTokens: readNumber(readRecord(usage, "input_tokens_details") ?? readRecord(usage, "prompt_tokens_details"), "cached_tokens") ?? state.cacheReadTokens,
8037
+ cacheWriteTokens: state.cacheWriteTokens,
7551
8038
  inputAuthority: promptTokens !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
7552
- outputAuthority: completionTokens !== undefined ? "official" : "partial"
8039
+ outputAuthority: completionTokens !== undefined ? "official" : state.outputAuthority ?? "none"
7553
8040
  });
7554
8041
  }
7555
- const incrementalOutput = estimateOpenAIResponseOutputTokens(chunk, state.model);
7556
- if (incrementalOutput > 0) {
7557
- state.outputTokens = (state.outputTokens ?? 0) + incrementalOutput;
7558
- state.outputAuthority = "partial";
8042
+ if (responseType === "response.incomplete" || responseType === "response.failed" || responseStatus === "incomplete" || responseStatus === "failed") {
8043
+ state.finalReceived = true;
8044
+ const promptDetails = readRecord(usage, "input_tokens_details") ?? readRecord(usage, "prompt_tokens_details");
8045
+ const input = promptTokens === undefined ? undefined : promptTokens;
8046
+ const output = completionTokens;
8047
+ state.estimatedInputTokens = input ?? state.estimatedInputTokens;
8048
+ state.outputTokens = output ?? state.outputTokens;
8049
+ state.cacheReadTokens = readNumber(promptDetails, "cached_tokens") ?? state.cacheReadTokens;
8050
+ if (output === undefined)
8051
+ finalizeMissingStreamOutput(state);
8052
+ if (input === undefined)
8053
+ estimateMissingInput(state);
8054
+ return finalizeTokenAccountingEvent(state, {
8055
+ inputTokens: input ?? state.estimatedInputTokens,
8056
+ outputTokens: output ?? state.outputTokens,
8057
+ cacheReadTokens: state.cacheReadTokens,
8058
+ cacheWriteTokens: state.cacheWriteTokens,
8059
+ inputAuthority: input !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
8060
+ outputAuthority: output !== undefined ? "official" : state.outputAuthority ?? "none"
8061
+ }, "failed");
7559
8062
  }
7560
8063
  return createPartialTokenAccountingEvent(state, {
8064
+ inputTokens: state.estimatedInputTokens,
7561
8065
  outputTokens: state.outputTokens,
7562
- inputAuthority: "none",
7563
- outputAuthority: state.outputTokens !== undefined ? "partial" : "none"
8066
+ cacheReadTokens: state.cacheReadTokens,
8067
+ cacheWriteTokens: state.cacheWriteTokens,
8068
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8069
+ outputAuthority: state.outputAuthority ?? "none"
7564
8070
  });
7565
8071
  },
7566
8072
  finalizeAbortedStream(state) {
8073
+ finalizeMissingStreamOutput(state, true);
8074
+ estimateMissingInput(state);
7567
8075
  return finalizeTokenAccountingEvent(state, {
7568
8076
  inputTokens: state.estimatedInputTokens,
7569
- outputTokens: state.outputTokens ?? 0,
7570
- inputAuthority: state.estimatedInputAuthority ?? "heuristic",
7571
- outputAuthority: state.outputTokens !== undefined ? state.outputAuthority ?? "partial" : "heuristic"
8077
+ outputTokens: state.outputTokens,
8078
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8079
+ outputAuthority: state.outputAuthority ?? "none"
7572
8080
  }, "aborted");
8081
+ },
8082
+ finalizeCompletedStream(state) {
8083
+ if (state.finalReceived)
8084
+ return createPartialTokenAccountingEvent(state);
8085
+ state.finalReceived = true;
8086
+ finalizeMissingStreamOutput(state);
8087
+ estimateMissingInput(state);
8088
+ return finalizeTokenAccountingEvent(state, {
8089
+ inputTokens: state.estimatedInputTokens,
8090
+ outputTokens: state.outputTokens,
8091
+ cacheReadTokens: state.cacheReadTokens,
8092
+ cacheWriteTokens: state.cacheWriteTokens,
8093
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8094
+ outputAuthority: state.outputAuthority ?? "none"
8095
+ }, state.deferredOutcome ?? (state.protocolTerminalSeen ? "completed" : "failed"));
7573
8096
  }
7574
8097
  };
7575
8098
  }
@@ -7578,126 +8101,207 @@ function createAnthropicAdapter() {
7578
8101
  provider: "anthropic",
7579
8102
  capabilities: getProviderTokenAccountingCapabilities("anthropic"),
7580
8103
  consumeRequest(state, body) {
7581
- state.requestBody = body;
7582
- state.model = asString2(body.model) ?? state.model ?? "unknown";
7583
- state.estimatedInputTokens = estimateAnthropicInputTokens(body);
7584
- state.estimatedInputAuthority = "local";
8104
+ storeRequestForEstimate(state, body, ["system", "messages", "tools"]);
8105
+ setSessionModel(state, body.model);
7585
8106
  },
7586
8107
  consumeResponse(state, body) {
7587
8108
  const usage = readRecord(body, "usage");
7588
8109
  const inputTokens = readNumber(usage, "input_tokens");
7589
8110
  const outputTokens = readNumber(usage, "output_tokens");
7590
- const cacheWriteTokens = readNumber(usage, "cache_creation_input_tokens") ?? 0;
7591
- const cacheReadTokens = readNumber(usage, "cache_read_input_tokens") ?? 0;
7592
- if (inputTokens !== undefined || outputTokens !== undefined) {
8111
+ const cacheWriteTokens = readNumber(usage, "cache_creation_input_tokens");
8112
+ const cacheReadTokens = readNumber(usage, "cache_read_input_tokens");
8113
+ const totalInputTokens = inputTokens === undefined ? undefined : safeTokenSum(inputTokens, cacheWriteTokens ?? 0, cacheReadTokens ?? 0);
8114
+ if (isDeferred(state)) {
8115
+ if (totalInputTokens !== undefined) {
8116
+ state.estimatedInputTokens = totalInputTokens;
8117
+ state.estimatedInputAuthority = "official";
8118
+ }
8119
+ if (outputTokens !== undefined) {
8120
+ state.outputTokens = outputTokens;
8121
+ state.outputAuthority = "official";
8122
+ clearObservedOutput(state);
8123
+ }
8124
+ if (cacheReadTokens !== undefined)
8125
+ state.cacheReadTokens = cacheReadTokens;
8126
+ if (cacheWriteTokens !== undefined)
8127
+ state.cacheWriteTokens = cacheWriteTokens;
8128
+ const failed = isDeferredFailure(body);
8129
+ if (!failed)
8130
+ observeAnthropicOutput(state, body);
8131
+ state.protocolTerminalSeen = true;
8132
+ state.deferredOutcome = failed ? "failed" : "completed";
8133
+ return createPartialTokenAccountingEvent(state, {
8134
+ inputTokens: state.estimatedInputTokens,
8135
+ outputTokens: state.outputTokens,
8136
+ cacheReadTokens: state.cacheReadTokens,
8137
+ cacheWriteTokens: state.cacheWriteTokens,
8138
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8139
+ outputAuthority: state.outputAuthority ?? "none"
8140
+ });
8141
+ }
8142
+ if (totalInputTokens === undefined)
8143
+ estimateMissingInput(state);
8144
+ const estimatedOutput = outputTokens === undefined && hasAnthropicOutput(body) && !isErrorResponse(body) ? estimateAnthropicOutputTokens(body) : undefined;
8145
+ if (usage && (inputTokens !== undefined || outputTokens !== undefined || readNumber(usage, "cache_creation_input_tokens") !== undefined || readNumber(usage, "cache_read_input_tokens") !== undefined)) {
7593
8146
  state.finalReceived = true;
7594
8147
  return finalizeTokenAccountingEvent(state, {
7595
- inputTokens: inputTokens ?? state.estimatedInputTokens,
7596
- outputTokens: outputTokens ?? estimateAnthropicOutputTokens(body),
8148
+ inputTokens: totalInputTokens ?? state.estimatedInputTokens,
8149
+ outputTokens: outputTokens ?? estimatedOutput,
7597
8150
  cacheReadTokens,
7598
8151
  cacheWriteTokens,
7599
- inputAuthority: inputTokens !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
7600
- outputAuthority: outputTokens !== undefined ? "official" : "heuristic"
8152
+ inputAuthority: totalInputTokens !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
8153
+ outputAuthority: outputTokens !== undefined ? "official" : estimatedOutput === undefined ? "none" : "local"
7601
8154
  });
7602
8155
  }
7603
8156
  return finalizeTokenAccountingEvent(state, {
7604
8157
  inputTokens: state.estimatedInputTokens,
7605
- outputTokens: estimateAnthropicOutputTokens(body),
7606
- inputAuthority: state.estimatedInputAuthority ?? "heuristic",
7607
- outputAuthority: "local"
8158
+ outputTokens: estimatedOutput,
8159
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8160
+ outputAuthority: estimatedOutput === undefined ? "none" : "local"
7608
8161
  });
7609
8162
  },
7610
8163
  consumeStreamChunk(state, chunk) {
7611
- const anthropicState = getAnthropicSessionState(state);
8164
+ if (state.finalReceived || isDoneSentinel(chunk))
8165
+ return null;
7612
8166
  const type = asString2(chunk.type);
8167
+ if (isDeferred(state) && type === "error") {
8168
+ state.protocolTerminalSeen = true;
8169
+ state.deferredOutcome = "failed";
8170
+ return createPartialTokenAccountingEvent(state, {
8171
+ inputTokens: state.estimatedInputTokens,
8172
+ outputTokens: state.outputTokens,
8173
+ cacheReadTokens: state.cacheReadTokens,
8174
+ cacheWriteTokens: state.cacheWriteTokens,
8175
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8176
+ outputAuthority: state.outputAuthority ?? "none"
8177
+ });
8178
+ }
7613
8179
  if (type === "message_start") {
7614
8180
  const message = readRecord(chunk, "message");
7615
8181
  const usage = readRecord(message, "usage");
7616
- state.model = asString2(message?.model) ?? state.model ?? "unknown";
7617
- state.estimatedInputTokens = readNumber(usage, "input_tokens") ?? state.estimatedInputTokens;
7618
- state.estimatedInputAuthority = state.estimatedInputTokens !== undefined ? "official" : state.estimatedInputAuthority ?? "local";
7619
- state.cacheWriteTokens = readNumber(usage, "cache_creation_input_tokens") ?? 0;
7620
- state.cacheReadTokens = readNumber(usage, "cache_read_input_tokens") ?? 0;
7621
- state.outputTokens = readNumber(usage, "output_tokens") ?? 0;
7622
- state.outputAuthority = "none";
7623
- anthropicState.anthropicEstimatedOutputTokens = 0;
7624
- anthropicState.anthropicContentBlocks?.clear();
8182
+ setSessionModel(state, message?.model);
8183
+ state.cacheWriteTokens = readNumber(usage, "cache_creation_input_tokens") ?? state.cacheWriteTokens;
8184
+ state.cacheReadTokens = readNumber(usage, "cache_read_input_tokens") ?? state.cacheReadTokens;
8185
+ const rawInputTokens = readNumber(usage, "input_tokens");
8186
+ const totalInputTokens = rawInputTokens === undefined ? undefined : safeTokenSum(rawInputTokens, state.cacheWriteTokens ?? 0, state.cacheReadTokens ?? 0);
8187
+ state.estimatedInputTokens = totalInputTokens ?? state.estimatedInputTokens;
8188
+ if (totalInputTokens !== undefined)
8189
+ state.estimatedInputAuthority = "official";
8190
+ const initialOutputTokens = readNumber(usage, "output_tokens");
8191
+ state.outputTokens = initialOutputTokens ?? state.outputTokens;
8192
+ state.outputAuthority = initialOutputTokens !== undefined ? "official" : "none";
8193
+ if (initialOutputTokens !== undefined)
8194
+ clearObservedOutput(state);
7625
8195
  return buildAnthropicStreamingEvent(state, {
7626
8196
  inputAuthority: state.estimatedInputAuthority ?? "none",
7627
- outputAuthority: "none"
8197
+ outputAuthority: state.outputAuthority
7628
8198
  });
7629
8199
  }
7630
8200
  if (type === "content_block_start") {
7631
- const index = readNumber(chunk, "index") ?? 0;
7632
8201
  const contentBlock = readRecord(chunk, "content_block");
7633
- anthropicState.anthropicContentBlocks?.set(index, {
7634
- type: asString2(contentBlock?.type),
7635
- start: contentBlock,
7636
- text: asString2(contentBlock?.text) ?? "",
7637
- toolUseId: asString2(contentBlock?.id),
7638
- toolName: asString2(contentBlock?.name),
7639
- toolInputJson: ""
7640
- });
7641
- updateAnthropicEstimatedOutput(state);
8202
+ if (contentBlock?.text !== undefined) {
8203
+ if (isDeferred(state))
8204
+ observeBoundedOutput(state, contentBlock.text);
8205
+ else
8206
+ observeOutput(state, contentBlock.text);
8207
+ }
7642
8208
  return buildAnthropicStreamingEvent(state);
7643
8209
  }
7644
8210
  if (type === "content_block_delta") {
7645
- const index = readNumber(chunk, "index") ?? 0;
7646
8211
  const delta = readRecord(chunk, "delta");
7647
- const block = anthropicState.anthropicContentBlocks?.get(index) ?? {};
7648
8212
  const deltaType = asString2(delta?.type);
7649
8213
  if (deltaType === "text_delta") {
7650
- block.type = block.type ?? "text";
7651
- block.text = `${block.text ?? ""}${asString2(delta?.text) ?? ""}`;
7652
- }
7653
- if (deltaType === "input_json_delta") {
7654
- block.type = block.type ?? "tool_use";
7655
- block.toolInputJson = `${block.toolInputJson ?? ""}${asString2(delta?.partial_json) ?? ""}`;
8214
+ if (isDeferred(state))
8215
+ observeBoundedOutput(state, delta?.text);
8216
+ else
8217
+ observeOutput(state, delta?.text);
7656
8218
  }
7657
- anthropicState.anthropicContentBlocks?.set(index, block);
7658
- updateAnthropicEstimatedOutput(state);
8219
+ if (deltaType === "input_json_delta")
8220
+ observeOutput(state, delta?.partial_json);
7659
8221
  return buildAnthropicStreamingEvent(state);
7660
8222
  }
7661
8223
  if (type === "content_block_stop") {
7662
- updateAnthropicEstimatedOutput(state);
7663
8224
  return buildAnthropicStreamingEvent(state);
7664
8225
  }
7665
8226
  if (type === "message_delta") {
7666
8227
  const usage = readRecord(chunk, "usage");
7667
8228
  const outputTokens = readNumber(usage, "output_tokens");
8229
+ const rawInputTokens = readNumber(usage, "input_tokens");
8230
+ if (rawInputTokens !== undefined) {
8231
+ state.cacheWriteTokens = readNumber(usage, "cache_creation_input_tokens") ?? state.cacheWriteTokens;
8232
+ state.cacheReadTokens = readNumber(usage, "cache_read_input_tokens") ?? state.cacheReadTokens;
8233
+ const totalInput = safeTokenSum(rawInputTokens, state.cacheWriteTokens ?? 0, state.cacheReadTokens ?? 0);
8234
+ state.estimatedInputTokens = totalInput ?? state.estimatedInputTokens;
8235
+ if (totalInput !== undefined)
8236
+ state.estimatedInputAuthority = "official";
8237
+ }
7668
8238
  if (outputTokens !== undefined) {
7669
8239
  state.outputTokens = outputTokens;
7670
- state.outputAuthority = "partial";
8240
+ state.outputAuthority = "official";
8241
+ clearObservedOutput(state);
7671
8242
  }
8243
+ state.cacheWriteTokens = readNumber(usage, "cache_creation_input_tokens") ?? state.cacheWriteTokens;
8244
+ state.cacheReadTokens = readNumber(usage, "cache_read_input_tokens") ?? state.cacheReadTokens;
7672
8245
  return buildAnthropicStreamingEvent(state, {
7673
- outputAuthority: state.outputTokens !== undefined ? "partial" : "none"
8246
+ outputAuthority: state.outputTokens !== undefined ? state.outputAuthority ?? "none" : "none"
7674
8247
  });
7675
8248
  }
7676
8249
  if (type === "message_stop") {
7677
8250
  if (state.finalReceived) {
7678
8251
  return null;
7679
8252
  }
8253
+ state.protocolTerminalSeen = true;
8254
+ if (isDeferred(state)) {
8255
+ state.deferredOutcome ??= "completed";
8256
+ return createPartialTokenAccountingEvent(state, {
8257
+ inputTokens: state.estimatedInputTokens,
8258
+ outputTokens: state.outputTokens,
8259
+ cacheReadTokens: state.cacheReadTokens,
8260
+ cacheWriteTokens: state.cacheWriteTokens,
8261
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8262
+ outputAuthority: state.outputAuthority ?? "none"
8263
+ });
8264
+ }
7680
8265
  state.finalReceived = true;
8266
+ finalizeMissingStreamOutput(state);
8267
+ estimateMissingInput(state);
7681
8268
  return finalizeTokenAccountingEvent(state, {
7682
8269
  inputTokens: state.estimatedInputTokens,
7683
8270
  outputTokens: state.outputTokens,
7684
- cacheReadTokens: state.cacheReadTokens ?? 0,
7685
- cacheWriteTokens: state.cacheWriteTokens ?? 0,
8271
+ cacheReadTokens: state.cacheReadTokens,
8272
+ cacheWriteTokens: state.cacheWriteTokens,
7686
8273
  inputAuthority: state.estimatedInputAuthority ?? "none",
7687
- outputAuthority: state.outputAuthority === "partial" || state.outputAuthority === "official" ? "official" : state.outputAuthority ?? "none"
8274
+ outputAuthority: state.outputAuthority ?? "none"
7688
8275
  });
7689
8276
  }
7690
8277
  return null;
7691
8278
  },
7692
8279
  finalizeAbortedStream(state) {
8280
+ finalizeMissingStreamOutput(state, true);
8281
+ estimateMissingInput(state);
7693
8282
  return finalizeTokenAccountingEvent(state, {
7694
8283
  inputTokens: state.estimatedInputTokens,
7695
8284
  outputTokens: state.outputTokens,
7696
- cacheReadTokens: state.cacheReadTokens ?? 0,
7697
- cacheWriteTokens: state.cacheWriteTokens ?? 0,
7698
- inputAuthority: state.estimatedInputAuthority ?? "heuristic",
8285
+ cacheReadTokens: state.cacheReadTokens,
8286
+ cacheWriteTokens: state.cacheWriteTokens,
8287
+ inputAuthority: state.estimatedInputAuthority ?? "none",
7699
8288
  outputAuthority: state.outputTokens !== undefined ? state.outputAuthority ?? "partial" : "none"
7700
8289
  }, "aborted");
8290
+ },
8291
+ finalizeCompletedStream(state) {
8292
+ if (state.finalReceived)
8293
+ return buildAnthropicStreamingEvent(state);
8294
+ state.finalReceived = true;
8295
+ finalizeMissingStreamOutput(state);
8296
+ estimateMissingInput(state);
8297
+ return finalizeTokenAccountingEvent(state, {
8298
+ inputTokens: state.estimatedInputTokens,
8299
+ outputTokens: state.outputTokens,
8300
+ cacheReadTokens: state.cacheReadTokens,
8301
+ cacheWriteTokens: state.cacheWriteTokens,
8302
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8303
+ outputAuthority: state.outputTokens !== undefined ? state.outputAuthority ?? "partial" : "none"
8304
+ }, state.deferredOutcome ?? (state.protocolTerminalSeen ? "completed" : "failed"));
7701
8305
  }
7702
8306
  };
7703
8307
  }
@@ -7706,71 +8310,158 @@ function createGeminiAdapter() {
7706
8310
  provider: "gemini",
7707
8311
  capabilities: getProviderTokenAccountingCapabilities("gemini"),
7708
8312
  consumeRequest(state, body) {
7709
- state.requestBody = body;
7710
- state.model = asString2(body.model) ?? state.model ?? "unknown";
7711
- state.estimatedInputTokens = estimateGeminiInputTokens(body);
7712
- state.estimatedInputAuthority = "local";
8313
+ storeRequestForEstimate(state, body, ["contents", "systemInstruction"]);
8314
+ setSessionModel(state, body.model);
7713
8315
  },
7714
8316
  consumeResponse(state, body) {
7715
8317
  const usageMetadata = readRecord(body, "usageMetadata");
7716
8318
  const promptTokenCount = readNumber(usageMetadata, "promptTokenCount");
7717
8319
  const candidatesTokenCount = readNumber(usageMetadata, "candidatesTokenCount");
7718
- const cachedContentTokenCount = readNumber(usageMetadata, "cachedContentTokenCount") ?? 0;
8320
+ const thoughtsTokenCount = readNumber(usageMetadata, "thoughtsTokenCount") ?? 0;
8321
+ const toolUsePromptTokenCount = readNumber(usageMetadata, "toolUsePromptTokenCount") ?? 0;
8322
+ const cachedContentTokenCount = readNumber(usageMetadata, "cachedContentTokenCount");
8323
+ if (isDeferred(state)) {
8324
+ const input = promptTokenCount === undefined ? undefined : safeTokenSum(promptTokenCount, toolUsePromptTokenCount);
8325
+ const output = candidatesTokenCount === undefined ? undefined : safeTokenSum(candidatesTokenCount, thoughtsTokenCount);
8326
+ if (input !== undefined) {
8327
+ state.estimatedInputTokens = input;
8328
+ state.estimatedInputAuthority = "official";
8329
+ }
8330
+ if (output !== undefined) {
8331
+ state.outputTokens = output;
8332
+ state.outputAuthority = "official";
8333
+ clearObservedOutput(state);
8334
+ }
8335
+ if (cachedContentTokenCount !== undefined)
8336
+ state.cacheReadTokens = cachedContentTokenCount;
8337
+ const failed = isDeferredFailure(body);
8338
+ if (!failed)
8339
+ observeGeminiOutput(state, body);
8340
+ state.protocolTerminalSeen = true;
8341
+ state.deferredOutcome = failed ? "failed" : "completed";
8342
+ return createPartialTokenAccountingEvent(state, {
8343
+ inputTokens: state.estimatedInputTokens,
8344
+ outputTokens: state.outputTokens,
8345
+ cacheReadTokens: state.cacheReadTokens,
8346
+ cacheWriteTokens: undefined,
8347
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8348
+ outputAuthority: state.outputAuthority ?? "none"
8349
+ });
8350
+ }
8351
+ if (promptTokenCount === undefined)
8352
+ estimateMissingInput(state);
8353
+ const estimatedOutput = candidatesTokenCount === undefined && hasGeminiOutput(body) && !isErrorResponse(body) ? estimateGeminiOutputTokens(body) : undefined;
7719
8354
  if (promptTokenCount !== undefined || candidatesTokenCount !== undefined) {
7720
8355
  state.finalReceived = true;
7721
8356
  return finalizeTokenAccountingEvent(state, {
7722
- inputTokens: promptTokenCount ?? state.estimatedInputTokens,
7723
- outputTokens: candidatesTokenCount ?? estimateGeminiOutputTokens(body),
8357
+ inputTokens: promptTokenCount === undefined ? state.estimatedInputTokens : safeTokenSum(promptTokenCount, toolUsePromptTokenCount) ?? state.estimatedInputTokens,
8358
+ outputTokens: candidatesTokenCount === undefined ? estimatedOutput : safeTokenSum(candidatesTokenCount, thoughtsTokenCount) ?? estimatedOutput,
7724
8359
  cacheReadTokens: cachedContentTokenCount,
7725
- cacheWriteTokens: 0,
7726
- inputAuthority: promptTokenCount !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
7727
- outputAuthority: candidatesTokenCount !== undefined ? "official" : "heuristic"
8360
+ cacheWriteTokens: undefined,
8361
+ inputAuthority: promptTokenCount !== undefined && safeTokenSum(promptTokenCount, toolUsePromptTokenCount) !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
8362
+ outputAuthority: candidatesTokenCount !== undefined && safeTokenSum(candidatesTokenCount, thoughtsTokenCount) !== undefined ? "official" : estimatedOutput === undefined ? "none" : "local"
7728
8363
  });
7729
8364
  }
7730
- const outputTokens = estimateGeminiOutputTokens(body);
8365
+ const outputTokens = estimatedOutput;
8366
+ state.finalReceived = true;
7731
8367
  state.outputTokens = outputTokens;
7732
- state.outputAuthority = "local";
8368
+ state.outputAuthority = outputTokens === undefined ? "none" : "local";
7733
8369
  return finalizeTokenAccountingEvent(state, {
7734
8370
  inputTokens: state.estimatedInputTokens,
7735
8371
  outputTokens,
7736
- inputAuthority: state.estimatedInputAuthority ?? "heuristic",
7737
- outputAuthority: "local"
8372
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8373
+ outputAuthority: state.outputAuthority
7738
8374
  });
7739
8375
  },
7740
8376
  consumeStreamChunk(state, chunk) {
8377
+ if (state.finalReceived || isDoneSentinel(chunk))
8378
+ return null;
8379
+ observeGeminiOutput(state, chunk);
7741
8380
  const usageMetadata = readRecord(chunk, "usageMetadata");
7742
8381
  const promptTokenCount = readNumber(usageMetadata, "promptTokenCount");
7743
8382
  const candidatesTokenCount = readNumber(usageMetadata, "candidatesTokenCount");
7744
- if (promptTokenCount !== undefined || candidatesTokenCount !== undefined) {
7745
- state.finalReceived = true;
7746
- return finalizeTokenAccountingEvent(state, {
7747
- inputTokens: promptTokenCount ?? state.estimatedInputTokens,
7748
- outputTokens: candidatesTokenCount ?? state.outputTokens,
7749
- cacheReadTokens: readNumber(usageMetadata, "cachedContentTokenCount") ?? 0,
7750
- cacheWriteTokens: 0,
7751
- inputAuthority: promptTokenCount !== undefined ? "official" : state.estimatedInputAuthority ?? "heuristic",
7752
- outputAuthority: candidatesTokenCount !== undefined ? "official" : "partial"
8383
+ const thoughtsTokenCount = readNumber(usageMetadata, "thoughtsTokenCount") ?? 0;
8384
+ const toolUsePromptTokenCount = readNumber(usageMetadata, "toolUsePromptTokenCount") ?? 0;
8385
+ const reportedCacheRead = readNumber(usageMetadata, "cachedContentTokenCount");
8386
+ const candidates = Array.isArray(chunk.candidates) ? chunk.candidates : [];
8387
+ if (candidates.some((candidate) => isRecord6(candidate) && asString2(candidate.finishReason) !== undefined)) {
8388
+ state.protocolTerminalSeen = true;
8389
+ if (isDeferred(state))
8390
+ state.deferredOutcome ??= "completed";
8391
+ }
8392
+ if (isDeferred(state) && isDeferredFailure(chunk)) {
8393
+ state.protocolTerminalSeen = true;
8394
+ state.deferredOutcome = "failed";
8395
+ }
8396
+ if (isDeferred(state) && reportedCacheRead !== undefined && promptTokenCount === undefined && candidatesTokenCount === undefined) {
8397
+ state.cacheReadTokens = reportedCacheRead;
8398
+ return createPartialTokenAccountingEvent(state, {
8399
+ inputTokens: state.estimatedInputTokens,
8400
+ outputTokens: state.outputTokens,
8401
+ cacheReadTokens: state.cacheReadTokens,
8402
+ cacheWriteTokens: undefined,
8403
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8404
+ outputAuthority: state.outputAuthority ?? "none"
7753
8405
  });
7754
8406
  }
7755
- const incrementalOutput = estimateGeminiOutputTokens(chunk);
7756
- if (incrementalOutput > 0) {
7757
- state.outputTokens = sum([state.outputTokens, incrementalOutput]);
7758
- state.outputAuthority = "partial";
8407
+ if (promptTokenCount !== undefined || candidatesTokenCount !== undefined) {
8408
+ const input = promptTokenCount === undefined ? undefined : safeTokenSum(promptTokenCount, toolUsePromptTokenCount);
8409
+ const output = candidatesTokenCount === undefined ? undefined : safeTokenSum(candidatesTokenCount, thoughtsTokenCount);
8410
+ state.estimatedInputTokens = input ?? state.estimatedInputTokens;
8411
+ if (input !== undefined)
8412
+ state.estimatedInputAuthority = "official";
8413
+ state.outputTokens = output ?? state.outputTokens;
8414
+ if (output !== undefined) {
8415
+ state.outputAuthority = "official";
8416
+ clearObservedOutput(state);
8417
+ }
8418
+ const cacheRead = readNumber(usageMetadata, "cachedContentTokenCount");
8419
+ if (cacheRead !== undefined)
8420
+ state.cacheReadTokens = cacheRead;
8421
+ return createPartialTokenAccountingEvent(state, {
8422
+ inputTokens: state.estimatedInputTokens,
8423
+ outputTokens: state.outputTokens,
8424
+ cacheReadTokens: state.cacheReadTokens,
8425
+ cacheWriteTokens: undefined,
8426
+ inputAuthority: state.estimatedInputAuthority ?? "heuristic",
8427
+ outputAuthority: state.outputAuthority ?? "partial"
8428
+ });
7759
8429
  }
7760
8430
  return createPartialTokenAccountingEvent(state, {
7761
8431
  inputTokens: state.estimatedInputTokens,
7762
8432
  outputTokens: state.outputTokens,
8433
+ cacheReadTokens: state.cacheReadTokens,
8434
+ cacheWriteTokens: state.cacheWriteTokens,
7763
8435
  inputAuthority: state.estimatedInputAuthority ?? "heuristic",
7764
- outputAuthority: state.outputTokens !== undefined ? "partial" : "none"
8436
+ outputAuthority: state.outputAuthority ?? (state.outputTokens !== undefined ? "partial" : "none")
7765
8437
  });
7766
8438
  },
7767
8439
  finalizeAbortedStream(state) {
8440
+ finalizeMissingStreamOutput(state, true);
8441
+ estimateMissingInput(state);
7768
8442
  return finalizeTokenAccountingEvent(state, {
7769
8443
  inputTokens: state.estimatedInputTokens,
7770
8444
  outputTokens: state.outputTokens,
7771
- inputAuthority: state.estimatedInputAuthority ?? "heuristic",
7772
- outputAuthority: state.outputTokens !== undefined ? "local" : "heuristic"
8445
+ cacheReadTokens: state.cacheReadTokens,
8446
+ cacheWriteTokens: state.cacheWriteTokens,
8447
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8448
+ outputAuthority: state.outputAuthority ?? (state.outputTokens !== undefined ? "partial" : "none")
7773
8449
  }, "aborted");
8450
+ },
8451
+ finalizeCompletedStream(state) {
8452
+ if (state.finalReceived)
8453
+ return createPartialTokenAccountingEvent(state);
8454
+ state.finalReceived = true;
8455
+ finalizeMissingStreamOutput(state);
8456
+ estimateMissingInput(state);
8457
+ return finalizeTokenAccountingEvent(state, {
8458
+ inputTokens: state.estimatedInputTokens,
8459
+ outputTokens: state.outputTokens,
8460
+ cacheReadTokens: state.cacheReadTokens,
8461
+ cacheWriteTokens: state.cacheWriteTokens,
8462
+ inputAuthority: state.estimatedInputAuthority ?? "none",
8463
+ outputAuthority: state.outputAuthority ?? (state.outputTokens !== undefined ? "partial" : "none")
8464
+ }, state.deferredOutcome ?? (state.protocolTerminalSeen ? "completed" : "failed"));
7774
8465
  }
7775
8466
  };
7776
8467
  }
@@ -7789,8 +8480,9 @@ function getProviderAdapter(provider) {
7789
8480
  }
7790
8481
  return adapter;
7791
8482
  }
7792
- function createTokenAccountingSession(input) {
8483
+ function createTokenAccountingSession(input, options = {}) {
7793
8484
  const adapter = getProviderAdapter(input.provider);
8485
+ let terminalEvent;
7794
8486
  const state = {
7795
8487
  provider: adapter.provider,
7796
8488
  routeId: input.routeId,
@@ -7799,20 +8491,38 @@ function createTokenAccountingSession(input) {
7799
8491
  attemptId: input.attemptId,
7800
8492
  streaming: input.streaming,
7801
8493
  model: input.model,
7802
- finalReceived: false
8494
+ finalReceived: false,
8495
+ deferFinalization: options.deferFinalization === true
7803
8496
  };
7804
8497
  return {
7805
8498
  consumeRequest({ body }) {
7806
8499
  adapter.consumeRequest(state, body);
7807
8500
  },
7808
8501
  consumeResponse({ body }) {
7809
- return adapter.consumeResponse(state, body);
8502
+ const event = adapter.consumeResponse(state, body);
8503
+ if (event.final || event.outcome !== "completed")
8504
+ terminalEvent = event;
8505
+ return event;
7810
8506
  },
7811
8507
  consumeStreamChunk({ chunk }) {
7812
- return adapter.consumeStreamChunk(state, chunk);
8508
+ const event = adapter.consumeStreamChunk(state, chunk);
8509
+ if (event && (event.final || event.outcome !== "completed"))
8510
+ terminalEvent = event;
8511
+ return event;
8512
+ },
8513
+ finalizeCompletedStream() {
8514
+ if (terminalEvent)
8515
+ return terminalEvent;
8516
+ const event = adapter.finalizeCompletedStream(state);
8517
+ terminalEvent = event;
8518
+ return event;
7813
8519
  },
7814
8520
  finalizeAbortedStream() {
7815
- return adapter.finalizeAbortedStream(state);
8521
+ if (terminalEvent)
8522
+ return terminalEvent;
8523
+ const event = adapter.finalizeAbortedStream(state);
8524
+ terminalEvent = event;
8525
+ return event;
7816
8526
  }
7817
8527
  };
7818
8528
  }