@juspay/neurolink 9.81.3 → 9.83.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +4 -0
  3. package/dist/browser/neurolink.min.js +342 -342
  4. package/dist/cli/commands/proxy.js +5 -0
  5. package/dist/cli/factories/commandFactory.d.ts +2 -1
  6. package/dist/cli/factories/commandFactory.js +36 -16
  7. package/dist/cli/utils/audioPlayer.d.ts +37 -0
  8. package/dist/cli/utils/audioPlayer.js +138 -0
  9. package/dist/core/modules/GenerationHandler.js +7 -1
  10. package/dist/index.d.ts +6 -1
  11. package/dist/index.js +14 -6
  12. package/dist/lib/core/modules/GenerationHandler.js +7 -1
  13. package/dist/lib/index.d.ts +6 -1
  14. package/dist/lib/index.js +14 -6
  15. package/dist/lib/providers/googleAiStudio.js +25 -4
  16. package/dist/lib/providers/googleNativeGemini3.js +22 -2
  17. package/dist/lib/providers/googleVertex.js +28 -4
  18. package/dist/lib/proxy/proxyDispatcher.d.ts +5 -0
  19. package/dist/lib/proxy/proxyDispatcher.js +61 -0
  20. package/dist/lib/types/common.d.ts +3 -0
  21. package/dist/lib/types/providers.d.ts +8 -0
  22. package/dist/lib/utils/pricing.js +144 -26
  23. package/dist/lib/utils/systemMessages.d.ts +29 -0
  24. package/dist/lib/utils/systemMessages.js +45 -0
  25. package/dist/lib/utils/tokenUtils.d.ts +11 -0
  26. package/dist/lib/utils/tokenUtils.js +33 -2
  27. package/dist/providers/googleAiStudio.js +25 -4
  28. package/dist/providers/googleNativeGemini3.js +22 -2
  29. package/dist/providers/googleVertex.js +28 -4
  30. package/dist/proxy/proxyDispatcher.d.ts +5 -0
  31. package/dist/proxy/proxyDispatcher.js +60 -0
  32. package/dist/types/common.d.ts +3 -0
  33. package/dist/types/providers.d.ts +8 -0
  34. package/dist/utils/pricing.js +144 -26
  35. package/dist/utils/systemMessages.d.ts +29 -0
  36. package/dist/utils/systemMessages.js +44 -0
  37. package/dist/utils/tokenUtils.d.ts +11 -0
  38. package/dist/utils/tokenUtils.js +33 -2
  39. package/package.json +3 -2
@@ -17,6 +17,7 @@ import ora from "ora";
17
17
  import { buildProxyHealthResponse, createProxyReadinessState, markProxyReady, waitForProxyReadiness, } from "../../lib/proxy/proxyHealth.js";
18
18
  import { logger } from "../../lib/utils/logger.js";
19
19
  import { formatUptime, isProcessRunning, StateFileManager, } from "../utils/serverUtils.js";
20
+ import { configureProxyKeepAliveDispatcher } from "../../lib/proxy/proxyDispatcher.js";
20
21
  import { loadProxyEnvFile, resolveProxyEnvFile, } from "../../lib/proxy/proxyEnv.js";
21
22
  import { createRequire } from "node:module";
22
23
  import { fileURLToPath } from "node:url";
@@ -1285,6 +1286,10 @@ async function startProxyCommandHandler(argv) {
1285
1286
  await ensureProxyStartAllowed(spinner);
1286
1287
  }
1287
1288
  const loadedEnvFile = await loadProxyStartEnv(argv, spinner);
1289
+ // Reuse upstream TCP connections (longer keep-alive + bounded pool) instead
1290
+ // of opening a new flow per request — cuts outbound flow churn through host
1291
+ // content-filters. Runs once, after env load so it can be tuned via env.
1292
+ configureProxyKeepAliveDispatcher();
1288
1293
  const { neurolink, cleanupLogs } = await createProxyNeurolinkRuntime(devPaths?.logsDir);
1289
1294
  const { proxyConfig, strategy, modelRouter, passthrough, primaryAccountKey, } = await loadProxyStartConfiguration(argv, spinner);
