@juspay/neurolink 12.32.1 → 12.32.2
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 +3 -2
- package/README.md +3 -3
- package/dist/browser/neurolink.min.js +397 -397
- package/dist/core/baseProvider.js +80 -8
- package/dist/neurolink.js +74 -62
- package/dist/providers/googleVertex/client.js +37 -8
- package/docs-site/static/search-index.json +74 -42
- package/package.json +1 -1
|
@@ -450,6 +450,21 @@ export class BaseProvider {
|
|
|
450
450
|
const sourceIterator = source[Symbol.asyncIterator]();
|
|
451
451
|
let activeStream = source;
|
|
452
452
|
let activeIterator = sourceIterator;
|
|
453
|
+
// The StreamResult whose non-stream fields (model, provider, metadata,
|
|
454
|
+
// analytics, tool*) the caller should see. Starts as the first attempt
|
|
455
|
+
// and is reassigned to a fallback candidate's own result the moment that
|
|
456
|
+
// candidate actually commits real output (see the retry loop below) —
|
|
457
|
+
// never to a candidate that failed before producing anything. Read by
|
|
458
|
+
// the live getters built at the bottom of this function, so a caller
|
|
459
|
+
// that reads a field AFTER a fallback takes over sees the attempt that
|
|
460
|
+
// actually served the stream, not the abandoned one.
|
|
461
|
+
let activeResult = result;
|
|
462
|
+
// Some provider results expose getter-only properties. The forwarding
|
|
463
|
+
// result below must still be writable because NeuroLink's stream pipeline
|
|
464
|
+
// assigns final fields after constructing it. Keep assignment overrides
|
|
465
|
+
// scoped to the result that was active when the assignment happened, so a
|
|
466
|
+
// value written to a rejected attempt cannot shadow a later retry.
|
|
467
|
+
const assignedValues = new WeakMap();
|
|
453
468
|
const iterableOf = (iterator) => ({
|
|
454
469
|
[Symbol.asyncIterator]: () => iterator,
|
|
455
470
|
});
|
|
@@ -526,7 +541,13 @@ export class BaseProvider {
|
|
|
526
541
|
retryHeld.push(chunk);
|
|
527
542
|
continue;
|
|
528
543
|
}
|
|
529
|
-
retryCommitted
|
|
544
|
+
if (!retryCommitted) {
|
|
545
|
+
retryCommitted = true;
|
|
546
|
+
// This candidate is now the attempt serving the stream:
|
|
547
|
+
// every metadata/analytics/tool field the caller reads must
|
|
548
|
+
// resolve from here, never from the abandoned first attempt.
|
|
549
|
+
activeResult = retry;
|
|
550
|
+
}
|
|
530
551
|
while (retryHeld.length > 0) {
|
|
531
552
|
yield retryHeld.shift();
|
|
532
553
|
}
|
|
@@ -567,14 +588,65 @@ export class BaseProvider {
|
|
|
567
588
|
cancelStream(activeStream);
|
|
568
589
|
releaseIterator(activeIterator);
|
|
569
590
|
});
|
|
570
|
-
// A naked spread
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
|
|
575
|
-
|
|
591
|
+
// A naked spread would read (and thereby snapshot) every field on
|
|
592
|
+
// `result` immediately — before the generator above has even started —
|
|
593
|
+
// permanently freezing the first attempt's values. Define every declared
|
|
594
|
+
// StreamResult field instead of only the keys present on `result`: retry
|
|
595
|
+
// providers may expose optional fields the rejected attempt omitted.
|
|
596
|
+
const target = {
|
|
576
597
|
stream: wrapped,
|
|
577
|
-
}
|
|
598
|
+
};
|
|
599
|
+
const resultKeys = [
|
|
600
|
+
"knowledge",
|
|
601
|
+
"agentModeVersion",
|
|
602
|
+
"provider",
|
|
603
|
+
"model",
|
|
604
|
+
"usage",
|
|
605
|
+
"finishReason",
|
|
606
|
+
"stopReason",
|
|
607
|
+
"rawFinishReason",
|
|
608
|
+
"toolCalls",
|
|
609
|
+
"toolResults",
|
|
610
|
+
"toolEvents",
|
|
611
|
+
"toolExecutions",
|
|
612
|
+
"toolsUsed",
|
|
613
|
+
"metadata",
|
|
614
|
+
"analytics",
|
|
615
|
+
"evaluation",
|
|
616
|
+
"events",
|
|
617
|
+
"workflow",
|
|
618
|
+
"transcription",
|
|
619
|
+
"audio",
|
|
620
|
+
"ttsMetadata",
|
|
621
|
+
];
|
|
622
|
+
for (const key of resultKeys) {
|
|
623
|
+
Object.defineProperty(target, key, {
|
|
624
|
+
enumerable: true,
|
|
625
|
+
configurable: true,
|
|
626
|
+
get: () => {
|
|
627
|
+
const assigned = assignedValues.get(activeResult);
|
|
628
|
+
return assigned?.has(key) ? assigned.get(key) : activeResult[key];
|
|
629
|
+
},
|
|
630
|
+
set: (value) => {
|
|
631
|
+
const descriptor = Object.getOwnPropertyDescriptor(activeResult, key);
|
|
632
|
+
if (descriptor?.set) {
|
|
633
|
+
descriptor.set.call(activeResult, value);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (!descriptor || "value" in descriptor) {
|
|
637
|
+
Reflect.set(activeResult, key, value);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
let assigned = assignedValues.get(activeResult);
|
|
641
|
+
if (!assigned) {
|
|
642
|
+
assigned = new Map();
|
|
643
|
+
assignedValues.set(activeResult, assigned);
|
|
644
|
+
}
|
|
645
|
+
assigned.set(key, value);
|
|
646
|
+
},
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
return target;
|
|
578
650
|
}
|
|
579
651
|
/**
|
|
580
652
|
* The eager half of the retired-model stream fallback, for providers whose
|
package/dist/neurolink.js
CHANGED
|
@@ -83,7 +83,7 @@ import { hasLifecycleErrorFired, markLifecycleErrorFired, } from "./utils/lifecy
|
|
|
83
83
|
import { interleaveTTSStream } from "./utils/ttsStream.js";
|
|
84
84
|
import { resolveLifecycleTimeoutMs } from "./utils/lifecycleTimeout.js";
|
|
85
85
|
import { cloneOptionsForCallIsolation } from "./utils/cloneOptions.js";
|
|
86
|
-
import {
|
|
86
|
+
import { preserveLiveStreamAccessors } from "./utils/streamResultAccessors.js";
|
|
87
87
|
import { coerceJsonToSchema, recoverScalarRoot, schemaAccepts, } from "./utils/json/coerce.js";
|
|
88
88
|
// Factory processing imports
|
|
89
89
|
import { createCleanStreamOptions, enhanceTextGenerationOptions, processFactoryOptions, processStreamingFactoryOptions, validateFactoryConfig, } from "./utils/factoryProcessing.js";
|
|
@@ -7828,7 +7828,8 @@ Current user's request: ${currentInput}`;
|
|
|
7828
7828
|
// bottom of this method, without ever reading — and thereby freezing —
|
|
7829
7829
|
// a getter-based provider's value here.
|
|
7830
7830
|
const mcpStreamOutcome = await this.createMCPStream(enhancedOptions);
|
|
7831
|
-
const { stream: mcpStream
|
|
7831
|
+
const { stream: mcpStream } = mcpStreamOutcome;
|
|
7832
|
+
const providerName = mcpStreamOutcome.provider ?? "unknown";
|
|
7832
7833
|
let streamedTTSResult;
|
|
7833
7834
|
const { stream: incrementalStream, ttsMetadata: streamTtsMetadata } = await this.createIncrementalTTSStream({
|
|
7834
7835
|
stream: mcpStream,
|
|
@@ -7841,9 +7842,9 @@ Current user's request: ${currentInput}`;
|
|
|
7841
7842
|
});
|
|
7842
7843
|
ttsMetadataSink?.(streamTtsMetadata);
|
|
7843
7844
|
const streamState = {
|
|
7844
|
-
finishReason:
|
|
7845
|
-
toolCalls:
|
|
7846
|
-
toolResults:
|
|
7845
|
+
finishReason: mcpStreamOutcome.finishReason ?? "stop",
|
|
7846
|
+
toolCalls: mcpStreamOutcome.toolCalls ?? [],
|
|
7847
|
+
toolResults: mcpStreamOutcome.toolResults ?? [],
|
|
7847
7848
|
};
|
|
7848
7849
|
streamSpan.setAttribute(ATTR.NL_PROVIDER, providerName || "unknown");
|
|
7849
7850
|
let accumulatedContent = "";
|
|
@@ -7926,7 +7927,7 @@ Current user's request: ${currentInput}`;
|
|
|
7926
7927
|
// not a provider failure, and retrying it on another provider
|
|
7927
7928
|
// spends that provider's tokens to exceed a budget the caller set.
|
|
7928
7929
|
const cappedByCallerBudget = enhancedOptions.fallbackOnMaxSteps === false &&
|
|
7929
|
-
|
|
7930
|
+
mcpStreamOutcome.metadata?.stopReason === "step-cap";
|
|
7930
7931
|
if (realOutputChunks === 0 &&
|
|
7931
7932
|
!cappedByCallerBudget &&
|
|
7932
7933
|
!metadata.fallbackAttempted &&
|
|
@@ -7949,10 +7950,10 @@ Current user's request: ${currentInput}`;
|
|
|
7949
7950
|
yield* incrementalFallback;
|
|
7950
7951
|
}
|
|
7951
7952
|
ttsResolver?.(streamedTTSResult);
|
|
7952
|
-
resolvedUsage =
|
|
7953
|
-
if (!resolvedUsage &&
|
|
7953
|
+
resolvedUsage = mcpStreamOutcome.usage;
|
|
7954
|
+
if (!resolvedUsage && mcpStreamOutcome.analytics) {
|
|
7954
7955
|
try {
|
|
7955
|
-
const resolved = await Promise.resolve(
|
|
7956
|
+
const resolved = await Promise.resolve(mcpStreamOutcome.analytics);
|
|
7956
7957
|
if (resolved?.tokenUsage) {
|
|
7957
7958
|
resolvedUsage = resolved.tokenUsage;
|
|
7958
7959
|
}
|
|
@@ -7961,11 +7962,16 @@ Current user's request: ${currentInput}`;
|
|
|
7961
7962
|
// non-blocking
|
|
7962
7963
|
}
|
|
7963
7964
|
}
|
|
7965
|
+
if (resolvedUsage) {
|
|
7966
|
+
mcpStreamOutcome.usage = resolvedUsage;
|
|
7967
|
+
}
|
|
7964
7968
|
self.emitter.emit("stream:complete", {
|
|
7965
7969
|
type: "stream:complete",
|
|
7966
7970
|
content: accumulatedContent,
|
|
7967
7971
|
provider: metadata.fallbackProvider ?? providerName,
|
|
7968
|
-
model: metadata.fallbackModel ??
|
|
7972
|
+
model: metadata.fallbackModel ??
|
|
7973
|
+
mcpStreamOutcome.model ??
|
|
7974
|
+
enhancedOptions.model,
|
|
7969
7975
|
finishReason: streamState.finishReason ?? "stop",
|
|
7970
7976
|
prompt: enhancedOptions.input?.text ||
|
|
7971
7977
|
enhancedOptions.prompt,
|
|
@@ -8047,7 +8053,7 @@ Current user's request: ${currentInput}`;
|
|
|
8047
8053
|
try {
|
|
8048
8054
|
const finalProvider = metadata.fallbackProvider ?? providerName ?? "unknown";
|
|
8049
8055
|
const finalModel = metadata.fallbackModel ??
|
|
8050
|
-
|
|
8056
|
+
mcpStreamOutcome.model ??
|
|
8051
8057
|
enhancedOptions.model ??
|
|
8052
8058
|
"unknown";
|
|
8053
8059
|
const finalFinishReason = streamError
|
|
@@ -8139,7 +8145,7 @@ Current user's request: ${currentInput}`;
|
|
|
8139
8145
|
streamResult.toolCalls = streamState.toolCalls;
|
|
8140
8146
|
streamResult.toolResults = streamState.toolResults;
|
|
8141
8147
|
if (!streamResult.usage) {
|
|
8142
|
-
streamResult.usage =
|
|
8148
|
+
streamResult.usage = mcpStreamOutcome.usage;
|
|
8143
8149
|
}
|
|
8144
8150
|
if (!streamResult.analytics) {
|
|
8145
8151
|
// CRITICAL: do NOT `await` a Promise-typed analytics here. Bedrock
|
|
@@ -8155,8 +8161,7 @@ Current user's request: ${currentInput}`;
|
|
|
8155
8161
|
// Promise<AnalyticsData>` — the cast widens the local
|
|
8156
8162
|
// `processStreamResult` return type which only declares the
|
|
8157
8163
|
// resolved shape.
|
|
8158
|
-
streamResult.analytics =
|
|
8159
|
-
streamAnalytics;
|
|
8164
|
+
streamResult.analytics = mcpStreamOutcome.analytics;
|
|
8160
8165
|
}
|
|
8161
8166
|
// The cost listener wants a resolved cost, but if the provider gave
|
|
8162
8167
|
// us a Promise (Bedrock) we can't tax the stream's return path on it.
|
|
@@ -8183,8 +8188,7 @@ Current user's request: ${currentInput}`;
|
|
|
8183
8188
|
guardrailsBlocked: metadata.guardrailsBlocked,
|
|
8184
8189
|
error: metadata.error,
|
|
8185
8190
|
events: eventSequence,
|
|
8186
|
-
|
|
8187
|
-
toolTelemetrySource: mcpStreamOutcome,
|
|
8191
|
+
sourceResult: mcpStreamOutcome,
|
|
8188
8192
|
});
|
|
8189
8193
|
}
|
|
8190
8194
|
catch (error) {
|
|
@@ -8986,24 +8990,14 @@ Current user's request: ${currentInput}`;
|
|
|
8986
8990
|
throw streamConsumeErr;
|
|
8987
8991
|
}
|
|
8988
8992
|
})();
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
// keeps resolving lazily through this boundary instead of being
|
|
8992
|
-
// read — and frozen — once, right here, before any chunk is drained.
|
|
8993
|
-
return Object.defineProperties({
|
|
8993
|
+
return preserveLiveStreamAccessors(poolStreamResult, {
|
|
8994
|
+
...poolStreamResult,
|
|
8994
8995
|
stream: wrappedStream,
|
|
8995
8996
|
provider: poolStreamProviderName,
|
|
8996
|
-
usage: poolStreamResult.usage,
|
|
8997
8997
|
model: poolStreamResult.model || poolStreamModel,
|
|
8998
|
-
finishReason: poolStreamResult.finishReason,
|
|
8999
8998
|
toolCalls: poolStreamResult.toolCalls ?? [],
|
|
9000
8999
|
toolResults: poolStreamResult.toolResults ?? [],
|
|
9001
|
-
|
|
9002
|
-
metadata: poolStreamResult.metadata,
|
|
9003
|
-
}, pickLiveStreamDescriptors(poolStreamResult, [
|
|
9004
|
-
"toolsUsed",
|
|
9005
|
-
"toolExecutions",
|
|
9006
|
-
]));
|
|
9000
|
+
});
|
|
9007
9001
|
}
|
|
9008
9002
|
catch (poolStreamError) {
|
|
9009
9003
|
if (isAbortError(poolStreamError)) {
|
|
@@ -9048,26 +9042,17 @@ Current user's request: ${currentInput}`;
|
|
|
9048
9042
|
provider: providerName,
|
|
9049
9043
|
systemPromptPassedLength: enhancedSystemPrompt.length,
|
|
9050
9044
|
});
|
|
9051
|
-
//
|
|
9052
|
-
//
|
|
9053
|
-
//
|
|
9054
|
-
//
|
|
9055
|
-
return
|
|
9056
|
-
|
|
9045
|
+
// Preserve every live descriptor from the provider result. Internal model
|
|
9046
|
+
// fallback can switch the descriptor's backing result only after iteration
|
|
9047
|
+
// starts, so eagerly rebuilding selected fields here would re-freeze the
|
|
9048
|
+
// rejected attempt before the caller drains a chunk.
|
|
9049
|
+
return preserveLiveStreamAccessors(streamResult, {
|
|
9050
|
+
...streamResult,
|
|
9057
9051
|
provider: providerName,
|
|
9058
|
-
usage: streamResult.usage,
|
|
9059
9052
|
model: streamResult.model || options.model,
|
|
9060
|
-
finishReason: streamResult.finishReason,
|
|
9061
9053
|
toolCalls: streamResult.toolCalls ?? [],
|
|
9062
9054
|
toolResults: streamResult.toolResults ?? [],
|
|
9063
|
-
|
|
9064
|
-
// Pass the provider's metadata object THROUGH BY REFERENCE: native
|
|
9065
|
-
// background-loop streams (Vertex Gemini/Claude) resolve
|
|
9066
|
-
// finishReason/stopReason/rawFinishReason/stepsUsed onto it only when
|
|
9067
|
-
// the loop finishes — snapshotting fields here would freeze them as
|
|
9068
|
-
// undefined before the stream is drained.
|
|
9069
|
-
metadata: streamResult.metadata,
|
|
9070
|
-
}, pickLiveStreamDescriptors(streamResult, ["toolsUsed", "toolExecutions"]));
|
|
9055
|
+
});
|
|
9071
9056
|
}
|
|
9072
9057
|
/**
|
|
9073
9058
|
* Process stream result
|
|
@@ -9098,10 +9083,20 @@ Current user's request: ${currentInput}`;
|
|
|
9098
9083
|
* Create stream response
|
|
9099
9084
|
*/
|
|
9100
9085
|
createStreamResponse(streamResult, stream, config) {
|
|
9101
|
-
|
|
9086
|
+
const source = config.sourceResult;
|
|
9087
|
+
const responseMetadata = {
|
|
9088
|
+
streamId: config.streamId,
|
|
9089
|
+
startTime: config.startTime,
|
|
9090
|
+
responseTime: config.responseTime,
|
|
9091
|
+
fallback: config.fallback || false,
|
|
9092
|
+
guardrailsBlocked: config.guardrailsBlocked,
|
|
9093
|
+
error: config.error,
|
|
9094
|
+
};
|
|
9095
|
+
const response = {
|
|
9096
|
+
...(source ?? {}),
|
|
9102
9097
|
stream,
|
|
9103
|
-
provider: config.providerName,
|
|
9104
|
-
model: config.options.model,
|
|
9098
|
+
provider: source?.provider ?? config.providerName,
|
|
9099
|
+
model: source?.model ?? config.options.model,
|
|
9105
9100
|
usage: streamResult.usage,
|
|
9106
9101
|
finishReason: streamResult.finishReason,
|
|
9107
9102
|
toolCalls: streamResult.toolCalls,
|
|
@@ -9109,20 +9104,37 @@ Current user's request: ${currentInput}`;
|
|
|
9109
9104
|
analytics: streamResult.analytics,
|
|
9110
9105
|
evaluation: streamResult.evaluation,
|
|
9111
9106
|
events: config.events && config.events.length > 0 ? config.events : undefined,
|
|
9112
|
-
metadata: Object.assign(
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9107
|
+
metadata: source ? undefined : Object.assign({}, responseMetadata),
|
|
9108
|
+
};
|
|
9109
|
+
if (!source) {
|
|
9110
|
+
return response;
|
|
9111
|
+
}
|
|
9112
|
+
// NeuroLink grades the turn's terminal state itself (a normalized
|
|
9113
|
+
// finishReason, stopReason for aborts and time limits), so those fields
|
|
9114
|
+
// stay plain values here. Copying the provider's getter for them would
|
|
9115
|
+
// report the raw vendor reason, e.g. Anthropic's end_turn, instead.
|
|
9116
|
+
const gradedTerminalFields = new Set([
|
|
9117
|
+
"finishReason",
|
|
9118
|
+
"stopReason",
|
|
9119
|
+
"rawFinishReason",
|
|
9120
|
+
]);
|
|
9121
|
+
for (const key of Object.getOwnPropertyNames(source)) {
|
|
9122
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
9123
|
+
if (!gradedTerminalFields.has(key) &&
|
|
9124
|
+
descriptor &&
|
|
9125
|
+
typeof descriptor.get === "function") {
|
|
9126
|
+
Object.defineProperty(response, key, descriptor);
|
|
9127
|
+
}
|
|
9128
|
+
}
|
|
9129
|
+
Object.defineProperty(response, "metadata", {
|
|
9130
|
+
enumerable: true,
|
|
9131
|
+
configurable: true,
|
|
9132
|
+
get: () => Object.assign(source.metadata ?? {}, responseMetadata),
|
|
9133
|
+
set: (value) => {
|
|
9134
|
+
source.metadata = value;
|
|
9135
|
+
},
|
|
9136
|
+
});
|
|
9137
|
+
return response;
|
|
9126
9138
|
}
|
|
9127
9139
|
/**
|
|
9128
9140
|
* Handle stream error with fallback
|
|
@@ -905,6 +905,12 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
905
905
|
...processedOptions,
|
|
906
906
|
tools: optionTools,
|
|
907
907
|
};
|
|
908
|
+
// Captured up-front (mirrors generate()'s inputPrompt) so the
|
|
909
|
+
// failure branch below can label a `generation:end` it emits before
|
|
910
|
+
// any content exists.
|
|
911
|
+
const inputPrompt = mergedOptions.input?.text ||
|
|
912
|
+
mergedOptions.prompt ||
|
|
913
|
+
"";
|
|
908
914
|
try {
|
|
909
915
|
// Route Claude models to native Anthropic SDK
|
|
910
916
|
let result;
|
|
@@ -937,6 +943,17 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
937
943
|
catch (error) {
|
|
938
944
|
this.fireGenerateOnError(options, error, streamStartTime);
|
|
939
945
|
this.emitStreamEnd(modelName, streamStartTime, false, error);
|
|
946
|
+
// Unlike Google AI Studio's native stream, Vertex's turn loop is
|
|
947
|
+
// awaited to completion INSIDE executeNativeGemini3Stream /
|
|
948
|
+
// executeNativeAnthropicStream before this method ever returns a
|
|
949
|
+
// StreamResult — a first-call failure (e.g. a 500 on the initial
|
|
950
|
+
// request) rejects here, before the generic per-stream dedup
|
|
951
|
+
// machinery in NeuroLink.stream() (which only wraps an already
|
|
952
|
+
// -returned stream) ever runs. Without this call a failed Vertex
|
|
953
|
+
// stream reported zero `generation:end` events instead of one:
|
|
954
|
+
// `emitStreamEnd` above only ever emits `stream:end`, a different
|
|
955
|
+
// event Pipeline B and analytics don't listen for.
|
|
956
|
+
this.emitGenerationEnd(modelName, null, streamStartTime, false, error, inputPrompt);
|
|
940
957
|
throw error;
|
|
941
958
|
}
|
|
942
959
|
}, (r) => r.stream,
|
|
@@ -5682,6 +5699,26 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5682
5699
|
const usage = result?.usage && typeof result.usage === "object"
|
|
5683
5700
|
? result.usage
|
|
5684
5701
|
: { input: 0, output: 0, total: 0 };
|
|
5702
|
+
// Mark on the result so the SDK-level runStandardGenerateRequest knows
|
|
5703
|
+
// this provider already emitted `generation:end` itself and skips its
|
|
5704
|
+
// own duplicate emission. Without this flag the public event listener
|
|
5705
|
+
// (and the observability test) would see two events per generate call.
|
|
5706
|
+
if (result && typeof result === "object") {
|
|
5707
|
+
result._generationEndEmitted =
|
|
5708
|
+
true;
|
|
5709
|
+
}
|
|
5710
|
+
// The failure path has no result to carry that flag — `result` is null —
|
|
5711
|
+
// so mark the error instead. Without this, `generateTextInternal`'s
|
|
5712
|
+
// wrapProviderError (which replaces this error with a new one further up
|
|
5713
|
+
// the stack) has nothing to copy forward, and emitGenerateErrorEvent goes
|
|
5714
|
+
// on to emit its own `generation:end` for a failure this provider already
|
|
5715
|
+
// reported — double-counting every failed native Vertex generate in
|
|
5716
|
+
// analytics and as two Pipeline B spans. Same shape as the AI Studio
|
|
5717
|
+
// (#1745) and Bedrock (#1762) fixes for the same gap.
|
|
5718
|
+
if (!success && error && typeof error === "object") {
|
|
5719
|
+
error._generationEndEmitted =
|
|
5720
|
+
true;
|
|
5721
|
+
}
|
|
5685
5722
|
emitter.emit("generation:end", {
|
|
5686
5723
|
provider: this.providerName,
|
|
5687
5724
|
responseTime: Date.now() - startTime,
|
|
@@ -5702,14 +5739,6 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5702
5739
|
? { error: error instanceof Error ? error.message : String(error) }
|
|
5703
5740
|
: {}),
|
|
5704
5741
|
});
|
|
5705
|
-
// Mark on the result so the SDK-level runStandardGenerateRequest knows
|
|
5706
|
-
// this provider already emitted `generation:end` itself and skips its
|
|
5707
|
-
// own duplicate emission. Without this flag the public event listener
|
|
5708
|
-
// (and the observability test) would see two events per generate call.
|
|
5709
|
-
if (result && typeof result === "object") {
|
|
5710
|
-
result._generationEndEmitted =
|
|
5711
|
-
true;
|
|
5712
|
-
}
|
|
5713
5742
|
}
|
|
5714
5743
|
/**
|
|
5715
5744
|
* Turn-clock-shaped adapter over the shared {@link buildLoopExitMessage}.
|