@juspay/neurolink 12.47.13 → 12.48.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.
- package/CHANGELOG.md +3 -3
- package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
- package/dist/browser/neurolink.min.js +578 -565
- package/dist/cli/factories/commandFactory.d.ts +7 -0
- package/dist/cli/factories/commandFactory.js +26 -0
- package/dist/cli/loop/optionsSchema.js +9 -0
- package/dist/context/budgetChecker.js +54 -2
- package/dist/context/contextDecision.js +2 -0
- package/dist/context/toolPairRepair.d.ts +12 -0
- package/dist/context/toolPairRepair.js +29 -3
- package/dist/core/baseProvider.d.ts +92 -3
- package/dist/core/baseProvider.js +254 -10
- package/dist/core/conversationMemoryManager.js +17 -2
- package/dist/core/loopEngine.js +1 -1
- package/dist/core/modules/MessageBuilder.js +11 -3
- package/dist/core/modules/TelemetryHandler.d.ts +2 -2
- package/dist/core/modules/ToolsManager.d.ts +35 -0
- package/dist/core/modules/ToolsManager.js +159 -55
- package/dist/core/nativeGenerateLoop.d.ts +58 -1
- package/dist/core/nativeGenerateLoop.js +277 -3
- package/dist/core/redisConversationMemoryManager.js +10 -3
- package/dist/core/resolveRequestKind.d.ts +24 -4
- package/dist/core/resolveRequestKind.js +31 -4
- package/dist/core/toolExecutionRecorder.d.ts +6 -0
- package/dist/core/toolExecutionRecorder.js +24 -2
- package/dist/core/toolRoutingDecision.js +1 -0
- package/dist/factories/mediaHandlerCatalog.js +5 -0
- package/dist/factories/providerDescriptors.js +15 -0
- package/dist/files/fileTools.d.ts +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +9 -2
- package/dist/memory/condensationPrompt.d.ts +8 -0
- package/dist/memory/condensationPrompt.js +29 -0
- package/dist/memory/hippocampusInitializer.d.ts +14 -2
- package/dist/memory/hippocampusInitializer.js +64 -3
- package/dist/neurolink.d.ts +252 -3
- package/dist/neurolink.js +898 -43
- package/dist/providers/amazonBedrock/client.js +6 -0
- package/dist/providers/amazonSagemaker.js +14 -1
- package/dist/providers/anthropic/client.js +148 -19
- package/dist/providers/anthropic/loopAdapter.js +2 -2
- package/dist/providers/googleAiStudio/client.js +29 -14
- package/dist/providers/openaiChatCompletionsBase.js +103 -23
- package/dist/providers/systemOneDecision.d.ts +6 -0
- package/dist/providers/systemOneDecision.js +14 -63
- package/dist/providers/typesafe.d.ts +6 -0
- package/dist/providers/typesafe.js +6 -0
- package/dist/rag/retrieval/searchDecision.js +1 -0
- package/dist/routing/classifierRouter.js +6 -5
- package/dist/routing/classifierStrategies.js +8 -1
- package/dist/types/classifierRouter.d.ts +16 -3
- package/dist/types/common.d.ts +2 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/context.d.ts +8 -1
- package/dist/types/conversation.d.ts +87 -0
- package/dist/types/decision.d.ts +210 -1
- package/dist/types/decision.js +9 -1
- package/dist/types/dispatch.d.ts +11 -4
- package/dist/types/generate.d.ts +118 -54
- package/dist/types/loopEngine.d.ts +7 -2
- package/dist/types/memory.d.ts +177 -9
- package/dist/types/openaiCompatible.d.ts +11 -3
- package/dist/types/stream.d.ts +13 -16
- package/dist/types/stt.d.ts +58 -1
- package/dist/types/tools.d.ts +14 -0
- package/dist/types/tools.js +8 -0
- package/dist/types/tts.d.ts +108 -0
- package/dist/types/voice.d.ts +41 -4
- package/dist/utils/decisionAnswers.d.ts +31 -1
- package/dist/utils/decisionAnswers.js +114 -0
- package/dist/utils/decisionLimits.d.ts +39 -0
- package/dist/utils/decisionLimits.js +116 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +9 -0
- package/dist/utils/messageBuilder.d.ts +45 -1
- package/dist/utils/messageBuilder.js +393 -16
- package/dist/utils/multimodalOptionsBuilder.d.ts +1 -0
- package/dist/utils/multimodalOptionsBuilder.js +1 -0
- package/dist/utils/speechText.d.ts +41 -0
- package/dist/utils/speechText.js +364 -0
- package/dist/utils/toolResultStatus.d.ts +26 -0
- package/dist/utils/toolResultStatus.js +73 -0
- package/dist/utils/ttsProcessor.d.ts +29 -0
- package/dist/utils/ttsProcessor.js +84 -2
- package/dist/voice/index.d.ts +1 -0
- package/dist/voice/index.js +3 -0
- package/dist/voice/providers/ElevenLabsSTT.d.ts +42 -0
- package/dist/voice/providers/ElevenLabsSTT.js +351 -0
- package/dist/voice/providers/ElevenLabsTTS.d.ts +81 -1
- package/dist/voice/providers/ElevenLabsTTS.js +415 -74
- package/docs-site/static/search-index.json +48 -33
- package/package.json +5 -2
- package/dist/utils/toolEndEmitter.d.ts +0 -25
- package/dist/utils/toolEndEmitter.js +0 -64
|
@@ -210,6 +210,10 @@ export class AmazonBedrockProvider extends BaseProvider {
|
|
|
210
210
|
});
|
|
211
211
|
// Cast options to StreamOptions for multimodal processing
|
|
212
212
|
const streamOptions = options;
|
|
213
|
+
// This provider bypasses BaseProvider's message builders, so the
|
|
214
|
+
// instance-level `conversationMemory.replayToolSteps` has to be folded
|
|
215
|
+
// in here or only the per-request value would ever apply.
|
|
216
|
+
this.applyToolReplayDefault(streamOptions);
|
|
213
217
|
const multimodalOptions = buildMultimodalOptions(streamOptions, this.providerName, this.modelName);
|
|
214
218
|
const multimodalMessages = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
215
219
|
// Convert to Bedrock format
|
|
@@ -923,6 +927,8 @@ export class AmazonBedrockProvider extends BaseProvider {
|
|
|
923
927
|
hasPDFFiles: !!options.input?.pdfFiles?.length,
|
|
924
928
|
pdfFileCount: options.input?.pdfFiles?.length || 0,
|
|
925
929
|
});
|
|
930
|
+
// Same instance-default fold as the generate() path above.
|
|
931
|
+
this.applyToolReplayDefault(options);
|
|
926
932
|
const multimodalOptions = buildMultimodalOptions(options, this.providerName, this.modelName);
|
|
927
933
|
const multimodalMessages = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
928
934
|
// Convert to Bedrock format
|
|
@@ -6,6 +6,7 @@ import { resolveToolExecutionRecords, toolCallsFromSummaries, } from "../core/to
|
|
|
6
6
|
import { transformToolExecutions } from "../utils/transformationUtils.js";
|
|
7
7
|
import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
|
|
8
8
|
import { withProviderRetry } from "../utils/providerRetry.js";
|
|
9
|
+
import { resolveToolChoice } from "../utils/toolChoice.js";
|
|
9
10
|
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
10
11
|
import { hasNativeDoGenerate, runNativeGenerateLoop, } from "../core/nativeGenerateLoop.js";
|
|
11
12
|
import { withSpan } from "../telemetry/withSpan.js";
|
|
@@ -233,6 +234,17 @@ export class AmazonSageMakerProvider extends BaseProvider {
|
|
|
233
234
|
...(responseFormat ? { responseFormat } : {}),
|
|
234
235
|
...(v3Tools.length > 0 ? { tools: v3Tools } : {}),
|
|
235
236
|
toolsRecord,
|
|
237
|
+
// Was never forwarded here, so a caller's toolChoice degraded to the
|
|
238
|
+
// endpoint's default on SageMaker alone; the language model already
|
|
239
|
+
// maps it onto `tool_choice`.
|
|
240
|
+
...(v3Tools.length > 0 && options.toolChoice
|
|
241
|
+
? { toolChoice: resolveToolChoice(options, toolsRecord, true) }
|
|
242
|
+
: {}),
|
|
243
|
+
...(options.toolChoiceSteps !== undefined
|
|
244
|
+
? { toolChoiceSteps: options.toolChoiceSteps }
|
|
245
|
+
: {}),
|
|
246
|
+
...(options.prepareStep ? { prepareStep: options.prepareStep } : {}),
|
|
247
|
+
modelId: this.modelName,
|
|
236
248
|
maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
|
|
237
249
|
...(options.maxTokens ? { maxOutputTokens: options.maxTokens } : {}),
|
|
238
250
|
...(options.temperature !== undefined
|
|
@@ -245,6 +257,7 @@ export class AmazonSageMakerProvider extends BaseProvider {
|
|
|
245
257
|
runStep: (call) => withProviderRetry(call, undefined, "sagemaker generate", undefined, options.abortSignal).catch((err) => {
|
|
246
258
|
throw this.handleProviderError(err);
|
|
247
259
|
}),
|
|
260
|
+
onRejectedToolCall: (name, error, id) => this.emitRejectedToolCall(name, error, id),
|
|
248
261
|
}, toolExecutionSummaries);
|
|
249
262
|
const enhanced = {
|
|
250
263
|
content: loop.text,
|
|
@@ -263,7 +276,7 @@ export class AmazonSageMakerProvider extends BaseProvider {
|
|
|
263
276
|
toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
|
|
264
277
|
enhancedWithTools: loop.toolsUsed.length > 0,
|
|
265
278
|
};
|
|
266
|
-
return this.finalizeNativeGenerate(enhanced, options, startTime);
|
|
279
|
+
return this.finalizeNativeGenerate(enhanced, options, startTime, toolExecutionSummaries);
|
|
267
280
|
}
|
|
268
281
|
/**
|
|
269
282
|
* Streaming was previously an `executeStream` override that unconditionally
|
|
@@ -26,7 +26,7 @@ import { stringifyAnthropicToolOutput } from "./toolOutput.js";
|
|
|
26
26
|
import { createAnthropicLoopAdapter } from "./loopAdapter.js";
|
|
27
27
|
import { DEFAULT_BEFORE_STEP_TIMEOUT_MS } from "../../utils/parameterValidation.js";
|
|
28
28
|
import { runAgenticLoop } from "../../core/loopEngine.js";
|
|
29
|
-
import { hasNativeDoGenerate, runNativeGenerateLoop, } from "../../core/nativeGenerateLoop.js";
|
|
29
|
+
import { hasNativeDoGenerate, resolveStepToolChoice, runNativeGenerateLoop, toPrepareStepRecord, } from "../../core/nativeGenerateLoop.js";
|
|
30
30
|
import { withProviderRetry } from "../../utils/providerRetry.js";
|
|
31
31
|
import { resolveRequestKind } from "../../core/resolveRequestKind.js";
|
|
32
32
|
import { resolveToolExecutionRecords, toolCallsFromSummaries, } from "../../core/toolExecutionRecorder.js";
|
|
@@ -35,7 +35,6 @@ import { createAnthropicConfig, getProviderModel, validateApiKey, } from "../../
|
|
|
35
35
|
import { composeAbortSignals, composeAbortSignalsScoped, createTimeoutController, mergeAbortSignals, TimeoutError, } from "../../utils/timeout.js";
|
|
36
36
|
import { raceWithAbort } from "../../utils/async/index.js";
|
|
37
37
|
import { normalizeResolvedToolChoice, resolveToolChoice, } from "../../utils/toolChoice.js";
|
|
38
|
-
import { emitToolEndFromStepFinish } from "../../utils/toolEndEmitter.js";
|
|
39
38
|
import { NoOutputGeneratedError } from "../../utils/generationErrors.js";
|
|
40
39
|
import { buildNoOutputSentinel, stampNoOutputSpan, } from "../../utils/noOutputSentinel.js";
|
|
41
40
|
import { convertZodToJsonSchema } from "../../utils/schemaConversion.js";
|
|
@@ -1613,6 +1612,11 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1613
1612
|
...(hasTools && options.toolChoice
|
|
1614
1613
|
? { toolChoice: resolveToolChoice(options, toolsRecord, true) }
|
|
1615
1614
|
: {}),
|
|
1615
|
+
...(options.toolChoiceSteps !== undefined
|
|
1616
|
+
? { toolChoiceSteps: options.toolChoiceSteps }
|
|
1617
|
+
: {}),
|
|
1618
|
+
...(options.prepareStep ? { prepareStep: options.prepareStep } : {}),
|
|
1619
|
+
modelId,
|
|
1616
1620
|
...(responseFormat ? { responseFormat } : {}),
|
|
1617
1621
|
...(providerOptions ? { providerOptions } : {}),
|
|
1618
1622
|
maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
|
|
@@ -1627,6 +1631,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1627
1631
|
runStep: (call) => withProviderRetry(call, trace.getActiveSpan() ?? undefined, "anthropic generate", undefined, options.abortSignal).catch((err) => {
|
|
1628
1632
|
throw this.handleProviderError(err);
|
|
1629
1633
|
}),
|
|
1634
|
+
onRejectedToolCall: (name, error, id) => this.emitRejectedToolCall(name, error, id),
|
|
1630
1635
|
}, toolExecutionSummaries);
|
|
1631
1636
|
const enhanced = {
|
|
1632
1637
|
content: loop.text,
|
|
@@ -1660,7 +1665,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1660
1665
|
toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutionSummaries)),
|
|
1661
1666
|
enhancedWithTools: loop.toolsUsed.length > 0,
|
|
1662
1667
|
};
|
|
1663
|
-
return this.finalizeNativeGenerate(enhanced, options, startTime);
|
|
1668
|
+
return this.finalizeNativeGenerate(enhanced, options, startTime, toolExecutionSummaries);
|
|
1664
1669
|
}
|
|
1665
1670
|
/**
|
|
1666
1671
|
* Fold a captured snapshot into the provider's usage bookkeeping and log it.
|
|
@@ -1748,6 +1753,11 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1748
1753
|
// options.tools is pre-merged by BaseProvider.stream() with base tools
|
|
1749
1754
|
// (MCP/built-in) + user-provided tools (RAG, etc.)
|
|
1750
1755
|
shouldUseTools = !options.disableTools && this.supportsTools();
|
|
1756
|
+
// Used as is: BaseProvider.stream() already event-wrapped, recorder-
|
|
1757
|
+
// wrapped and discovery-partitioned this record, and search_tools
|
|
1758
|
+
// hydrates new tools into THIS object mid-turn. A copy would lose the
|
|
1759
|
+
// null prototype, the non-enumerable resolver and every later
|
|
1760
|
+
// hydration, so a discovered tool the model then called was "not found".
|
|
1751
1761
|
toolsRecord = shouldUseTools
|
|
1752
1762
|
? options.tools || (await this.getAllTools())
|
|
1753
1763
|
: {};
|
|
@@ -1776,9 +1786,17 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1776
1786
|
throw this.handleProviderError(setupErr);
|
|
1777
1787
|
}
|
|
1778
1788
|
const modelId = this.modelName || getDefaultAnthropicModel();
|
|
1779
|
-
|
|
1780
|
-
|
|
1789
|
+
// The turn's choice in NeuroLink shape. Translated to the wire PER STEP
|
|
1790
|
+
// inside buildParams, because a forced choice applies only to the first
|
|
1791
|
+
// `toolChoiceSteps` steps — baked in once, it compelled a tool call on
|
|
1792
|
+
// every step and the turn ran until maxSteps.
|
|
1793
|
+
const turnToolChoice = shouldUseTools && anthropicTools && anthropicTools.length > 0
|
|
1794
|
+
? resolveToolChoice(options, toolsRecord, shouldUseTools)
|
|
1781
1795
|
: undefined;
|
|
1796
|
+
// Completed-step records handed to a caller's prepareStep hook. Filled by
|
|
1797
|
+
// the buildToolResultMessages wrapper below, which is the only per-step
|
|
1798
|
+
// hook that sees a step's calls and results together.
|
|
1799
|
+
const stepRecords = [];
|
|
1782
1800
|
// Extended thinking: enabled when the caller supplies an explicit token
|
|
1783
1801
|
// budget (mirrors the previous experimental_thinking gating). Thinking
|
|
1784
1802
|
// deltas stream out on the `reasoning` chunk channel.
|
|
@@ -1803,7 +1821,6 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1803
1821
|
},
|
|
1804
1822
|
});
|
|
1805
1823
|
const maxSteps = options.maxSteps || DEFAULT_MAX_STEPS;
|
|
1806
|
-
const emitter = this.neurolink?.getEventEmitter();
|
|
1807
1824
|
const channel = createStreamChannel();
|
|
1808
1825
|
const { push: pushChunk } = channel;
|
|
1809
1826
|
const { usagePromise, finishPromise, resolveUsage, resolveFinish } = createDeferredAnalytics();
|
|
@@ -1842,7 +1859,15 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1842
1859
|
});
|
|
1843
1860
|
let capturedProviderError;
|
|
1844
1861
|
const client = this.client;
|
|
1862
|
+
// Every attempted call, for the no-output sentinel below; the result
|
|
1863
|
+
// reports only calls that returned a value, the rule the generate loop
|
|
1864
|
+
// and Bedrock already follow.
|
|
1845
1865
|
const toolsUsed = [];
|
|
1866
|
+
const toolsSucceeded = [];
|
|
1867
|
+
// Every tool this turn ran, in the loop's own summary shape. Backs the
|
|
1868
|
+
// result's toolCalls / toolResults / toolExecutions getters, which read it
|
|
1869
|
+
// live so a consumer that drains the stream first sees the full turn.
|
|
1870
|
+
const streamToolSummaries = [];
|
|
1846
1871
|
const streamStartTime = Date.now();
|
|
1847
1872
|
// Mutable-reference contract from StreamResult.metadata: created before
|
|
1848
1873
|
// the loop and filled once the turn drains, because wrapper spreads
|
|
@@ -1965,7 +1990,27 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
1965
1990
|
}
|
|
1966
1991
|
return { conversation: rebuilt };
|
|
1967
1992
|
};
|
|
1968
|
-
const buildParams = (conversation) => {
|
|
1993
|
+
const buildParams = async (conversation, step) => {
|
|
1994
|
+
// Consulted whenever there is a base choice OR a hook: a hook with
|
|
1995
|
+
// no base choice still gets to force a tool on a given step.
|
|
1996
|
+
// No declared tools → no tool_choice and no hook call; Anthropic
|
|
1997
|
+
// rejects a tool_choice on a request without tools.
|
|
1998
|
+
const anthropicToolChoice = (turnToolChoice !== undefined || options.prepareStep) &&
|
|
1999
|
+
anthropicTools &&
|
|
2000
|
+
anthropicTools.length > 0
|
|
2001
|
+
? // Per step, so a prepareStep-forced choice gets the same
|
|
2002
|
+
// Claude 5.5/5.1 relaxation as the turn's base choice.
|
|
2003
|
+
relaxForcedToolChoice(modelId, toolChoiceToAnthropic(await resolveStepToolChoice({
|
|
2004
|
+
base: turnToolChoice,
|
|
2005
|
+
step,
|
|
2006
|
+
toolChoiceSteps: options.toolChoiceSteps,
|
|
2007
|
+
prepareStep: options.prepareStep,
|
|
2008
|
+
steps: stepRecords,
|
|
2009
|
+
maxSteps,
|
|
2010
|
+
model: modelId,
|
|
2011
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
2012
|
+
})))
|
|
2013
|
+
: undefined;
|
|
1969
2014
|
// Mid-turn discovery sync: search_tools (tools.discovery) hydrates
|
|
1970
2015
|
// new tools into toolsRecord between steps; Claude only calls tools
|
|
1971
2016
|
// declared in the request, so advertise them now.
|
|
@@ -2055,33 +2100,77 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
2055
2100
|
const adapter = {
|
|
2056
2101
|
...baseAdapter,
|
|
2057
2102
|
buildToolResultMessages: (conversation, stepResult, toolResults, engineStep) => {
|
|
2103
|
+
const settledAt = new Date();
|
|
2058
2104
|
for (const result of toolResults) {
|
|
2059
2105
|
toolsUsed.push(result.name);
|
|
2106
|
+
if (!result.error) {
|
|
2107
|
+
toolsSucceeded.push(result.name);
|
|
2108
|
+
}
|
|
2109
|
+
streamToolSummaries.push({
|
|
2110
|
+
toolCallId: result.id,
|
|
2111
|
+
toolName: result.name,
|
|
2112
|
+
input: result.args,
|
|
2113
|
+
...(result.error
|
|
2114
|
+
? { error: result.error }
|
|
2115
|
+
: { output: result.output }),
|
|
2116
|
+
// The engine reports a step's results together, after the
|
|
2117
|
+
// batch has settled, so per-tool timing is not available here;
|
|
2118
|
+
// `toolExecutions` prefers the execution recorder's own records,
|
|
2119
|
+
// which do carry it.
|
|
2120
|
+
startTime: settledAt,
|
|
2121
|
+
endTime: settledAt,
|
|
2122
|
+
stepIndex: engineStep,
|
|
2123
|
+
});
|
|
2060
2124
|
}
|
|
2125
|
+
const rawBlocks = stepResult.raw;
|
|
2126
|
+
stepRecords.push(toPrepareStepRecord({
|
|
2127
|
+
stepNumber: engineStep,
|
|
2128
|
+
content: rawBlocks.map((block) => block),
|
|
2129
|
+
text: stepResult.text,
|
|
2130
|
+
toolCalls: stepResult.toolCalls.map((call) => ({
|
|
2131
|
+
toolName: call.name,
|
|
2132
|
+
toolCallId: call.id,
|
|
2133
|
+
input: call.args,
|
|
2134
|
+
})),
|
|
2135
|
+
toolResults: toolResults.map((result) => ({
|
|
2136
|
+
toolName: result.name,
|
|
2137
|
+
toolCallId: result.id,
|
|
2138
|
+
output: result.error ? { error: result.error } : result.output,
|
|
2139
|
+
})),
|
|
2140
|
+
finishReason: "tool-calls",
|
|
2141
|
+
inputTokens: stepResult.usage.inputTokens,
|
|
2142
|
+
outputTokens: stepResult.usage.outputTokens,
|
|
2143
|
+
...(stepResult.usage.cacheReadTokens !== undefined ||
|
|
2144
|
+
stepResult.usage.cacheWriteTokens !== undefined
|
|
2145
|
+
? {
|
|
2146
|
+
cacheReadTokens: stepResult.usage.cacheReadTokens ?? 0,
|
|
2147
|
+
cacheWriteTokens: stepResult.usage.cacheWriteTokens ?? 0,
|
|
2148
|
+
}
|
|
2149
|
+
: {}),
|
|
2150
|
+
}));
|
|
2151
|
+
// `tool:start` / `tool:end` are emitted by the executor itself
|
|
2152
|
+
// (ToolsManager, external MCP, neurolink.executeTool), so nothing is
|
|
2153
|
+
// emitted here: a second `tool:end` per result — with a zeroed
|
|
2154
|
+
// responseTime — used to be, and doubled every consumer's count.
|
|
2061
2155
|
const toolCallsForStorage = toolResults.map((result) => ({
|
|
2062
|
-
type: "tool-call",
|
|
2063
2156
|
toolCallId: result.id,
|
|
2064
2157
|
toolName: result.name,
|
|
2065
2158
|
args: result.args,
|
|
2159
|
+
stepIndex: engineStep,
|
|
2066
2160
|
}));
|
|
2067
2161
|
const toolResultsForStorage = toolResults.map((result) => result.error
|
|
2068
2162
|
? {
|
|
2069
|
-
type: "tool-result",
|
|
2070
2163
|
toolCallId: result.id,
|
|
2071
2164
|
toolName: result.name,
|
|
2072
2165
|
error: result.error,
|
|
2166
|
+
stepIndex: engineStep,
|
|
2073
2167
|
}
|
|
2074
2168
|
: {
|
|
2075
|
-
type: "tool-result",
|
|
2076
2169
|
toolCallId: result.id,
|
|
2077
2170
|
toolName: result.name,
|
|
2078
|
-
|
|
2171
|
+
output: result.output,
|
|
2172
|
+
stepIndex: engineStep,
|
|
2079
2173
|
});
|
|
2080
|
-
emitToolEndFromStepFinish(emitter, toolResultsForStorage.map((tr) => ({
|
|
2081
|
-
toolName: tr.toolName,
|
|
2082
|
-
result: "result" in tr ? tr.result : undefined,
|
|
2083
|
-
error: "error" in tr ? tr.error : undefined,
|
|
2084
|
-
})));
|
|
2085
2174
|
this.handleToolExecutionStorage(toolCallsForStorage, toolResultsForStorage, options, new Date()).catch((storageErr) => {
|
|
2086
2175
|
logger.warn("[AnthropicProvider] Failed to store tool executions", {
|
|
2087
2176
|
provider: this.providerName,
|
|
@@ -2344,7 +2433,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
2344
2433
|
}
|
|
2345
2434
|
}
|
|
2346
2435
|
};
|
|
2347
|
-
|
|
2436
|
+
const result = {
|
|
2348
2437
|
stream: transformedStream(),
|
|
2349
2438
|
provider: this.providerName,
|
|
2350
2439
|
// Cell-3 identity fix: `this.modelName` is the raw, possibly-unset
|
|
@@ -2355,8 +2444,14 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
2355
2444
|
// build the request. Reporting the raw field previously let a
|
|
2356
2445
|
// default-model turn's StreamResult claim an undefined/empty model.
|
|
2357
2446
|
model: modelId,
|
|
2358
|
-
toolCalls
|
|
2359
|
-
|
|
2447
|
+
// `toolCalls` / `toolResults` / `toolExecutions` are defined as live
|
|
2448
|
+
// getters below, over the per-turn summaries.
|
|
2449
|
+
// The live array: pushed as steps settle, so a consumer reading after
|
|
2450
|
+
// the drain sees every tool that ran and returned a value (a thrown or
|
|
2451
|
+
// not-found call is not "used"). This was never returned before, so
|
|
2452
|
+
// `toolsUsed` came back empty for every tool on this path and
|
|
2453
|
+
// `enhancedWithTools` was always false.
|
|
2454
|
+
toolsUsed: toolsSucceeded,
|
|
2360
2455
|
metadata: turnMetadata,
|
|
2361
2456
|
// Wire the deferred usage/finish promises into the analytics collector
|
|
2362
2457
|
// (mirrors openaiChatCompletionsBase). Without this the loop computed a
|
|
@@ -2384,6 +2479,40 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
2384
2479
|
return analytics;
|
|
2385
2480
|
}),
|
|
2386
2481
|
};
|
|
2482
|
+
// Live getters, mirroring openaiChatCompletionsBase: the wrapper layers
|
|
2483
|
+
// (BaseProvider.stream, NeuroLink.stream) re-apply accessor descriptors
|
|
2484
|
+
// rather than spreading values, so these resolve when read, after the
|
|
2485
|
+
// background loop has run the tools — a plain value here would be the
|
|
2486
|
+
// empty snapshot taken before the first chunk.
|
|
2487
|
+
Object.defineProperty(result, "toolCalls", {
|
|
2488
|
+
enumerable: true,
|
|
2489
|
+
configurable: true,
|
|
2490
|
+
get: () => toolCallsFromSummaries(streamToolSummaries),
|
|
2491
|
+
});
|
|
2492
|
+
Object.defineProperty(result, "toolResults", {
|
|
2493
|
+
enumerable: true,
|
|
2494
|
+
configurable: true,
|
|
2495
|
+
get: () => streamToolSummaries.map((s) => ({
|
|
2496
|
+
toolName: s.toolName,
|
|
2497
|
+
id: s.toolCallId,
|
|
2498
|
+
status: s.error ? "failure" : "success",
|
|
2499
|
+
...(s.error ? { error: s.error } : {}),
|
|
2500
|
+
...(s.output !== undefined
|
|
2501
|
+
? { output: s.output }
|
|
2502
|
+
: {}),
|
|
2503
|
+
})),
|
|
2504
|
+
});
|
|
2505
|
+
Object.defineProperty(result, "toolExecutions", {
|
|
2506
|
+
enumerable: true,
|
|
2507
|
+
configurable: true,
|
|
2508
|
+
get: () => resolveToolExecutionRecords(options, transformToolExecutions(streamToolSummaries.map((s) => ({
|
|
2509
|
+
toolName: s.toolName,
|
|
2510
|
+
input: s.input,
|
|
2511
|
+
output: s.error ? { error: s.error } : s.output,
|
|
2512
|
+
duration: s.endTime.getTime() - s.startTime.getTime(),
|
|
2513
|
+
})))),
|
|
2514
|
+
});
|
|
2515
|
+
return result;
|
|
2387
2516
|
}
|
|
2388
2517
|
async isAvailable() {
|
|
2389
2518
|
try {
|
|
@@ -382,8 +382,8 @@ export function createAnthropicLoopAdapter(config) {
|
|
|
382
382
|
execute: async (args, opts) => execute(args, opts),
|
|
383
383
|
};
|
|
384
384
|
},
|
|
385
|
-
buildStepRequest(conversation, step) {
|
|
386
|
-
return { raw: config.buildParams(conversation, step) };
|
|
385
|
+
async buildStepRequest(conversation, step) {
|
|
386
|
+
return { raw: await config.buildParams(conversation, step) };
|
|
387
387
|
},
|
|
388
388
|
async executeStep(request, channel, signal) {
|
|
389
389
|
const params = request.raw;
|
|
@@ -8,6 +8,7 @@ import { ERROR_CODES, NeuroLinkError } from "../../utils/errorHandling.js";
|
|
|
8
8
|
import { logger } from "../../utils/logger.js";
|
|
9
9
|
import { drainDetachedPump } from "../../utils/drainDetachedPump.js";
|
|
10
10
|
import { createGeminiLoopAdapter } from "../../core/geminiLoopAdapter.js";
|
|
11
|
+
import { isDirectTTSRequest } from "../../core/resolveRequestKind.js";
|
|
11
12
|
import { runAgenticLoop } from "../../core/loopEngine.js";
|
|
12
13
|
import { DEFAULT_TOOL_MAX_RETRIES, resolveToolTimeoutMs, } from "../../core/constants.js";
|
|
13
14
|
import { isToolsSchemaExclusionInForce } from "../../core/modules/structuredOutputPolicy.js";
|
|
@@ -2043,7 +2044,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
2043
2044
|
// TTS direct-synthesis mode: synthesise the input text directly (no LLM
|
|
2044
2045
|
// call). BaseProvider.runGenerateInActiveContext does the same dispatch
|
|
2045
2046
|
// — replicated here because AI Studio's override bypasses that path.
|
|
2046
|
-
if (options.tts
|
|
2047
|
+
if (isDirectTTSRequest(options.tts)) {
|
|
2047
2048
|
logger.info("[GoogleAIStudio] Routing TTS direct-synthesis to handleDirectTTSSynthesis", { model: modelName });
|
|
2048
2049
|
return this.handleDirectTTSSynthesis(options, Date.now());
|
|
2049
2050
|
}
|
|
@@ -2393,7 +2394,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
2393
2394
|
contents: [text],
|
|
2394
2395
|
});
|
|
2395
2396
|
const embedding = result.embeddings?.[0]?.values;
|
|
2396
|
-
if (!embedding) {
|
|
2397
|
+
if (!embedding || embedding.length === 0) {
|
|
2397
2398
|
throw new ProviderError("No embedding returned from Google AI", this.providerName);
|
|
2398
2399
|
}
|
|
2399
2400
|
logger.debug("Embedding generated successfully", {
|
|
@@ -2425,10 +2426,11 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
2425
2426
|
model: embeddingModelName,
|
|
2426
2427
|
count: texts.length,
|
|
2427
2428
|
});
|
|
2429
|
+
let rows;
|
|
2428
2430
|
try {
|
|
2429
2431
|
const apiKey = this.getApiKey();
|
|
2430
2432
|
const client = await createGoogleGenAIClient(apiKey, this.getBaseURL());
|
|
2431
|
-
|
|
2433
|
+
rows = [];
|
|
2432
2434
|
if (embeddingModelName.includes("gemini-embedding-2")) {
|
|
2433
2435
|
// @google/genai 2.x reads an array of strings for these models as ONE
|
|
2434
2436
|
// content with several parts, so a batch would come back as a single
|
|
@@ -2441,7 +2443,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
2441
2443
|
contents: [text],
|
|
2442
2444
|
})));
|
|
2443
2445
|
for (const result of results) {
|
|
2444
|
-
|
|
2446
|
+
rows.push(result.embeddings?.[0]?.values);
|
|
2445
2447
|
}
|
|
2446
2448
|
}
|
|
2447
2449
|
}
|
|
@@ -2450,20 +2452,13 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
2450
2452
|
model: embeddingModelName,
|
|
2451
2453
|
contents: texts,
|
|
2452
2454
|
});
|
|
2453
|
-
|
|
2455
|
+
rows.push(...(result.embeddings || []).map((e) => e.values));
|
|
2454
2456
|
}
|
|
2455
2457
|
// One vector per text, or fail: a silent mismatch would pair texts with
|
|
2456
2458
|
// the wrong vectors downstream.
|
|
2457
|
-
if (
|
|
2458
|
-
throw new Error(`Embedding response held ${
|
|
2459
|
+
if (rows.length !== texts.length) {
|
|
2460
|
+
throw new Error(`Embedding response held ${rows.length} vectors for ${texts.length} texts`);
|
|
2459
2461
|
}
|
|
2460
|
-
logger.debug("Batch embeddings generated successfully", {
|
|
2461
|
-
provider: this.providerName,
|
|
2462
|
-
model: embeddingModelName,
|
|
2463
|
-
count: embeddings.length,
|
|
2464
|
-
embeddingDimension: embeddings[0]?.length,
|
|
2465
|
-
});
|
|
2466
|
-
return embeddings;
|
|
2467
2462
|
}
|
|
2468
2463
|
catch (error) {
|
|
2469
2464
|
logger.error("Batch embedding generation failed", {
|
|
@@ -2473,6 +2468,26 @@ export class GoogleAIStudioProvider extends BaseProvider {
|
|
|
2473
2468
|
});
|
|
2474
2469
|
throw this.handleProviderError(error);
|
|
2475
2470
|
}
|
|
2471
|
+
// Checked after the try on purpose: a thrown error is re-worded by
|
|
2472
|
+
// handleProviderError from keywords in its text, so an index such as 429
|
|
2473
|
+
// or 502 would turn this into a rate-limit or server error.
|
|
2474
|
+
const embeddings = rows.map((values, index) => {
|
|
2475
|
+
if (!values || values.length === 0) {
|
|
2476
|
+
logger.error("Batch embedding returned no values", {
|
|
2477
|
+
model: embeddingModelName,
|
|
2478
|
+
index,
|
|
2479
|
+
});
|
|
2480
|
+
throw new ProviderError(`Embedding for the text at index ${index} came back without values`, this.providerName);
|
|
2481
|
+
}
|
|
2482
|
+
return values;
|
|
2483
|
+
});
|
|
2484
|
+
logger.debug("Batch embeddings generated successfully", {
|
|
2485
|
+
provider: this.providerName,
|
|
2486
|
+
model: embeddingModelName,
|
|
2487
|
+
count: embeddings.length,
|
|
2488
|
+
embeddingDimension: embeddings[0]?.length,
|
|
2489
|
+
});
|
|
2490
|
+
return embeddings;
|
|
2476
2491
|
}
|
|
2477
2492
|
getApiKey() {
|
|
2478
2493
|
const apiKey = this.credentials?.apiKey ||
|