1290
1295
  if (spinner) {
@@ -35,8 +35,9 @@ export declare class CLICommandFactory {
35
35
  private static validateAnthropicSubscriptionOptions;
36
36
  private static handleOutput;
37
37
  /**
38
- * Helper method to handle TTS audio file output
38
+ * Helper method to handle TTS audio file output and playback
39
39
  * Saves audio to file when --tts-output flag is provided
40
+ * Plays audio when --tts-play flag is provided
40
41
  */
41
42
  private static handleTTSOutput;
42
43
  /**
@@ -20,6 +20,7 @@ import { handleError } from "../errorHandler.js";
20
20
  import { LoopSession } from "../loop/session.js";
21
21
  import { initializeCliParser } from "../parser.js";
22
22
  import { formatFileSize, saveAudioToFile } from "../utils/audioFileUtils.js";
23
+ import { playAudio } from "../utils/audioPlayer.js";
23
24
  import { resolveFilePaths } from "../utils/pathResolver.js";
24
25
  import { animatedWrite } from "../utils/typewriter.js";
25
26
  import { createStreamAbortHandler } from "../utils/abortHandler.js";
@@ -1040,13 +1041,15 @@ export class CLICommandFactory {
1040
1041
  }
1041
1042
  }
1042
1043
  /**
1043
- * Helper method to handle TTS audio file output
1044
+ * Helper method to handle TTS audio file output and playback
1044
1045
  * Saves audio to file when --tts-output flag is provided
1046
+ * Plays audio when --tts-play flag is provided
1045
1047
  */
1046
1048
  static async handleTTSOutput(result, options) {
1047
- // Check if --tts-output flag is provided
1048
1049
  const ttsOutputPath = options.ttsOutput;
1049
- if (!ttsOutputPath) {
1050
+ const shouldPlay = options.ttsPlay;
1051
+ // Nothing to do if neither save nor play is requested
1052
+ if (!ttsOutputPath && !shouldPlay) {
1050
1053
  return;
1051
1054
  }
1052
1055
  // Extract audio from result with proper type checking
@@ -1061,20 +1064,36 @@ export class CLICommandFactory {
1061
1064
  }
1062
1065
  return;
1063
1066
  }
1064
- try {
1065
- // Save audio to file
1066
- const saveResult = await saveAudioToFile(audio, ttsOutputPath);
1067
- if (saveResult.success) {
1068
- if (!options.quiet) {
1069
- logger.always(chalk.green(`🔊 Audio saved to: ${saveResult.path} (${formatFileSize(saveResult.size)})`));
1067
+ // Save audio to file if --tts-output is provided
1068
+ if (ttsOutputPath) {
1069
+ try {
1070
+ const saveResult = await saveAudioToFile(audio, ttsOutputPath);
1071
+ if (saveResult.success) {
1072
+ if (!options.quiet) {
1073
+ logger.always(chalk.green(`🔊 Audio saved to: ${saveResult.path} (${formatFileSize(saveResult.size)})`));
1074
+ }
1075
+ }
1076
+ else {
1077
+ handleError(new Error(saveResult.error || "Failed to save audio file"), "TTS Output");
1070
1078
  }
1071
1079
  }
1072
- else {
1073
- handleError(new Error(saveResult.error || "Failed to save audio file"), "TTS Output");
1080
+ catch (error) {
1081
+ handleError(error, "TTS Output");
1074
1082
  }
1075
1083
  }
1076
- catch (error) {
1077
- handleError(error, "TTS Output");
1084
+ // Play audio if --tts-play is provided
1085
+ if (shouldPlay) {
1086
+ try {
1087
+ if (!options.quiet) {
1088
+ logger.always(chalk.blue("Playing audio..."));
1089
+ }
1090
+ await playAudio(audio.buffer, audio.format);
1091
+ }
1092
+ catch (err) {
1093
+ // Non-fatal: warn but don't crash
1094
+ logger.always(chalk.yellow(`Audio playback failed: ${err.message}`));
1095
+ logger.always(chalk.yellow(" Tip: Save the audio with --tts-output <file> and play manually."));
1096
+ }
1078
1097
  }
1079
1098
  }
1080
1099
  /**
@@ -2898,15 +2917,16 @@ export class CLICommandFactory {
2898
2917
  logger.always(`\nOutput saved to ${options.output}`);
2899
2918
  }
2900
2919
  }
2901
- // Handle TTS audio output if --tts-output is provided
2920
+ // Handle TTS audio output/playback if --tts-output or --tts-play is provided
2902
2921
  // Note: For streaming, TTS audio is collected during the stream
2903
2922
  // and saved at the end if available
2904
2923
  const ttsOutputPath = options.ttsOutput;
2905
- if (ttsOutputPath) {
2924
+ const shouldPlay = options.ttsPlay;
2925
+ if (ttsOutputPath || shouldPlay) {
2906
2926
  // For now, streaming TTS output is not yet available
2907
2927
  // This will be enabled when the TTS streaming infrastructure is complete
2908
2928
  if (!options.quiet) {
2909
- logger.always(chalk.yellow("⚠️ TTS audio output for streaming is not yet available. Use 'generate' command for TTS output."));
2929
+ logger.always(chalk.yellow("TTS audio for streaming is not yet available. Use 'generate' command for TTS output."));
2910
2930
  }
2911
2931
  }
2912
2932
  // Debug output for streaming
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Audio playback utilities for CLI
3
+ *
4
+ * Provides functionality for playing TTS audio using platform-specific
5
+ * CLI tools with proper cleanup and error handling.
6
+ *
7
+ * @module cli/utils/audioPlayer
8
+ */
9
+ import type { AudioFormat } from "../../lib/types/index.js";
10
+ /**
11
+ * Get the file extension for an audio format
12
+ *
13
+ * @param format - Audio format
14
+ * @returns File extension string (e.g., "mp3", "wav")
15
+ */
16
+ export declare function getAudioExtension(format: AudioFormat): string;
17
+ /**
18
+ * Play audio from a buffer using platform-specific CLI tools
19
+ *
20
+ * Writes the buffer to a temporary file, plays it using the appropriate
21
+ * system audio player, and cleans up the temp file afterward.
22
+ *
23
+ * Supported platforms:
24
+ * - macOS: uses `afplay` (built-in, supports mp3/wav/aac/flac)
25
+ * - Linux: uses `paplay` for non-wav, `aplay` for wav
26
+ * - Windows: uses PowerShell SoundPlayer (wav) or WMPlayer.OCX (mp3)
27
+ *
28
+ * @param buffer - Audio data buffer
29
+ * @param format - Audio format (mp3, wav, ogg, opus)
30
+ * @throws Error if playback fails or platform is unsupported
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * await playAudio(audioBuffer, "mp3");
35
+ * ```
36
+ */
37
+ export declare function playAudio(buffer: Buffer, format: AudioFormat): Promise<void>;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Audio playback utilities for CLI
3
+ *
4
+ * Provides functionality for playing TTS audio using platform-specific
5
+ * CLI tools with proper cleanup and error handling.
6
+ *
7
+ * @module cli/utils/audioPlayer
8
+ */
9
+ import { execFile } from "node:child_process";
10
+ import fs from "node:fs";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import { promisify } from "node:util";
14
+ const execFileAsync = promisify(execFile);
15
+ /**
16
+ * Get the file extension for an audio format
17
+ *
18
+ * @param format - Audio format
19
+ * @returns File extension string (e.g., "mp3", "wav")
20
+ */
21
+ export function getAudioExtension(format) {
22
+ switch (format) {
23
+ case "mp3":
24
+ return "mp3";
25
+ case "wav":
26
+ return "wav";
27
+ case "ogg":
28
+ return "ogg";
29
+ case "opus":
30
+ return "opus";
31
+ default:
32
+ return "mp3";
33
+ }
34
+ }
35
+ /**
36
+ * Get the platform-specific audio player command and arguments
37
+ *
38
+ * @param filePath - Path to the audio file
39
+ * @param format - Audio format
40
+ * @returns Object with command and args for execFile
41
+ */
42
+ function getPlayerCommand(filePath, format) {
43
+ const platform = process.platform;
44
+ switch (platform) {
45
+ case "darwin":
46
+ return { command: "afplay", args: [filePath] };
47
+ case "linux":
48
+ if (format === "wav") {
49
+ return { command: "aplay", args: [filePath] };
50
+ }
51
+ return { command: "paplay", args: [filePath] };
52
+ case "win32":
53
+ if (format === "wav") {
54
+ return {
55
+ command: "powershell",
56
+ args: [
57
+ "-NoProfile",
58
+ "-Command",
59
+ `(New-Object System.Media.SoundPlayer '${filePath}').PlaySync()`,
60
+ ],
61
+ };
62
+ }
63
+ return {
64
+ command: "powershell",
65
+ args: [
66
+ "-NoProfile",
67
+ "-Command",
68
+ `$player = New-Object -ComObject WMPlayer.OCX; $player.URL = '${filePath}'; $player.controls.play(); Start-Sleep -Seconds 1; while ($player.playState -eq 3) { Start-Sleep -Milliseconds 100 }; $player.close()`,
69
+ ],
70
+ };
71
+ default:
72
+ throw new Error(`Unsupported platform: ${platform}. Audio playback is supported on macOS, Linux, and Windows.`);
73
+ }
74
+ }
75
+ /**
76
+ * Play audio from a buffer using platform-specific CLI tools
77
+ *
78
+ * Writes the buffer to a temporary file, plays it using the appropriate
79
+ * system audio player, and cleans up the temp file afterward.
80
+ *
81
+ * Supported platforms:
82
+ * - macOS: uses `afplay` (built-in, supports mp3/wav/aac/flac)
83
+ * - Linux: uses `paplay` for non-wav, `aplay` for wav
84
+ * - Windows: uses PowerShell SoundPlayer (wav) or WMPlayer.OCX (mp3)
85
+ *
86
+ * @param buffer - Audio data buffer
87
+ * @param format - Audio format (mp3, wav, ogg, opus)
88
+ * @throws Error if playback fails or platform is unsupported
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * await playAudio(audioBuffer, "mp3");
93
+ * ```
94
+ */
95
+ export async function playAudio(buffer, format) {
96
+ const ext = getAudioExtension(format);
97
+ const tempFile = path.join(os.tmpdir(), `nl-tts-${Date.now()}.${ext}`);
98
+ try {
99
+ // Write audio buffer to temp file
100
+ await fs.promises.writeFile(tempFile, buffer);
101
+ const { command, args } = getPlayerCommand(tempFile, format);
102
+ try {
103
+ await execFileAsync(command, args);
104
+ }
105
+ catch (execError) {
106
+ const err = execError;
107
+ // Handle binary not found
108
+ if (err.code === "ENOENT") {
109
+ if (process.platform === "linux" && command === "paplay") {
110
+ // Fallback to aplay on Linux
111
+ try {
112
+ await execFileAsync("aplay", [tempFile]);
113
+ return;
114
+ }
115
+ catch (fallbackError) {
116
+ const fbErr = fallbackError;
117
+ if (fbErr.code === "ENOENT") {
118
+ throw new Error("Neither paplay nor aplay found. Install PulseAudio (paplay) or ALSA (aplay) for audio playback.", { cause: fallbackError });
119
+ }
120
+ throw fallbackError;
121
+ }
122
+ }
123
+ throw new Error(`Audio player '${command}' not found. Ensure it is installed and available in PATH.`, { cause: execError });
124
+ }
125
+ throw execError;
126
+ }
127
+ }
128
+ finally {
129
+ // Always clean up temp file
130
+ try {
131
+ await fs.promises.unlink(tempFile);
132
+ }
133
+ catch {
134
+ // Ignore cleanup errors
135
+ }
136
+ }
137
+ }
138
+ //# sourceMappingURL=audioPlayer.js.map
@@ -26,6 +26,7 @@ import { coerceJsonToSchema } from "../../utils/json/coerce.js";
26
26
  import { NoObjectGeneratedError } from "../../utils/generationErrors.js";
27
27
  import { Output, stepCountIs } from "../../utils/tool.js";
28
28
  import { generateText } from "../../utils/generation.js";
29
+ import { extractSystemMessages } from "../../utils/systemMessages.js";
29
30
  const genTracer = tracers.generation;
30
31
  /**
31
32
  * Safely preview-serialize a value for debug logging.
@@ -105,9 +106,14 @@ export class GenerationHandler {
105
106
  }
106
107
  }
107
108
  const prepareStep = options.prepareStep;
109
+ // Hoist system-role messages into generateText's top-level `system` option
110
+ // rather than passing them inside `messages` (deprecated by the AI SDK,
111
+ // rejected in v7). See extractSystemMessages for the rationale. (#1024)
112
+ const { system, messages: nonSystemMessages } = extractSystemMessages(messages);
108
113
  return await generateText({
109
114
  model,
110
- messages,
115
+ ...(system && { system }),
116
+ messages: nonSystemMessages,
111
117
  ...(shouldUseTools &&
112
118
  Object.keys(toolsWithCache).length > 0 && { tools: toolsWithCache }),
113
119
  stopWhen: stepCountIs(options.maxSteps ?? DEFAULT_MAX_STEPS),
package/dist/index.d.ts CHANGED
@@ -35,7 +35,12 @@ import { AIProviderFactory } from "./core/factory.js";
35
35
  export { AIProviderFactory };
36
36
  export { NeuroLinkConfigManager as ConfigManager } from "./config/configManager.js";
37
37
  export { BaseFactory, BaseRegistry, NeuroLinkFeatureError, createErrorFactory, withRetry, TypedEventEmitter, } from "./core/infrastructure/index.js";
38
- export { NeuroLinkClient, createClient, NeuroLinkApiError, NeuroLinkLanguageModel, NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance, createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware, SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream, OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv, ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/index.js";
38
+ export { NeuroLinkClient, createClient, NeuroLinkApiError, } from "./client/httpClient.js";
39
+ export { NeuroLinkLanguageModel, NeuroLinkProvider as NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance, } from "./client/aiSdkAdapter.js";
40
+ export { createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware, } from "./client/interceptors.js";
41
+ export { SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream, } from "./client/streamingClient.js";
42
+ export { OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthenticationError as OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv, } from "./client/auth.js";
43
+ export { ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/errors.js";
39
44
  export { AIProviderName, BedrockModels, OpenAIModels, VertexModels, } from "./constants/enums.js";
40
45
  export { dynamicModelProvider } from "./core/dynamicModels.js";
41
46
  export { validateTool } from "./sdk/toolRegistration.js";
package/dist/index.js CHANGED
@@ -41,20 +41,28 @@ export { BaseFactory, BaseRegistry, NeuroLinkFeatureError, createErrorFactory, w
41
41
  // ============================================================================
42
42
  // CLIENT SDK EXPORTS - Type-safe API access for browser and Node.js
43
43
  // Note: React hooks are NOT re-exported here. Import from '@juspay/neurolink/client'.
44
+ // These re-exports intentionally bypass ./client/index.js: that barrel statically
45
+ // re-exports ./reactHooks.js, so routing through it makes the ROOT import require
46
+ // `react` (an optional peer dep) and crash react-less installs at import time.
44
47
  // ============================================================================
45
48
  export {
46
49
  // HTTP Client
47
- NeuroLinkClient, createClient, NeuroLinkApiError,
50
+ NeuroLinkClient, createClient, NeuroLinkApiError, } from "./client/httpClient.js";
51
+ export {
48
52
  // AI SDK Adapter
49
- NeuroLinkLanguageModel, NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance,
53
+ NeuroLinkLanguageModel, NeuroLinkProvider as NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance, } from "./client/aiSdkAdapter.js";
54
+ export {
50
55
  // Interceptors
51
- createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware,
56
+ createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware, } from "./client/interceptors.js";
57
+ export {
52
58
  // Streaming Client
53
- SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream,
59
+ SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream, } from "./client/streamingClient.js";
60
+ export {
54
61
  // Authentication
55
- OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv,
62
+ OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthenticationError as OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv, } from "./client/auth.js";
63
+ export {
56
64
  // Errors
57
- ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/index.js";
65
+ ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/errors.js";
58
66
  export { AIProviderName, BedrockModels, OpenAIModels, VertexModels, } from "./constants/enums.js";
59
67
  // Dynamic Models exports
60
68
  export { dynamicModelProvider } from "./core/dynamicModels.js";
@@ -26,6 +26,7 @@ import { coerceJsonToSchema } from "../../utils/json/coerce.js";
26
26
  import { NoObjectGeneratedError } from "../../utils/generationErrors.js";
27
27
  import { Output, stepCountIs } from "../../utils/tool.js";
28
28
  import { generateText } from "../../utils/generation.js";
29
+ import { extractSystemMessages } from "../../utils/systemMessages.js";
29
30
  const genTracer = tracers.generation;
30
31
  /**
31
32
  * Safely preview-serialize a value for debug logging.
@@ -105,9 +106,14 @@ export class GenerationHandler {
105
106
  }
106
107
  }
107
108
  const prepareStep = options.prepareStep;
109
+ // Hoist system-role messages into generateText's top-level `system` option
110
+ // rather than passing them inside `messages` (deprecated by the AI SDK,
111
+ // rejected in v7). See extractSystemMessages for the rationale. (#1024)
112
+ const { system, messages: nonSystemMessages } = extractSystemMessages(messages);
108
113
  return await generateText({
109
114
  model,
110
- messages,
115
+ ...(system && { system }),
116
+ messages: nonSystemMessages,
111
117
  ...(shouldUseTools &&
112
118
  Object.keys(toolsWithCache).length > 0 && { tools: toolsWithCache }),
113
119
  stopWhen: stepCountIs(options.maxSteps ?? DEFAULT_MAX_STEPS),
@@ -35,7 +35,12 @@ import { AIProviderFactory } from "./core/factory.js";
35
35
  export { AIProviderFactory };
36
36
  export { NeuroLinkConfigManager as ConfigManager } from "./config/configManager.js";
37
37
  export { BaseFactory, BaseRegistry, NeuroLinkFeatureError, createErrorFactory, withRetry, TypedEventEmitter, } from "./core/infrastructure/index.js";
38
- export { NeuroLinkClient, createClient, NeuroLinkApiError, NeuroLinkLanguageModel, NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance, createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware, SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream, OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv, ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/index.js";
38
+ export { NeuroLinkClient, createClient, NeuroLinkApiError, } from "./client/httpClient.js";
39
+ export { NeuroLinkLanguageModel, NeuroLinkProvider as NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance, } from "./client/aiSdkAdapter.js";
40
+ export { createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware, } from "./client/interceptors.js";
41
+ export { SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream, } from "./client/streamingClient.js";
42
+ export { OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthenticationError as OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv, } from "./client/auth.js";
43
+ export { ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/errors.js";
39
44
  export { AIProviderName, BedrockModels, OpenAIModels, VertexModels, } from "./constants/enums.js";
40
45
  export { dynamicModelProvider } from "./core/dynamicModels.js";
41
46
  export { validateTool } from "./sdk/toolRegistration.js";
package/dist/lib/index.js CHANGED
@@ -41,20 +41,28 @@ export { BaseFactory, BaseRegistry, NeuroLinkFeatureError, createErrorFactory, w
41
41
  // ============================================================================
42
42
  // CLIENT SDK EXPORTS - Type-safe API access for browser and Node.js
43
43
  // Note: React hooks are NOT re-exported here. Import from '@juspay/neurolink/client'.
44
+ // These re-exports intentionally bypass ./client/index.js: that barrel statically
45
+ // re-exports ./reactHooks.js, so routing through it makes the ROOT import require
46
+ // `react` (an optional peer dep) and crash react-less installs at import time.
44
47
  // ============================================================================
45
48
  export {
46
49
  // HTTP Client
47
- NeuroLinkClient, createClient, NeuroLinkApiError,
50
+ NeuroLinkClient, createClient, NeuroLinkApiError, } from "./client/httpClient.js";
51
+ export {
48
52
  // AI SDK Adapter
49
- NeuroLinkLanguageModel, NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance,
53
+ NeuroLinkLanguageModel, NeuroLinkProvider as NeuroLinkAIProvider, createNeuroLinkProvider, createNeuroLinkModel, createStreamingResponse, neurolink as neuroLinkAIInstance, } from "./client/aiSdkAdapter.js";
54
+ export {
50
55
  // Interceptors
51
- createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware,
56
+ createApiKeyAuthInterceptor, createBearerAuthInterceptor, createDynamicAuthInterceptor, createLoggingInterceptor, createRetryInterceptor, createRateLimitInterceptor, createRequestTransformInterceptor, createResponseTransformInterceptor, createCacheInterceptor, createTimeoutInterceptor, createErrorHandlerInterceptor, composeMiddleware, conditionalMiddleware, } from "./client/interceptors.js";
57
+ export {
52
58
  // Streaming Client
53
- SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream,
59
+ SSEClient, WebSocketStreamingClient, createStreamingClient, createAsyncStream, collectStream, } from "./client/streamingClient.js";
60
+ export {
54
61
  // Authentication
55
- OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv,
62
+ OAuth2TokenManager, JWTTokenManager, createApiKeyMiddleware, createBearerTokenMiddleware, createTokenManagerMiddleware, createAuthWithRetryMiddleware, createMultiAuthMiddleware, OAuth2Error, OAuth2AuthenticationError as OAuth2AuthError, TokenRefreshError, decodeJWTPayload, isJWTExpired, getJWTExpiry, getApiKeyFromEnv, } from "./client/auth.js";
63
+ export {
56
64
  // Errors
57
- ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/index.js";
65
+ ErrorCode as ClientErrorCode, NeuroLinkError as ClientNeuroLinkError, HttpError, ClientRateLimitError, ClientValidationError, ClientAuthenticationError, ClientAuthorizationError, NotFoundError, ClientNetworkError, ClientTimeoutError, ClientConnectionError, AbortError, ClientConfigurationError, StreamError, ClientProviderError, ContextLengthError, ContentFilterError, createErrorFromResponse, createErrorFromNative, mapStatusToErrorCode, isRetryableStatus, isRetryableError, isNeuroLinkError, isApiError, } from "./client/errors.js";
58
66
  export { AIProviderName, BedrockModels, OpenAIModels, VertexModels, } from "./constants/enums.js";
59
67
  // Dynamic Models exports
60
68
  export { dynamicModelProvider } from "./core/dynamicModels.js";
@@ -584,6 +584,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
584
584
  let lastStepText = "";
585
585
  let totalInputTokens = 0;
586
586
  let totalOutputTokens = 0;
587
+ let totalCacheReadTokens = 0;
587
588
  let step = 0;
588
589
  let completedWithFinalAnswer = false;
589
590
  const failedTools = new Map();
@@ -614,6 +615,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
614
615
  const chunkResult = await collectStreamChunksIncremental(rawStream, channel);
615
616
  totalInputTokens += chunkResult.inputTokens;
616
617
  totalOutputTokens += chunkResult.outputTokens;
618
+ totalCacheReadTokens += chunkResult.cacheReadTokens ?? 0;
617
619
  const stepText = extractTextFromParts(chunkResult.rawResponseParts);
618
620
  // If no function calls, this was the final step — channel
619
621
  // already received all text parts incrementally.
@@ -698,13 +700,23 @@ export class GoogleAIStudioProvider extends BaseProvider {
698
700
  span.setAttribute(ATTR.GEN_AI_INPUT_TOKENS, totalInputTokens);
699
701
  span.setAttribute(ATTR.GEN_AI_OUTPUT_TOKENS, totalOutputTokens);
700
702
  span.setAttribute(ATTR.GEN_AI_FINISH_REASON, hitStepLimitWithoutFinalAnswer ? "max_steps" : "stop");
703
+ // Gemini promptTokenCount is OVERLAPPING: it already includes
704
+ // cachedContentTokenCount. Subtract once here so calculateCost
705
+ // bills the cached portion at the cheaper cacheRead rate without
706
+ // double-counting. Total billable tokens are conserved.
707
+ const adjustedInputTokens = Math.max(0, totalInputTokens - totalCacheReadTokens);
701
708
  analyticsResolve({
702
709
  provider: this.providerName,
703
710
  model: modelName,
704
711
  tokenUsage: {
705
- input: totalInputTokens,
712
+ input: adjustedInputTokens,
706
713
  output: totalOutputTokens,
707
- total: totalInputTokens + totalOutputTokens,
714
+ total: adjustedInputTokens +
715
+ totalCacheReadTokens +
716
+ totalOutputTokens,
717
+ ...(totalCacheReadTokens > 0
718
+ ? { cacheReadTokens: totalCacheReadTokens }
719
+ : {}),
708
720
  },
709
721
  requestDuration: responseTime,
710
722
  timestamp: new Date().toISOString(),
@@ -831,6 +843,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
831
843
  let lastStepText = "";
832
844
  let totalInputTokens = 0;
833
845
  let totalOutputTokens = 0;
846
+ let totalCacheReadTokens = 0;
834
847
  const allToolCalls = [];
835
848
  const toolExecutions = [];
836
849
  let step = 0;
@@ -856,6 +869,7 @@ export class GoogleAIStudioProvider extends BaseProvider {
856
869
  const chunkResult = await collectStreamChunks(stream);
857
870
  totalInputTokens += chunkResult.inputTokens;
858
871
  totalOutputTokens += chunkResult.outputTokens;
872
+ totalCacheReadTokens += chunkResult.cacheReadTokens ?? 0;
859
873
  const stepText = extractTextFromParts(chunkResult.rawResponseParts);
860
874
  // If no function calls, we're done
861
875
  if (chunkResult.stepFunctionCalls.length === 0) {
@@ -927,14 +941,21 @@ export class GoogleAIStudioProvider extends BaseProvider {
927
941
  // analytics / evaluation / tracing stay attached. The native AI
928
942
  // Studio generate path bypasses BaseProvider.generate(), so
929
943
  // skipping enhanceResult would silently drop those features.
944
+ // Gemini promptTokenCount is OVERLAPPING (already includes
945
+ // cachedContentTokenCount). Subtract once so the cached portion is
946
+ // billed at the cheaper cacheRead rate without double-counting.
947
+ const adjustedInputTokens = Math.max(0, totalInputTokens - totalCacheReadTokens);
930
948
  const baseResult = {
931
949
  content: finalText,
932
950
  provider: this.providerName,
933
951
  model: modelName,
934
952
  usage: {
935
- input: totalInputTokens,
953
+ input: adjustedInputTokens,
936
954
  output: totalOutputTokens,
937
- total: totalInputTokens + totalOutputTokens,
955
+ total: adjustedInputTokens + totalCacheReadTokens + totalOutputTokens,
956
+ ...(totalCacheReadTokens > 0
957
+ ? { cacheReadTokens: totalCacheReadTokens }
958
+ : {}),
938
959
  },
939
960
  responseTime,
940
961
  toolsUsed: allToolCalls.map((tc) => tc.toolName),
@@ -514,6 +514,7 @@ export async function collectStreamChunks(stream) {
514
514
  const stepFunctionCalls = [];
515
515
  let inputTokens = 0;
516
516
  let outputTokens = 0;
517
+ let cacheReadTokens = 0;
517
518
  for await (const chunk of stream) {
518
519
  // Extract raw parts from candidates FIRST
519
520
  // This avoids using chunk.text which triggers SDK warning when
@@ -533,9 +534,18 @@ export async function collectStreamChunks(stream) {
533
534
  if (usage) {
534
535
  inputTokens = Math.max(inputTokens, usage.promptTokenCount || 0);
535
536
  outputTokens = Math.max(outputTokens, usage.candidatesTokenCount || 0);
537
+ // cachedContentTokenCount is OVERLAPPING (a subset already inside
538
+ // promptTokenCount). Surface it so the call site subtracts once.
539
+ cacheReadTokens = Math.max(cacheReadTokens, usage.cachedContentTokenCount || 0);
536
540
  }
537
541
  }
538
- return { rawResponseParts, stepFunctionCalls, inputTokens, outputTokens };
542
+ return {
543
+ rawResponseParts,
544
+ stepFunctionCalls,
545
+ inputTokens,
546
+ outputTokens,
547
+ cacheReadTokens,
548
+ };
539
549
  }
540
550
  /**
541
551
  * Create a push-based text channel that bridges a background producer
@@ -627,6 +637,7 @@ export async function collectStreamChunksIncremental(stream, channel) {
627
637
  const stepFunctionCalls = [];
628
638
  let inputTokens = 0;
629
639
  let outputTokens = 0;
640
+ let cacheReadTokens = 0;
630
641
  for await (const chunk of stream) {
631
642
  const chunkRecord = chunk;
632
643
  const candidates = chunkRecord.candidates;
@@ -648,9 +659,18 @@ export async function collectStreamChunksIncremental(stream, channel) {
648
659
  if (usage) {
649
660
  inputTokens = Math.max(inputTokens, usage.promptTokenCount || 0);
650
661
  outputTokens = Math.max(outputTokens, usage.candidatesTokenCount || 0);
662
+ // cachedContentTokenCount is OVERLAPPING (a subset already inside
663
+ // promptTokenCount). Surface it so the call site subtracts once.
664
+ cacheReadTokens = Math.max(cacheReadTokens, usage.cachedContentTokenCount || 0);
651
665
  }
652
666
  }
653
- return { rawResponseParts, stepFunctionCalls, inputTokens, outputTokens };
667
+ return {
668
+ rawResponseParts,
669
+ stepFunctionCalls,
670
+ inputTokens,
671
+ outputTokens,
672
+ cacheReadTokens,
673
+ };
654
674
  }
655
675
  /**
656
676
  * Extract the thoughtSignature token from raw response parts.