@juspay/neurolink 11.14.0 → 11.15.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/CHANGELOG.md +1 -5
- package/dist/browser/neurolink.min.js +317 -317
- package/dist/core/geminiLoopAdapter.js +13 -7
- package/dist/providers/googleVertex/client.js +150 -125
- package/dist/types/loopEngine.d.ts +17 -1
- package/dist/types/providers.d.ts +8 -0
- package/docs-site/static/search-index.json +1 -1
- package/package.json +1 -1
|
@@ -101,13 +101,19 @@ export function createGeminiLoopAdapter(config) {
|
|
|
101
101
|
},
|
|
102
102
|
async executeStep(request, channel, signal) {
|
|
103
103
|
const rawStream = await config.sendStep(request.raw, signal);
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
// The shared helper by default: it owns usage extraction and
|
|
105
|
+
// thought-signature preservation, and it wants a StreamChannel but only
|
|
106
|
+
// ever calls `.push`, so the engine's push-only channel satisfies it.
|
|
107
|
+
//
|
|
108
|
+
// A provider whose drain genuinely differs supplies `collectStep`
|
|
109
|
+
// instead. Vertex does: it folds cumulative usage counts as deltas in
|
|
110
|
+
// its own loop, and that behaviour is characterized, so it keeps its
|
|
111
|
+
// collector rather than being quietly switched to this one.
|
|
112
|
+
const collected = config.collectStep
|
|
113
|
+
? await config.collectStep(rawStream, channel)
|
|
114
|
+
: await collectStreamChunksIncremental(rawStream, {
|
|
115
|
+
push: (chunk) => channel.push(chunk),
|
|
116
|
+
});
|
|
111
117
|
// The provider's context guard calibrates from real per-step counts;
|
|
112
118
|
// `inputTokens` is this step's full prompt size, which is what it
|
|
113
119
|
// projects the next request from.
|
|
@@ -575,6 +575,101 @@ function reclaimVertexAnthropicContext(messages, modelName, observedPromptTokens
|
|
|
575
575
|
messages.push(...rebuilt);
|
|
576
576
|
return true;
|
|
577
577
|
}
|
|
578
|
+
/**
|
|
579
|
+
* Fold one Vertex Gemini step's stream into the shape the loop engine reports.
|
|
580
|
+
*
|
|
581
|
+
* Lifted from the inline drain in executeNativeGemini3Stream so the loop can
|
|
582
|
+
* later be handed to createGeminiLoopAdapter as `collectStep`. Vertex does not
|
|
583
|
+
* share googleNativeGemini3's collector: it reads parts straight off each
|
|
584
|
+
* candidate — avoiding the SDK warning that `chunk.text` raises when
|
|
585
|
+
* thoughtSignature or functionCall parts are present — and that behaviour is
|
|
586
|
+
* characterized.
|
|
587
|
+
*
|
|
588
|
+
* `onUsageDelta` fires PER CHUNK and is not an optimisation to fold away. The
|
|
589
|
+
* drain updates the turn totals incrementally so they are correct at every
|
|
590
|
+
* point mid-stream: a step killed by an abort, the turn deadline or the stall
|
|
591
|
+
* watchdog still bills the tokens it already reported. Returning a step total
|
|
592
|
+
* for the caller to add would be arithmetically identical and operationally
|
|
593
|
+
* wrong, because a killed step never returns.
|
|
594
|
+
*/
|
|
595
|
+
async function collectVertexStreamChunks(stream, channel, hooks = {}) {
|
|
596
|
+
const rawResponseParts = [];
|
|
597
|
+
const stepFunctionCalls = [];
|
|
598
|
+
let lastFinishReason;
|
|
599
|
+
let stepInputTokens = 0;
|
|
600
|
+
let stepOutputTokens = 0;
|
|
601
|
+
let stepCacheReadTokens = 0;
|
|
602
|
+
let stepReasoningTokens = 0;
|
|
603
|
+
for await (const chunk of stream) {
|
|
604
|
+
hooks.onProgress?.();
|
|
605
|
+
// Extract raw parts from candidates FIRST
|
|
606
|
+
// This avoids using chunk.text which triggers SDK warning when
|
|
607
|
+
// non-text parts (thoughtSignature, functionCall) are present
|
|
608
|
+
const chunkRecord = chunk;
|
|
609
|
+
const candidates = chunkRecord.candidates;
|
|
610
|
+
const firstCandidate = candidates?.[0];
|
|
611
|
+
// Capture the SDK finish reason (Bug 2: previously dropped). Last
|
|
612
|
+
// non-empty value across chunks wins.
|
|
613
|
+
const chunkFinishReason = firstCandidate?.finishReason;
|
|
614
|
+
if (typeof chunkFinishReason === "string" && chunkFinishReason) {
|
|
615
|
+
lastFinishReason = chunkFinishReason;
|
|
616
|
+
}
|
|
617
|
+
const chunkContent = firstCandidate?.content;
|
|
618
|
+
if (chunkContent && Array.isArray(chunkContent.parts)) {
|
|
619
|
+
for (const part of chunkContent.parts) {
|
|
620
|
+
rawResponseParts.push(part);
|
|
621
|
+
if (typeof part.text === "string" && part.text.length > 0) {
|
|
622
|
+
channel.push({ content: part.text });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (chunk.functionCalls) {
|
|
627
|
+
stepFunctionCalls.push(...chunk.functionCalls);
|
|
628
|
+
}
|
|
629
|
+
// Extract usage metadata from chunk
|
|
630
|
+
// promptTokenCount is typically in the final chunk, candidatesTokenCount accumulates
|
|
631
|
+
const usageMetadata = chunkRecord.usageMetadata;
|
|
632
|
+
if (usageMetadata) {
|
|
633
|
+
// Take the latest promptTokenCount (usually only in final chunk)
|
|
634
|
+
if (usageMetadata.promptTokenCount !== undefined &&
|
|
635
|
+
usageMetadata.promptTokenCount > 0) {
|
|
636
|
+
hooks.onUsageDelta?.("input", usageMetadata.promptTokenCount - stepInputTokens);
|
|
637
|
+
stepInputTokens = usageMetadata.promptTokenCount;
|
|
638
|
+
// Feed the context guard the REAL prompt size of this call.
|
|
639
|
+
hooks.onUsage?.(usageMetadata.promptTokenCount, usageMetadata.candidatesTokenCount ?? 0);
|
|
640
|
+
// cachedContentTokenCount is OVERLAPPING (a subset already inside
|
|
641
|
+
// promptTokenCount). Clamp to the prompt count so an uncached
|
|
642
|
+
// step reports 0 instead of a stale cached value.
|
|
643
|
+
const chunkCacheReadTokens = Math.min(usageMetadata.cachedContentTokenCount ?? 0, usageMetadata.promptTokenCount);
|
|
644
|
+
hooks.onUsageDelta?.("cacheRead", chunkCacheReadTokens - stepCacheReadTokens);
|
|
645
|
+
stepCacheReadTokens = chunkCacheReadTokens;
|
|
646
|
+
}
|
|
647
|
+
// Take the latest candidatesTokenCount (accumulates through chunks)
|
|
648
|
+
if (usageMetadata.candidatesTokenCount !== undefined &&
|
|
649
|
+
usageMetadata.candidatesTokenCount > 0) {
|
|
650
|
+
hooks.onUsageDelta?.("output", usageMetadata.candidatesTokenCount - stepOutputTokens);
|
|
651
|
+
stepOutputTokens = usageMetadata.candidatesTokenCount;
|
|
652
|
+
}
|
|
653
|
+
// thoughtsTokenCount (thinking tokens, billed at the output
|
|
654
|
+
// rate) is NOT part of candidatesTokenCount — Gemini reports
|
|
655
|
+
// totalTokenCount = prompt + candidates + thoughts.
|
|
656
|
+
if (usageMetadata.thoughtsTokenCount !== undefined &&
|
|
657
|
+
usageMetadata.thoughtsTokenCount > 0) {
|
|
658
|
+
hooks.onUsageDelta?.("reasoning", usageMetadata.thoughtsTokenCount - stepReasoningTokens);
|
|
659
|
+
stepReasoningTokens = usageMetadata.thoughtsTokenCount;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
rawResponseParts,
|
|
665
|
+
stepFunctionCalls,
|
|
666
|
+
...(lastFinishReason ? { finishReason: lastFinishReason } : {}),
|
|
667
|
+
inputTokens: stepInputTokens,
|
|
668
|
+
outputTokens: stepOutputTokens,
|
|
669
|
+
...(stepCacheReadTokens ? { cacheReadTokens: stepCacheReadTokens } : {}),
|
|
670
|
+
...(stepReasoningTokens ? { reasoningTokens: stepReasoningTokens } : {}),
|
|
671
|
+
};
|
|
672
|
+
}
|
|
578
673
|
export class GoogleVertexProvider extends BaseProvider {
|
|
579
674
|
projectId;
|
|
580
675
|
location;
|
|
@@ -1515,74 +1610,39 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1515
1610
|
// are therefore correct at every point mid-drain — a step killed
|
|
1516
1611
|
// mid-stream (abort / turn deadline / stall watchdog) still counts
|
|
1517
1612
|
// the billed tokens it already reported.
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
// non-text parts (thoughtSignature, functionCall) are present
|
|
1527
|
-
const chunkRecord = chunk;
|
|
1528
|
-
const candidates = chunkRecord.candidates;
|
|
1529
|
-
const firstCandidate = candidates?.[0];
|
|
1530
|
-
// Capture the SDK finish reason (Bug 2: previously dropped). Last
|
|
1531
|
-
// non-empty value across chunks wins.
|
|
1532
|
-
const chunkFinishReason = firstCandidate?.finishReason;
|
|
1533
|
-
if (typeof chunkFinishReason === "string" && chunkFinishReason) {
|
|
1534
|
-
lastFinishReason = chunkFinishReason;
|
|
1535
|
-
stepFinishReason = chunkFinishReason;
|
|
1536
|
-
}
|
|
1537
|
-
const chunkContent = firstCandidate?.content;
|
|
1538
|
-
if (chunkContent && Array.isArray(chunkContent.parts)) {
|
|
1539
|
-
for (const part of chunkContent.parts) {
|
|
1540
|
-
rawResponseParts.push(part);
|
|
1541
|
-
if (typeof part.text === "string" && part.text.length > 0) {
|
|
1542
|
-
incrementalTextChunks.push(part.text);
|
|
1543
|
-
}
|
|
1613
|
+
// The drain now lives in collectVertexStreamChunks. The hooks below
|
|
1614
|
+
// are the two couplings it had to this loop, plus the per-chunk
|
|
1615
|
+
// usage deltas — those must stay per-chunk so a step killed
|
|
1616
|
+
// mid-stream still bills what it reported.
|
|
1617
|
+
const collected = await collectVertexStreamChunks(stream, {
|
|
1618
|
+
push: (chunk) => {
|
|
1619
|
+
if (chunk.content) {
|
|
1620
|
+
incrementalTextChunks.push(chunk.content);
|
|
1544
1621
|
}
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
if (usageMetadata) {
|
|
1553
|
-
// Take the latest promptTokenCount (usually only in final chunk)
|
|
1554
|
-
if (usageMetadata.promptTokenCount !== undefined &&
|
|
1555
|
-
usageMetadata.promptTokenCount > 0) {
|
|
1556
|
-
totalInputTokens +=
|
|
1557
|
-
usageMetadata.promptTokenCount - stepInputTokens;
|
|
1558
|
-
stepInputTokens = usageMetadata.promptTokenCount;
|
|
1559
|
-
// Feed the context guard the REAL prompt size of this call.
|
|
1560
|
-
contextGuard.noteUsage(usageMetadata.promptTokenCount, usageMetadata.candidatesTokenCount ?? 0);
|
|
1561
|
-
// cachedContentTokenCount is OVERLAPPING (a subset already inside
|
|
1562
|
-
// promptTokenCount). Clamp to the prompt count so an uncached
|
|
1563
|
-
// step reports 0 instead of a stale cached value.
|
|
1564
|
-
const chunkCacheReadTokens = Math.min(usageMetadata.cachedContentTokenCount ?? 0, usageMetadata.promptTokenCount);
|
|
1565
|
-
totalCacheReadTokens +=
|
|
1566
|
-
chunkCacheReadTokens - stepCacheReadTokens;
|
|
1567
|
-
stepCacheReadTokens = chunkCacheReadTokens;
|
|
1622
|
+
},
|
|
1623
|
+
}, {
|
|
1624
|
+
onProgress: () => turnClock.noteProgress(),
|
|
1625
|
+
onUsage: (input, output) => contextGuard.noteUsage(input, output),
|
|
1626
|
+
onUsageDelta: (counter, delta) => {
|
|
1627
|
+
if (counter === "input") {
|
|
1628
|
+
totalInputTokens += delta;
|
|
1568
1629
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
usageMetadata.candidatesTokenCount > 0) {
|
|
1572
|
-
totalOutputTokens +=
|
|
1573
|
-
usageMetadata.candidatesTokenCount - stepOutputTokens;
|
|
1574
|
-
stepOutputTokens = usageMetadata.candidatesTokenCount;
|
|
1630
|
+
else if (counter === "output") {
|
|
1631
|
+
totalOutputTokens += delta;
|
|
1575
1632
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
// totalTokenCount = prompt + candidates + thoughts.
|
|
1579
|
-
if (usageMetadata.thoughtsTokenCount !== undefined &&
|
|
1580
|
-
usageMetadata.thoughtsTokenCount > 0) {
|
|
1581
|
-
totalReasoningTokens +=
|
|
1582
|
-
usageMetadata.thoughtsTokenCount - stepReasoningTokens;
|
|
1583
|
-
stepReasoningTokens = usageMetadata.thoughtsTokenCount;
|
|
1633
|
+
else if (counter === "cacheRead") {
|
|
1634
|
+
totalCacheReadTokens += delta;
|
|
1584
1635
|
}
|
|
1585
|
-
|
|
1636
|
+
else {
|
|
1637
|
+
totalReasoningTokens += delta;
|
|
1638
|
+
}
|
|
1639
|
+
},
|
|
1640
|
+
});
|
|
1641
|
+
rawResponseParts.push(...collected.rawResponseParts);
|
|
1642
|
+
stepFunctionCalls.push(...collected.stepFunctionCalls);
|
|
1643
|
+
if (collected.finishReason) {
|
|
1644
|
+
stepFinishReason = collected.finishReason;
|
|
1645
|
+
lastFinishReason = collected.finishReason;
|
|
1586
1646
|
}
|
|
1587
1647
|
// Extract text from raw parts after stream completes
|
|
1588
1648
|
// This avoids SDK warning about non-text parts (thoughtSignature, functionCall)
|
|
@@ -2508,70 +2568,35 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2508
2568
|
// are therefore correct at every point mid-drain — a step killed
|
|
2509
2569
|
// mid-stream (abort / turn deadline / stall watchdog) still counts
|
|
2510
2570
|
// the billed tokens it already reported.
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
//
|
|
2516
|
-
for
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
const firstCandidate = candidates?.[0];
|
|
2524
|
-
// Capture the SDK finish reason (Bug 2: previously dropped). Last
|
|
2525
|
-
// non-empty value across chunks wins.
|
|
2526
|
-
const chunkFinishReason = firstCandidate?.finishReason;
|
|
2527
|
-
if (typeof chunkFinishReason === "string" && chunkFinishReason) {
|
|
2528
|
-
lastFinishReason = chunkFinishReason;
|
|
2529
|
-
stepFinishReason = chunkFinishReason;
|
|
2530
|
-
}
|
|
2531
|
-
const chunkContent = firstCandidate?.content;
|
|
2532
|
-
if (chunkContent && Array.isArray(chunkContent.parts)) {
|
|
2533
|
-
rawResponseParts.push(...chunkContent.parts);
|
|
2534
|
-
}
|
|
2535
|
-
if (chunk.functionCalls) {
|
|
2536
|
-
stepFunctionCalls.push(...chunk.functionCalls);
|
|
2537
|
-
}
|
|
2538
|
-
// Extract usage metadata from chunk
|
|
2539
|
-
// promptTokenCount is typically in the final chunk, candidatesTokenCount accumulates
|
|
2540
|
-
const usageMetadata = chunkRecord.usageMetadata;
|
|
2541
|
-
if (usageMetadata) {
|
|
2542
|
-
// Take the latest promptTokenCount (usually only in final chunk)
|
|
2543
|
-
if (usageMetadata.promptTokenCount !== undefined &&
|
|
2544
|
-
usageMetadata.promptTokenCount > 0) {
|
|
2545
|
-
totalInputTokens +=
|
|
2546
|
-
usageMetadata.promptTokenCount - stepInputTokens;
|
|
2547
|
-
stepInputTokens = usageMetadata.promptTokenCount;
|
|
2548
|
-
// Feed the context guard the REAL prompt size of this call.
|
|
2549
|
-
contextGuard.noteUsage(usageMetadata.promptTokenCount, usageMetadata.candidatesTokenCount ?? 0);
|
|
2550
|
-
// cachedContentTokenCount is OVERLAPPING (a subset already inside
|
|
2551
|
-
// promptTokenCount). Clamp to the prompt count so an uncached
|
|
2552
|
-
// step reports 0 instead of a stale cached value.
|
|
2553
|
-
const chunkCacheReadTokens = Math.min(usageMetadata.cachedContentTokenCount ?? 0, usageMetadata.promptTokenCount);
|
|
2554
|
-
totalCacheReadTokens +=
|
|
2555
|
-
chunkCacheReadTokens - stepCacheReadTokens;
|
|
2556
|
-
stepCacheReadTokens = chunkCacheReadTokens;
|
|
2571
|
+
// Same collector as the streaming twin. generate() returns one
|
|
2572
|
+
// result rather than streaming, so the channel is a no-op — the
|
|
2573
|
+
// stream loop uses it to fill incrementalTextChunks for replay, and
|
|
2574
|
+
// there is nothing to replay here. Everything else is identical,
|
|
2575
|
+
// including the per-chunk usage deltas that keep the turn totals
|
|
2576
|
+
// correct for a step killed mid-stream.
|
|
2577
|
+
const collected = await collectVertexStreamChunks(stream, { push: () => { } }, {
|
|
2578
|
+
onProgress: () => turnClock.noteProgress(),
|
|
2579
|
+
onUsage: (input, output) => contextGuard.noteUsage(input, output),
|
|
2580
|
+
onUsageDelta: (counter, delta) => {
|
|
2581
|
+
if (counter === "input") {
|
|
2582
|
+
totalInputTokens += delta;
|
|
2557
2583
|
}
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
usageMetadata.candidatesTokenCount > 0) {
|
|
2561
|
-
totalOutputTokens +=
|
|
2562
|
-
usageMetadata.candidatesTokenCount - stepOutputTokens;
|
|
2563
|
-
stepOutputTokens = usageMetadata.candidatesTokenCount;
|
|
2584
|
+
else if (counter === "output") {
|
|
2585
|
+
totalOutputTokens += delta;
|
|
2564
2586
|
}
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
// totalTokenCount = prompt + candidates + thoughts.
|
|
2568
|
-
if (usageMetadata.thoughtsTokenCount !== undefined &&
|
|
2569
|
-
usageMetadata.thoughtsTokenCount > 0) {
|
|
2570
|
-
totalReasoningTokens +=
|
|
2571
|
-
usageMetadata.thoughtsTokenCount - stepReasoningTokens;
|
|
2572
|
-
stepReasoningTokens = usageMetadata.thoughtsTokenCount;
|
|
2587
|
+
else if (counter === "cacheRead") {
|
|
2588
|
+
totalCacheReadTokens += delta;
|
|
2573
2589
|
}
|
|
2574
|
-
|
|
2590
|
+
else {
|
|
2591
|
+
totalReasoningTokens += delta;
|
|
2592
|
+
}
|
|
2593
|
+
},
|
|
2594
|
+
});
|
|
2595
|
+
rawResponseParts.push(...collected.rawResponseParts);
|
|
2596
|
+
stepFunctionCalls.push(...collected.stepFunctionCalls);
|
|
2597
|
+
if (collected.finishReason) {
|
|
2598
|
+
stepFinishReason = collected.finishReason;
|
|
2599
|
+
lastFinishReason = collected.finishReason;
|
|
2575
2600
|
}
|
|
2576
2601
|
// Extract text from raw parts after stream completes
|
|
2577
2602
|
// This avoids SDK warning about non-text parts (thoughtSignature, functionCall)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import type { Tool } from "./tools.js";
|
|
3
|
-
import type { NativeFunctionCall, NativeToolDeclarationsResult } from "./providers.js";
|
|
3
|
+
import type { CollectedChunkResult, NativeFunctionCall, NativeToolDeclarationsResult } from "./providers.js";
|
|
4
4
|
/**
|
|
5
5
|
* One chunk on the engine's stream.
|
|
6
6
|
*
|
|
@@ -240,6 +240,22 @@ export type GeminiLoopAdapterCoreConfig = {
|
|
|
240
240
|
* step with that step's real token counts.
|
|
241
241
|
*/
|
|
242
242
|
noteUsage?: (inputTokens: number, outputTokens: number) => void;
|
|
243
|
+
/**
|
|
244
|
+
* Fold one step's raw stream into the shape the adapter reports.
|
|
245
|
+
*
|
|
246
|
+
* Defaults to `collectStreamChunksIncremental`, which is what AI Studio and
|
|
247
|
+
* any provider sharing the googleNativeGemini3 helpers want. Vertex does
|
|
248
|
+
* NOT share them: its loop drains the stream itself, folding cumulative
|
|
249
|
+
* usage counts as deltas and capturing thought signatures in its own way,
|
|
250
|
+
* and that behaviour is characterized rather than incidental.
|
|
251
|
+
*
|
|
252
|
+
* So the collector is a hook rather than a hard-coded call. A provider
|
|
253
|
+
* whose drain differs supplies its own and keeps its measured behaviour;
|
|
254
|
+
* one that matches the shared helper passes nothing.
|
|
255
|
+
*/
|
|
256
|
+
collectStep?: (stream: unknown, channel: {
|
|
257
|
+
push(chunk: AgenticLoopChunk): void;
|
|
258
|
+
}) => Promise<CollectedChunkResult>;
|
|
243
259
|
};
|
|
244
260
|
/**
|
|
245
261
|
* Opt in to the single MALFORMED_FUNCTION_CALL retry.
|
|
@@ -1735,6 +1735,14 @@ export type NativeFunctionResponse = {
|
|
|
1735
1735
|
};
|
|
1736
1736
|
};
|
|
1737
1737
|
/** Result from collectStreamChunks. */
|
|
1738
|
+
/**
|
|
1739
|
+
* Which turn-level counter a per-chunk Vertex usage delta belongs to.
|
|
1740
|
+
*
|
|
1741
|
+
* Vertex updates its turn totals incrementally so they stay correct mid-stream
|
|
1742
|
+
* — a step killed by an abort, the turn deadline or the stall watchdog still
|
|
1743
|
+
* bills the tokens it already reported.
|
|
1744
|
+
*/
|
|
1745
|
+
export type VertexUsageCounter = "input" | "output" | "cacheRead" | "reasoning";
|
|
1738
1746
|
export type CollectedChunkResult = {
|
|
1739
1747
|
rawResponseParts: unknown[];
|
|
1740
1748
|
stepFunctionCalls: NativeFunctionCall[];
|