@juspay/neurolink 11.15.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.
|
@@ -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)
|
|
@@ -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[];
|