@jeffusion/bungee-llms 4.5.0 → 5.1.0

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