@juspay/neurolink 12.12.10 → 12.12.12

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.
@@ -67,6 +67,7 @@ export const MODEL_CONTEXT_WINDOWS = {
67
67
  "mistralai/mixtral-8x7b-instruct-v0.1": 32_768,
68
68
  "microsoft/phi-4": 16_384,
69
69
  "google/gemma-3-27b-it": 8_192,
70
+ "openai/gpt-oss-20b": 131_072,
70
71
  },
71
72
  "lm-studio": {
72
73
  _default: 8_192,
@@ -644,6 +644,7 @@ export declare enum DeepSeekModels {
644
644
  * Note: NIM hosts hundreds of models; pass arbitrary IDs via --model.
645
645
  */
646
646
  export declare enum NvidiaNimModels {
647
+ GPT_OSS_20B = "openai/gpt-oss-20b",
647
648
  LLAMA_3_3_70B_INSTRUCT = "meta/llama-3.3-70b-instruct",
648
649
  LLAMA_3_1_405B_INSTRUCT = "meta/llama-3.1-405b-instruct",
649
650
  LLAMA_3_1_70B_INSTRUCT = "meta/llama-3.1-70b-instruct",
@@ -872,6 +872,13 @@ export var DeepSeekModels;
872
872
  */
873
873
  export var NvidiaNimModels;
874
874
  (function (NvidiaNimModels) {
875
+ // NVIDIA retired a large part of this list upstream on 2026-08-26 —
876
+ // llama-3.3-70b, llama-3.1-70b, llama-3.2-90b-vision, the deepseek-r1
877
+ // distill and gemma-3-27b all answer "no longer available" now. The members
878
+ // are kept so existing callers still compile, but the provider default
879
+ // below must point at something live: gpt-oss-20b is on the current roster
880
+ // and was probed for text, streaming, tool calling and structured output.
881
+ NvidiaNimModels["GPT_OSS_20B"] = "openai/gpt-oss-20b";
875
882
  // Meta Llama
876
883
  NvidiaNimModels["LLAMA_3_3_70B_INSTRUCT"] = "meta/llama-3.3-70b-instruct";
877
884
  NvidiaNimModels["LLAMA_3_1_405B_INSTRUCT"] = "meta/llama-3.1-405b-instruct";
@@ -22,7 +22,6 @@ export declare abstract class BaseProvider implements AIProvider {
22
22
  } | null): void;
23
23
  private messageBuilder;
24
24
  private streamHandler;
25
- private generationHandler;
26
25
  protected telemetryHandler: TelemetryHandler;
27
26
  private utilities;
28
27
  private readonly toolsManager;
@@ -185,30 +184,10 @@ export declare abstract class BaseProvider implements AIProvider {
185
184
  * @returns Promise resolving to ModelMessage array ready for AI SDK
186
185
  */
187
186
  protected buildMessagesForStream(options: StreamOptions | TextGenerationOptions): Promise<ModelMessage[]>;
188
- /**
189
- * Execute the generation with AI SDK - delegated to GenerationHandler
190
- */
191
- private executeGeneration;
192
- /**
193
- * Log generation completion information - delegated to GenerationHandler
194
- */
195
- private logGenerationComplete;
196
187
  /**
197
188
  * Record performance metrics - delegated to TelemetryHandler
198
189
  */
199
190
  protected recordPerformanceMetrics(usage: RawUsageObject | undefined, responseTime: number): Promise<void>;
200
- /**
201
- * Extract tool information from generation result - delegated to GenerationHandler
202
- */
203
- private extractToolInformation;
204
- /**
205
- * Format the enhanced result - delegated to GenerationHandler
206
- */
207
- private formatEnhancedResult;
208
- /**
209
- * Analyze AI response structure and log detailed debugging information - delegated to GenerationHandler
210
- */
211
- private analyzeAIResponse;
212
191
  /**
213
192
  * Text generation method - implements AIProvider interface
214
193
  * Tools are always available unless explicitly disabled
@@ -281,7 +260,6 @@ export declare abstract class BaseProvider implements AIProvider {
281
260
  private runGenerateInActiveContext;
282
261
  protected handleDirectTTSSynthesis(options: TextGenerationOptions, startTime: number): Promise<EnhancedGenerateResult>;
283
262
  private handleVideoFrameGeneration;
284
- private executeStandardGenerateFlow;
285
263
  /**
286
264
  * Close out a turn produced by a provider's own native generate loop.
287
265
  *
@@ -32,14 +32,13 @@ import { TimeoutError as AsyncTimeoutError, withTimeoutFn, } from "../utils/asyn
32
32
  import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
33
33
  import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
34
34
  import { getKeyCount, getKeysAsString } from "../utils/transformationUtils.js";
35
- import { ToolExecutionRecorder, resolveToolExecutionRecords, } from "./toolExecutionRecorder.js";
35
+ import { ToolExecutionRecorder } from "./toolExecutionRecorder.js";
36
36
  import { TTS_ERROR_CODES, TTSProcessor } from "../utils/ttsProcessor.js";
37
37
  import { executeVideoAnalysis, hasVideoFrames, } from "../utils/videoAnalysisProcessor.js";
38
38
  import { dedupeTools } from "./toolDedup.js";
39
39
  import { resolveToolPolicy, toolNameMatcher } from "../tools/toolPolicy.js";
40
40
  import { applyToolGate } from "../tools/toolGate.js";
41
41
  import { partitionToolsForDiscovery, isDiscoveryMetaTool, LARGE_CATALOG_WARN_THRESHOLD, } from "../tools/toolDiscovery.js";
42
- import { GenerationHandler } from "./modules/GenerationHandler.js";
43
42
  // Import modules for composition
44
43
  import { MessageBuilder } from "./modules/MessageBuilder.js";
45
44
  import { StreamHandler } from "./modules/StreamHandler.js";
@@ -137,7 +136,6 @@ export class BaseProvider {
137
136
  // alone.
138
137
  messageBuilder;
139
138
  streamHandler;
140
- generationHandler;
141
139
  telemetryHandler;
142
140
  utilities;
143
141
  toolsManager;
@@ -150,7 +148,6 @@ export class BaseProvider {
150
148
  this.messageBuilder = new MessageBuilder(this.providerName, this.modelName);
151
149
  this.streamHandler = new StreamHandler(this.providerName, this.modelName);
152
150
  this.telemetryHandler = new TelemetryHandler(this.providerName, this.modelName, this.neurolink);
153
- this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), { getEmitterFn: () => this.neurolink?.getEventEmitter() });
154
151
  this.utilities = new Utilities(this.providerName, this.modelName, this.defaultTimeout, this.middlewareOptions);
155
152
  this.toolsManager = new ToolsManager(this.providerName, this.directTools, this.neurolink, {
156
153
  isZodSchema: (schema) => this.isZodSchema(schema),
@@ -175,7 +172,6 @@ export class BaseProvider {
175
172
  this.messageBuilder = new MessageBuilder(this.providerName, this.modelName);
176
173
  this.streamHandler = new StreamHandler(this.providerName, this.modelName);
177
174
  this.telemetryHandler = new TelemetryHandler(this.providerName, this.modelName, this.neurolink);
178
- this.generationHandler = new GenerationHandler(this.providerName, this.modelName, () => this.supportsTools(), (options, type) => this.telemetryHandler.getTelemetryConfig(options, type), (toolCalls, toolResults, options, timestamp) => this.handleToolExecutionStorage(toolCalls, toolResults, options, timestamp), { getEmitterFn: () => this.neurolink?.getEventEmitter() });
179
175
  this.utilities = new Utilities(this.providerName, this.modelName, this.defaultTimeout, this.middlewareOptions);
180
176
  }
181
177
  /**
@@ -1202,42 +1198,12 @@ export class BaseProvider {
1202
1198
  async buildMessagesForStream(options) {
1203
1199
  return this.messageBuilder.buildMessagesForStream(options);
1204
1200
  }
1205
- /**
1206
- * Execute the generation with AI SDK - delegated to GenerationHandler
1207
- */
1208
- async executeGeneration(model, messages, tools, options) {
1209
- return this.generationHandler.executeGeneration(model, messages, tools, options);
1210
- }
1211
- /**
1212
- * Log generation completion information - delegated to GenerationHandler
1213
- */
1214
- logGenerationComplete(generateResult) {
1215
- this.generationHandler.logGenerationComplete(generateResult);
1216
- }
1217
1201
  /**
1218
1202
  * Record performance metrics - delegated to TelemetryHandler
1219
1203
  */
1220
1204
  async recordPerformanceMetrics(usage, responseTime) {
1221
1205
  await this.telemetryHandler.recordPerformanceMetrics(usage, responseTime);
1222
1206
  }
1223
- /**
1224
- * Extract tool information from generation result - delegated to GenerationHandler
1225
- */
1226
- extractToolInformation(generateResult) {
1227
- return this.generationHandler.extractToolInformation(generateResult);
1228
- }
1229
- /**
1230
- * Format the enhanced result - delegated to GenerationHandler
1231
- */
1232
- formatEnhancedResult(generateResult, tools, toolsUsed, toolExecutions, options) {
1233
- return this.generationHandler.formatEnhancedResult(generateResult, tools, toolsUsed, toolExecutions, options);
1234
- }
1235
- /**
1236
- * Analyze AI response structure and log detailed debugging information - delegated to GenerationHandler
1237
- */
1238
- analyzeAIResponse(result) {
1239
- this.generationHandler.analyzeAIResponse(result);
1240
- }
1241
1207
  /**
1242
1208
  * Text generation method - implements AIProvider interface
1243
1209
  * Tools are always available unless explicitly disabled
@@ -1398,13 +1364,28 @@ export class BaseProvider {
1398
1364
  if (requestKind === "tts-direct") {
1399
1365
  return this.handleDirectTTSSynthesis(options, startTime);
1400
1366
  }
1401
- const { tools, model } = await this.prepareGenerationContext(options);
1367
+ // Only `model` is used now — the video-frame path needs it. `tools`
1368
+ // fed the standard generate flow, which no longer exists.
1369
+ const { model } = await this.prepareGenerationContext(options);
1402
1370
  const messages = await this.buildMessages(options);
1403
1371
  const videoFrameResult = await this.handleVideoFrameGeneration(options, messages, model, startTime);
1404
1372
  if (videoFrameResult) {
1405
1373
  return videoFrameResult;
1406
1374
  }
1407
- return await this.executeStandardGenerateFlow(options, startTime, model, messages, tools);
1375
+ // Every provider that generates text overrides `generate()` and runs a
1376
+ // native loop. There is no shared fallback any more: the standard flow
1377
+ // called the ai package's `generateText`, and once that was gone the
1378
+ // flow could only throw. Reaching here means a provider was asked for
1379
+ // text without implementing it — an image or embedding provider handed
1380
+ // a text model, or a new subclass with no `generate()` yet.
1381
+ throw new NeuroLinkError({
1382
+ code: ERROR_CODES.INVALID_CONFIGURATION,
1383
+ message: `${this.providerName} cannot generate text: it does not override generate(). Every text provider implements a native generate() — see docs/plans/2026-09-03-completing-the-ai-sdk-removal.md`,
1384
+ category: ErrorCategory.CONFIGURATION,
1385
+ severity: ErrorSeverity.CRITICAL,
1386
+ retriable: false,
1387
+ context: { provider: this.providerName, model: this.modelName },
1388
+ });
1408
1389
  }
1409
1390
  catch (error) {
1410
1391
  otelSpan.setStatus({
@@ -1537,50 +1518,6 @@ export class BaseProvider {
1537
1518
  usage,
1538
1519
  }, options, startTime);
1539
1520
  }
1540
- async executeStandardGenerateFlow(options, startTime, model, messages, tools) {
1541
- // Apply a defensive default timeout when the caller didn't pass one.
1542
- // Without this guard, AI SDK's generateText() will wait forever on
1543
- // an upstream that accepts the connection but never produces a response
1544
- // (observed against the litellm gateway when a request triggers the
1545
- // team-access denial path — connection stays open, no response is sent,
1546
- // and the matrix test hangs the entire suite). Callers can still pass
1547
- // a larger value (e.g. video generation passes 10 min).
1548
- //
1549
- // A provider descriptor may declare a LARGER generate budget than the
1550
- // 3-min floor (litellm: 300s — slow proxied models routinely need more
1551
- // than 180s end-to-end even while streaming). The declared value only
1552
- // ever raises the default, never lowers it: several descriptors carry
1553
- // aspirational sub-180s numbers (openai 30s, bedrock 45s) that were
1554
- // never enforced on this path, and enforcing them now would break
1555
- // long-running generations that have always been allowed.
1556
- const descriptorGenerateMs = PROVIDER_DESCRIPTORS_BY_NAME.get(this.providerName)?.timeouts?.generateMs;
1557
- // An explicit, valid turnTimeoutMs is the caller's whole-turn contract
1558
- // and owns this hard abort; `timeout` then keeps its per-model-call
1559
- // meaning (it reaches the model layer via providerOptions.neurolink).
1560
- // Before this, `timeout` alone bounded the ENTIRE multi-step loop, so a
1561
- // caller asking for a 40-minute turn of 5-minute calls was killed at 5
1562
- // minutes flat — mid-loop, dressed as "Request was aborted.".
1563
- const generateResult = await this.withTurnTimeout(options, descriptorGenerateMs, (timedOptions) => this.executeGeneration(model, messages, tools, timedOptions));
1564
- this.analyzeAIResponse(generateResult);
1565
- this.logGenerationComplete(generateResult);
1566
- const responseTime = Date.now() - startTime;
1567
- const { toolsUsed, toolExecutions } = this.extractToolInformation(generateResult);
1568
- // Prefer the per-call recorder's real records (params/result/timing per
1569
- // execution); fall back to a conversion of the step-extraction entries
1570
- // for tools the recorder could not wrap (provider-executed tools).
1571
- const toolExecutionRecords = resolveToolExecutionRecords(options, toolExecutions);
1572
- let enhancedResult = this.formatEnhancedResult(generateResult, tools, toolsUsed, toolExecutionRecords, options);
1573
- // Recorded AFTER formatEnhancedResult so telemetry sees the same usage
1574
- // the caller gets: the cross-step aggregate (totalUsage, not last-step
1575
- // usage) WITH the providerMetadata cache merge applied — otherwise
1576
- // providers whose cache data lives only in providerMetadata would have
1577
- // their cache tokens billed at the full input rate in OTEL metrics,
1578
- // diverging from analytics.cost.
1579
- await this.recordPerformanceMetrics(enhancedResult.usage, responseTime);
1580
- enhancedResult = await this.synthesizeAIResponseIfNeeded(enhancedResult, options);
1581
- const finalResult = await this.enhanceResult(enhancedResult, options, startTime);
1582
- return finalResult;
1583
- }
1584
1521
  /**
1585
1522
  * Close out a turn produced by a provider's own native generate loop.
1586
1523
  *
@@ -321,7 +321,7 @@ const HAND_DESCRIPTORS = [
321
321
  baseURL: "NVIDIA_NIM_BASE_URL",
322
322
  model: "NVIDIA_NIM_MODEL",
323
323
  },
324
- defaultModel: NvidiaNimModels.LLAMA_3_3_70B_INSTRUCT,
324
+ defaultModel: NvidiaNimModels.GPT_OSS_20B,
325
325
  toolSupport: "native",
326
326
  localRuntime: false,
327
327
  healthCheck: "env-only",
@@ -174,7 +174,10 @@ export class ProviderRegistry {
174
174
  const nimCreds = credentials;
175
175
  const { NvidiaNimProvider } = await import("../providers/nvidiaNim/index.js");
176
176
  return new NvidiaNimProvider(modelName, sdk, undefined, nimCreds);
177
- }, process.env.NVIDIA_NIM_MODEL || NvidiaNimModels.LLAMA_3_3_70B_INSTRUCT, ["nvidia", "nim", "nvidia-nim"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.NVIDIA_NIM));
177
+ }, process.env.NVIDIA_NIM_MODEL ||
178
+ PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.NVIDIA_NIM)
179
+ ?.defaultModel ||
180
+ NvidiaNimModels.GPT_OSS_20B, ["nvidia", "nim", "nvidia-nim"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.NVIDIA_NIM));
178
181
  // Register LM Studio provider (local)
179
182
  ProviderFactory.registerProvider(AIProviderName.LM_STUDIO, async (modelName, _providerName, sdk, _region, credentials) => {
180
183
  const lmStudioCreds = credentials;
@@ -1280,7 +1280,10 @@ export class AnthropicProvider extends BaseProvider {
1280
1280
  };
1281
1281
  },
1282
1282
  doStream: () => {
1283
- throw new Error(`${providerName}: doStream is not implemented on the delegating model the streaming path uses executeStream directly.`);
1283
+ throw new Error(`${providerName}: doStream is not implemented on the delegating model. ` +
1284
+ `NeuroLink streams through executeStream, reached via NeuroLink.stream() — ` +
1285
+ `use that (the browser bundle exports the NeuroLink class) rather than ` +
1286
+ `calling doStream on a model handle.`);
1284
1287
  },
1285
1288
  };
1286
1289
  return delegatingModel;
@@ -5,6 +5,7 @@ import { logger } from "../../utils/logger.js";
5
5
  import { redactUrlCredentials } from "../../utils/logSanitize.js";
6
6
  import { createNvidiaNimConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
7
7
  import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
8
+ import { PROVIDER_DESCRIPTORS_BY_NAME } from "../../factories/providerDescriptors.js";
8
9
  /**
9
10
  * Decide whether a NIM 400 response body is a rejection of the named
10
11
  * field (as opposed to an unrelated 400 that happens to mention the
@@ -136,7 +137,16 @@ const getNimApiKey = () => {
136
137
  return validateApiKey(createNvidiaNimConfig());
137
138
  };
138
139
  const getDefaultNimModel = () => {
139
- return getProviderModel("NVIDIA_NIM_MODEL", NvidiaNimModels.LLAMA_3_3_70B_INSTRUCT);
140
+ // NVIDIA retired meta/llama-3.3-70b-instruct upstream on 2026-08-26, so this
141
+ // fallback answered "no longer available" for anyone who selected nvidia-nim
142
+ // without naming a model. gpt-oss-20b is on the current roster and was
143
+ // probed for text, streaming, tool calling and structured output.
144
+ // Read the descriptor rather than repeating the literal. Review on this PR
145
+ // pointed out the default lived in four places and that changing only some
146
+ // of them is a no-op — which happened twice while tracking this down. The
147
+ // descriptor is the Factory+Registry convention's source of truth.
148
+ return getProviderModel("NVIDIA_NIM_MODEL", PROVIDER_DESCRIPTORS_BY_NAME.get("nvidia-nim")
149
+ ?.defaultModel ?? NvidiaNimModels.GPT_OSS_20B);
140
150
  };
141
151
  /**
142
152
  * NVIDIA NIM Provider — native HTTP+SSE, no AI SDK.
@@ -784,7 +784,10 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
784
784
  };
785
785
  },
786
786
  doStream: () => {
787
- throw new Error(`${providerName}: doStream is not implemented on the delegating model the streaming path uses executeStream directly.`);
787
+ throw new Error(`${providerName}: doStream is not implemented on the delegating model. ` +
788
+ `NeuroLink streams through executeStream, reached via NeuroLink.stream() — ` +
789
+ `use that (the browser bundle exports the NeuroLink class) rather than ` +
790
+ `calling doStream on a model handle.`);
788
791
  },
789
792
  };
790
793
  }
@@ -420,7 +420,7 @@ export const DEFAULT_MODELS = {
420
420
  [AIProviderName.OPENROUTER]: OpenRouterModels.CLAUDE_SONNET_4_5,
421
421
  [AIProviderName.OPENAI_COMPATIBLE]: "gpt-4o",
422
422
  [AIProviderName.DEEPSEEK]: DeepSeekModels.DEEPSEEK_CHAT,
423
- [AIProviderName.NVIDIA_NIM]: NvidiaNimModels.LLAMA_3_3_70B_INSTRUCT,
423
+ [AIProviderName.NVIDIA_NIM]: NvidiaNimModels.GPT_OSS_20B,
424
424
  // LM Studio + llama.cpp auto-discover their loaded model from /v1/models;
425
425
  // an empty default is the documented signal to use that path.
426
426
  [AIProviderName.LM_STUDIO]: "",
@@ -500,6 +500,14 @@ const PRICING = {
500
500
  },
501
501
  },
502
502
  "nvidia-nim": {
503
+ // Source: NVIDIA build.nvidia.com listed rates for openai/gpt-oss-20b,
504
+ // read 2026-09-06. NIM prices and models both rot — this PR exists
505
+ // because the default model was decommissioned — so re-check against
506
+ // https://build.nvidia.com/openai/gpt-oss-20b rather than trusting this.
507
+ "openai/gpt-oss-20b": {
508
+ input: 0.05 / 1_000_000,
509
+ output: 0.2 / 1_000_000,
510
+ },
503
511
  "meta/llama-3.3-70b-instruct": {
504
512
  input: 0.4 / 1_000_000,
505
513
  output: 0.4 / 1_000_000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.12.10",
3
+ "version": "12.12.12",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -1,145 +0,0 @@
1
- /**
2
- * Generation Handler Module
3
- *
4
- * Handles text generation execution, result formatting, and tool information extraction.
5
- * Extracted from BaseProvider to follow Single Responsibility Principle.
6
- *
7
- * Responsibilities:
8
- * - Generation execution with AI SDK
9
- * - Tool information extraction
10
- * - Result formatting and enhancement
11
- * - Response analysis and logging
12
- *
13
- * @module core/modules/GenerationHandler
14
- */
15
- import type { GenerateTextResult, AIProviderName, EnhancedGenerateResult, NeuroLinkEvents, StandardRecord, TextGenerationOptions, ToolExecutionRecord, TypedEventEmitter } from "../../types/index.js";
16
- import type { LanguageModel, ModelMessage, Tool } from "../../types/index.js";
17
- /**
18
- * Turn budget + wrap-up deadline (parity with the googleVertex native loops).
19
- * A deadline is engaged only when the caller expressed one: turnTimeoutMs
20
- * wins, else an explicit generate timeout. Callers that set neither keep the
21
- * pre-existing behaviour (no wrap-up; the outer defensive timeout in
22
- * executeStandardGenerateFlow still applies). With `wrapupTimeLeadMs` left of
23
- * the deadline, the loop stops offering tools (toolChoice: "none") so the
24
- * model spends the remaining budget producing a final answer instead of being
25
- * guillotined mid-tool-loop with all work discarded. The lead is clamped to a
26
- * quarter of the budget so short explicit timeouts (e.g. 30s) don't trigger
27
- * wrap-up on the very first step.
28
- *
29
- * `turnStartMs` anchors the deadline to the ORIGINAL generation start:
30
- * callGenerateText re-runs on executeGeneration's fallback retries
31
- * (structured-output conflict, temperature-deprecated) and provider retries,
32
- * and a deadline computed from Date.now() per attempt would hand each retry
33
- * a fresh budget — multiplying the caller's wall-clock cap.
34
- */
35
- export declare function resolveTurnBudget(options: TextGenerationOptions, turnStartMs: number): {
36
- callerTimeoutMs: number | undefined;
37
- turnBudgetMs: number | undefined;
38
- wrapupLeadMs: number;
39
- turnDeadline: number | undefined;
40
- };
41
- /**
42
- * GenerationHandler class - Handles text generation operations for AI providers
43
- */
44
- export declare class GenerationHandler {
45
- private readonly providerName;
46
- private readonly modelName;
47
- private readonly supportsToolsFn;
48
- private readonly getTelemetryConfigFn;
49
- private readonly handleToolStorageFn;
50
- /**
51
- * The remaining, optional dependencies.
52
- *
53
- * Grouped rather than added as further positional parameters: the
54
- * constructor is already at the six-parameter cap, and both of these are
55
- * optional injection seams rather than required collaborators.
56
- *
57
- * `generateTextFn` exists because every other dependency of this class
58
- * arrives through the constructor while `generateText` was reached by
59
- * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
60
- * ESM re-export cannot be substituted from a test, so asserting on the
61
- * arguments this class builds — the entire contract of the system-message
62
- * hoisting below — had no seam to work through. Production passes neither.
63
- */
64
- private readonly deps;
65
- constructor(providerName: AIProviderName, modelName: string, supportsToolsFn: () => boolean, getTelemetryConfigFn: (options: TextGenerationOptions, type: string) => {
66
- isEnabled: boolean;
67
- functionId?: string;
68
- metadata?: Record<string, string | number | boolean>;
69
- } | undefined, handleToolStorageFn: (toolCalls: unknown[], toolResults: unknown[], options: TextGenerationOptions, timestamp: Date) => Promise<void>,
70
- /**
71
- * The remaining, optional dependencies.
72
- *
73
- * Grouped rather than added as further positional parameters: the
74
- * constructor is already at the six-parameter cap, and both of these are
75
- * optional injection seams rather than required collaborators.
76
- *
77
- * `generateTextFn` exists because every other dependency of this class
78
- * arrives through the constructor while `generateText` was reached by
79
- * static import. `utils/generation.ts` is a bare re-export of `ai`, and an
80
- * ESM re-export cannot be substituted from a test, so asserting on the
81
- * arguments this class builds — the entire contract of the system-message
82
- * hoisting below — had no seam to work through. Production passes neither.
83
- */
84
- deps?: {
85
- getEmitterFn?: () => TypedEventEmitter<NeuroLinkEvents> | undefined;
86
- generateTextFn?: (options: Record<string, unknown>) => Promise<GenerateTextResult<Record<string, Tool>, unknown>>;
87
- });
88
- /**
89
- * Helper method to call generateText with optional structured output
90
- * @private
91
- */
92
- /**
93
- * The ai-package generate loop.
94
- *
95
- * Unreachable: every text provider now implements a native generate() and
96
- * none of them return here. That was established by trapping the seam —
97
- * replacing the ai package's generateText with a throwing stub left the full
98
- * provider matrix passing and zero cells reaching it — and non-text request
99
- * kinds return from runGenerateInActiveContext before this handler is
100
- * consulted.
101
- *
102
- * Kept as an explicit failure rather than deleted outright so a provider
103
- * added without a native generate() fails loudly here instead of silently
104
- * reintroducing a dependency on the removed package.
105
- */
106
- private callGenerateText;
107
- executeGeneration(model: LanguageModel, messages: ModelMessage[], tools: Record<string, Tool>, options: TextGenerationOptions): Promise<GenerateTextResult<Record<string, Tool>, unknown>>;
108
- /**
109
- * Extract cache metrics from provider metadata (e.g. Anthropic's providerMetadata.anthropic)
110
- * The AI SDK's LanguageModelUsage only has inputTokens/outputTokens.
111
- * Cache metrics are surfaced via providerMetadata by provider-specific SDK adapters.
112
- */
113
- /**
114
- * Set gen_ai usage attributes + cache-aware cost on the span from the
115
- * CROSS-STEP aggregate (result.totalUsage). result.usage is the LAST step
116
- * only — using it undercounted every multi-step tool loop, and pricing the
117
- * raw cache-inclusive inputTokens without the cache fields billed cache
118
- * reads at the full input rate.
119
- */
120
- private setUsageSpanAttributes;
121
- private extractCacheMetricsFromProviderMetadata;
122
- /**
123
- * Log generation completion information
124
- */
125
- logGenerationComplete(generateResult: GenerateTextResult<Record<string, Tool>, unknown>): void;
126
- /**
127
- * Extract tool information from generation result
128
- */
129
- extractToolInformation(generateResult: GenerateTextResult<Record<string, Tool>, unknown>): {
130
- toolsUsed: string[];
131
- toolExecutions: Array<{
132
- name: string;
133
- input: StandardRecord;
134
- output: unknown;
135
- }>;
136
- };
137
- /**
138
- * Format the enhanced result
139
- */
140
- formatEnhancedResult(generateResult: GenerateTextResult<Record<string, Tool>, unknown>, tools: Record<string, Tool>, toolsUsed: string[], toolExecutions: ToolExecutionRecord[], options: TextGenerationOptions): EnhancedGenerateResult;
141
- /**
142
- * Analyze AI response structure and log detailed debugging information
143
- */
144
- analyzeAIResponse(rawResult: unknown): void;
145
- }