@juspay/neurolink 10.8.13 → 10.8.15

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 (28) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/browser/neurolink.min.js +484 -664
  3. package/dist/cli/factories/sagemakerCommandFactory.js +2 -11
  4. package/dist/features/ppt/slideGenerator.js +10 -18
  5. package/dist/lib/features/ppt/slideGenerator.js +10 -18
  6. package/dist/lib/processors/document/ExcelProcessor.js +11 -19
  7. package/dist/lib/processors/document/WordProcessor.js +3 -11
  8. package/dist/lib/processors/media/AudioProcessor.js +3 -12
  9. package/dist/lib/processors/media/VideoProcessor.d.ts +11 -0
  10. package/dist/lib/processors/media/VideoProcessor.js +33 -24
  11. package/dist/lib/providers/sagemaker/client.js +2 -11
  12. package/dist/lib/server/voice/voiceWebSocketHandler.js +3 -12
  13. package/dist/lib/services/server/ai/observability/instrumentation.js +2 -11
  14. package/dist/lib/tasks/backends/bullmqBackend.js +2 -10
  15. package/dist/lib/utils/tryImport.d.ts +35 -0
  16. package/dist/lib/utils/tryImport.js +95 -0
  17. package/dist/processors/document/ExcelProcessor.js +11 -19
  18. package/dist/processors/document/WordProcessor.js +3 -11
  19. package/dist/processors/media/AudioProcessor.js +3 -12
  20. package/dist/processors/media/VideoProcessor.d.ts +11 -0
  21. package/dist/processors/media/VideoProcessor.js +33 -24
  22. package/dist/providers/sagemaker/client.js +2 -11
  23. package/dist/server/voice/voiceWebSocketHandler.js +3 -12
  24. package/dist/services/server/ai/observability/instrumentation.js +2 -11
  25. package/dist/tasks/backends/bullmqBackend.js +2 -10
  26. package/dist/utils/tryImport.d.ts +35 -0
  27. package/dist/utils/tryImport.js +94 -0
  28. package/package.json +9 -5
@@ -55,39 +55,48 @@ import { SIZE_LIMITS_MB } from "../config/index.js";
55
55
  import { FileErrorCode } from "../errors/index.js";
56
56
  import { tracers, ATTR, withSpan } from "../../telemetry/index.js";
57
57
  import { logger } from "../../utils/logger.js";
58
+ import { tryImport } from "../../utils/tryImport.js";
59
+ /**
60
+ * Narrow a loaded `fluent-ffmpeg` export to the shape this file actually uses:
61
+ * a callable carrying the `ffprobe` and `setFfmpegPath` statics.
62
+ *
63
+ * `tryImport` proves only that the package RESOLVES. Callers invoke the export
64
+ * and reach straight for its statics, so a package whose shape changed (ESM
65
+ * rewrite, major bump, a shim in node_modules) would otherwise surface as
66
+ * "Cannot read properties of undefined (reading 'ffprobe')" from inside
67
+ * probeVideo — blaming the call site instead of the package that is wrong.
68
+ */
69
+ export function assertFluentFfmpegShape(mod) {
70
+ // Not `Partial<typeof import("fluent-ffmpeg")>`: Partial maps over properties
71
+ // and drops the call signature, so `typeof x === "function"` would narrow the
72
+ // result to `never`. Probe the statics structurally instead.
73
+ const statics = mod;
74
+ if (typeof mod !== "function" ||
75
+ typeof statics.ffprobe !== "function" ||
76
+ typeof statics.setFfmpegPath !== "function") {
77
+ throw new Error(`The installed "fluent-ffmpeg" package does not export a callable with ` +
78
+ `ffprobe and setFfmpegPath statics (got ${typeof mod}). ` +
79
+ `Reinstall a compatible version:\n pnpm add fluent-ffmpeg`);
80
+ }
81
+ }
58
82
  // fluent-ffmpeg's default export is callable + has static methods — avoid caching
59
83
  // the module type (it confuses TS); Node's module cache handles dedup.
60
84
  async function loadFluentFfmpeg() {
61
- try {
62
- const mod = await import(/* @vite-ignore */ "fluent-ffmpeg");
63
- return mod.default;
64
- }
65
- catch (err) {
66
- const e = err instanceof Error ? err : null;
67
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
68
- e.message.includes("fluent-ffmpeg")) {
69
- throw new Error('Video processing requires the "fluent-ffmpeg" package. Install it with:\n pnpm add fluent-ffmpeg', { cause: err });
70
- }
71
- throw err;
72
- }
85
+ // fluent-ffmpeg is CJS (`export =`), so `typeof import(...)` describes the
86
+ // callable itself and carries no `default`. Under Node ESM the namespace
87
+ // still wraps it, so ask for that shape explicitly.
88
+ const mod = await tryImport("fluent-ffmpeg", "Video processing");
89
+ const ffmpeg = mod.default;
90
+ assertFluentFfmpegShape(ffmpeg);
91
+ return ffmpeg;
73
92
  }
74
93
  let _mediabunny = null;
75
94
  async function loadMediaBunny() {
76
95
  if (_mediabunny) {
77
96
  return _mediabunny;
78
97
  }
79
- try {
80
- _mediabunny = await import(/* @vite-ignore */ "mediabunny");
81
- return _mediabunny;
82
- }
83
- catch (err) {
84
- const e = err instanceof Error ? err : null;
85
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
86
- e.message.includes("mediabunny")) {
87
- throw new Error('Video processing requires the "mediabunny" package. Install it with:\n pnpm add mediabunny', { cause: err });
88
- }
89
- throw err;
90
- }
98
+ _mediabunny = await tryImport("mediabunny", "Video processing");
99
+ return _mediabunny;
91
100
  }
92
101
  // =============================================================================
93
102
  // FFMPEG PATH INITIALIZATION
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { handleSageMakerError, SageMakerError, isRetryableError, getRetryDelay, } from "./errors.js";
8
8
  import { logger } from "../../utils/logger.js";
9
+ import { tryImport } from "../../utils/tryImport.js";
9
10
  /**
10
11
  * Lazily load `@aws-sdk/client-sagemaker-runtime`.
11
12
  *
@@ -16,17 +17,7 @@ import { logger } from "../../utils/logger.js";
16
17
  * error instead of a raw resolution failure.
17
18
  */
18
19
  async function loadSageMakerRuntime() {
19
- try {
20
- return await import(/* @vite-ignore */ "@aws-sdk/client-sagemaker-runtime");
21
- }
22
- catch (err) {
23
- const e = err instanceof Error ? err : null;
24
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
25
- e.message.includes("client-sagemaker-runtime")) {
26
- throw new Error('SageMaker inference requires "@aws-sdk/client-sagemaker-runtime". Install it with:\n pnpm add @aws-sdk/client-sagemaker-runtime', { cause: err });
27
- }
28
- throw err;
29
- }
20
+ return tryImport("@aws-sdk/client-sagemaker-runtime", "SageMaker inference");
30
21
  }
31
22
  /**
32
23
  * Enhanced SageMaker Runtime client with retry logic and error handling
@@ -5,20 +5,11 @@ import { timingSafeEqualString } from "./tokenCompare.js";
5
5
  import { CartesiaStream } from "../../adapters/tts/cartesiaHandler.js";
6
6
  import { NeuroLink } from "../../neurolink.js";
7
7
  import { logger } from "../../utils/logger.js";
8
+ import { tryImport } from "../../utils/tryImport.js";
8
9
  import { withTimeout } from "../../utils/async/withTimeout.js";
9
10
  async function loadCobra(accessKey) {
10
- try {
11
- const mod = (await import(/* @vite-ignore */ "@picovoice/cobra-node"));
12
- return new mod.Cobra(accessKey);
13
- }
14
- catch (err) {
15
- const e = err instanceof Error ? err : null;
16
- if (e?.code === "ERR_MODULE_NOT_FOUND" &&
17
- e.message.includes("cobra-node")) {
18
- throw new Error('Voice activity detection requires "@picovoice/cobra-node". Install it with:\n pnpm add @picovoice/cobra-node', { cause: err });
19
- }
20
- throw err;
21
- }
11
+ const mod = await tryImport("@picovoice/cobra-node", "Voice activity detection");
12
+ return new mod.Cobra(accessKey);
22
13
  }
23
14
  const SONIOX_URL = process.env.SONIOX_WS_URL ?? "wss://stt-rt.soniox.com/transcribe-websocket";
24
15
  function getRequiredEnv(name) {
@@ -20,6 +20,7 @@ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semanti
20
20
  import { AsyncLocalStorage } from "async_hooks";
21
21
  import { extractMcpErrorText } from "../../../../utils/mcpErrorText.js";
22
22
  import { logger } from "../../../../utils/logger.js";
23
+ import { tryImport } from "../../../../utils/tryImport.js";
23
24
  import { LANGFUSE_ATTR } from "../../../../telemetry/attributes.js";
24
25
  const LOG_PREFIX = "[OpenTelemetry]";
25
26
  function createOtelResource(config, serviceName) {
@@ -568,17 +569,7 @@ class ContextEnricher {
568
569
  }
569
570
  }
570
571
  async function createLangfuseProcessor(config) {
571
- let mod;
572
- try {
573
- mod = await import(/* @vite-ignore */ "@langfuse/otel");
574
- }
575
- catch (err) {
576
- const e = err instanceof Error ? err : null;
577
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("langfuse")) {
578
- throw new Error('Langfuse observability requires "@langfuse/otel". Install it with:\n pnpm add @langfuse/otel', { cause: err });
579
- }
580
- throw err;
581
- }
572
+ const mod = await tryImport("@langfuse/otel", "Langfuse observability");
582
573
  return new mod.LangfuseSpanProcessor({
583
574
  publicKey: config.publicKey,
584
575
  secretKey: config.secretKey,
@@ -7,19 +7,11 @@
7
7
  * - Survives process restarts (Redis-persisted)
8
8
  */
9
9
  import { logger } from "../../utils/logger.js";
10
+ import { tryImport } from "../../utils/tryImport.js";
10
11
  import { TaskError } from "../errors.js";
11
12
  import { TASK_DEFAULTS, } from "../../types/index.js";
12
13
  async function loadBullMQ() {
13
- try {
14
- return await import(/* @vite-ignore */ "bullmq");
15
- }
16
- catch (err) {
17
- const e = err instanceof Error ? err : null;
18
- if (e?.code === "ERR_MODULE_NOT_FOUND" && e.message.includes("bullmq")) {
19
- throw new Error('BullMQ task backend requires the "bullmq" package. Install it with:\n pnpm add bullmq', { cause: err });
20
- }
21
- throw err;
22
- }
14
+ return tryImport("bullmq", "BullMQ task backend");
23
15
  }
24
16
  const QUEUE_NAME = "neurolink-tasks";
25
17
  export class BullMQBackend {
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Dynamically import an optional dependency, converting a missing package into
3
+ * an actionable error.
4
+ *
5
+ * NeuroLink declares 42 `optionalDependencies`, and each loader had grown its
6
+ * own copy of the same try/catch: check `err.code`, substring-match the package
7
+ * name, rethrow a friendlier Error. The copies had drifted — some said
8
+ * `pnpm add`, one said `npm install`, and the phrasing varied per call site.
9
+ *
10
+ * Only missing-module failures are intercepted. A package that is installed but
11
+ * throws while evaluating (syntax error, ESM/CJS interop, a broken transitive
12
+ * dep) rethrows unchanged — turning those into "please install X" would send
13
+ * the caller after a package they already have. The same applies to specifiers
14
+ * that are not installable packages at all (relative paths, file/data URLs):
15
+ * they rethrow untouched rather than producing an unrunnable install command.
16
+ *
17
+ * @param pkg - Module specifier. The install hint is only produced for bare
18
+ * package names; anything else rethrows the loader's own error.
19
+ * @param feature - Human-readable capability name, used to open the message.
20
+ * @returns The imported module namespace, typed as `T` (`unknown` if the
21
+ * caller does not supply a type argument, since the shape cannot be known
22
+ * for a dynamic specifier).
23
+ * @throws The loader's original error, or an `Error` naming the package and
24
+ * the `pnpm add` command when a bare package is genuinely absent. The
25
+ * original failure is always preserved on `cause`.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const ExcelJS = await tryImport<typeof import("exceljs")>(
30
+ * "exceljs",
31
+ * "Excel file processing",
32
+ * );
33
+ * ```
34
+ */
35
+ export declare function tryImport<T = unknown>(pkg: string, feature: string): Promise<T>;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Dynamically import an optional dependency, converting a missing package into
3
+ * an actionable error.
4
+ *
5
+ * NeuroLink declares 42 `optionalDependencies`, and each loader had grown its
6
+ * own copy of the same try/catch: check `err.code`, substring-match the package
7
+ * name, rethrow a friendlier Error. The copies had drifted — some said
8
+ * `pnpm add`, one said `npm install`, and the phrasing varied per call site.
9
+ *
10
+ * Only missing-module failures are intercepted. A package that is installed but
11
+ * throws while evaluating (syntax error, ESM/CJS interop, a broken transitive
12
+ * dep) rethrows unchanged — turning those into "please install X" would send
13
+ * the caller after a package they already have. The same applies to specifiers
14
+ * that are not installable packages at all (relative paths, file/data URLs):
15
+ * they rethrow untouched rather than producing an unrunnable install command.
16
+ *
17
+ * @param pkg - Module specifier. The install hint is only produced for bare
18
+ * package names; anything else rethrows the loader's own error.
19
+ * @param feature - Human-readable capability name, used to open the message.
20
+ * @returns The imported module namespace, typed as `T` (`unknown` if the
21
+ * caller does not supply a type argument, since the shape cannot be known
22
+ * for a dynamic specifier).
23
+ * @throws The loader's original error, or an `Error` naming the package and
24
+ * the `pnpm add` command when a bare package is genuinely absent. The
25
+ * original failure is always preserved on `cause`.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const ExcelJS = await tryImport<typeof import("exceljs")>(
30
+ * "exceljs",
31
+ * "Excel file processing",
32
+ * );
33
+ * ```
34
+ */
35
+ export async function tryImport(pkg, feature) {
36
+ try {
37
+ return (await import(/* @vite-ignore */ pkg));
38
+ }
39
+ catch (err) {
40
+ if (isMissingModule(err, pkg)) {
41
+ throw new Error(`${feature} requires the "${pkg}" package. Install it with:\n pnpm add ${pkg}`, { cause: err });
42
+ }
43
+ throw err;
44
+ }
45
+ }
46
+ /**
47
+ * True only when `err` is the module loader failing to resolve `pkg` itself.
48
+ *
49
+ * The package name is matched inside quotes because that is how both loaders
50
+ * format it — ESM `Cannot find package 'x' imported from …`, CJS
51
+ * `Cannot find module 'x'`. Matching the bare substring instead (as the
52
+ * hand-rolled copies did) also matches a *transitive* dependency whose path
53
+ * happens to contain the name, which would blame the wrong package.
54
+ *
55
+ * The specifier must also be a bare package name. Without that check a missing
56
+ * relative path or file URL is rewritten into `pnpm add ./fixtures/thing.mjs`,
57
+ * which is not a command anyone can run — and this helper is already called
58
+ * with `file:` and `data:` URLs, so that path is reachable rather than
59
+ * theoretical.
60
+ */
61
+ function isMissingModule(err, pkg) {
62
+ if (!(err instanceof Error)) {
63
+ return false;
64
+ }
65
+ if (!isBarePackageSpecifier(pkg)) {
66
+ return false;
67
+ }
68
+ const code = err.code;
69
+ if (code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") {
70
+ return false;
71
+ }
72
+ return err.message.includes(`'${pkg}'`) || err.message.includes(`"${pkg}"`);
73
+ }
74
+ /**
75
+ * True for specifiers that name an installable package — `exceljs`,
76
+ * `@scope/pkg`, `pkg/sub/path`.
77
+ *
78
+ * False for the two things an install hint cannot help with: filesystem paths
79
+ * (`./x`, `../x`, `/x`, and Windows `C:\x`, which the scheme test below also
80
+ * rejects) and any URL (`file:`, `data:`, `http:`), including the `node:`
81
+ * builtins, which are never installed.
82
+ */
83
+ function isBarePackageSpecifier(specifier) {
84
+ if (specifier.length === 0) {
85
+ return false;
86
+ }
87
+ if (specifier.startsWith("/") ||
88
+ specifier.startsWith("./") ||
89
+ specifier.startsWith("../")) {
90
+ return false;
91
+ }
92
+ // RFC 3986 scheme: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ":"
93
+ return !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(specifier);
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "10.8.13",
3
+ "version": "10.8.15",
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": {
@@ -144,8 +144,8 @@
144
144
  "test:tool-dedup": "pnpm run test:tool-dedup:vitest && npx tsx test/continuous-test-suite-tool-dedup.ts",
145
145
  "test:model-pool:vitest": "pnpm exec vitest run test/modelPool.test.ts",
146
146
  "test:model-pool": "pnpm run test:model-pool:vitest && npx tsx test/continuous-test-suite-model-pool.ts",
147
- "test:model-capabilities:vitest": "pnpm exec vitest run test/modelCapabilities.test.ts",
148
- "test:agent-runtime:vitest": "pnpm exec vitest run test/agentRuntime.test.ts test/agentDelegation.test.ts test/agentPlumbing.test.ts test/toolExecutionRecorder.test.ts test/samplingParams.test.ts test/structuredRecovery.test.ts",
147
+ "test:model-capabilities": "npx tsx test/continuous-test-suite-model-capabilities.ts",
148
+ "test:agent-runtime:vitest": "pnpm exec vitest run test/agentRuntime.test.ts test/agentDelegation.test.ts test/agentPlumbing.test.ts test/toolExecutionRecorder.test.ts",
149
149
  "test:retry-after:vitest": "pnpm exec vitest run test/retryAfter.test.ts",
150
150
  "test:ci": "pnpm run test && pnpm run test:client && pnpm run test:hitl",
151
151
  "// CI tier — fast, no live AI calls, safe for every commit": "",
@@ -155,7 +155,7 @@
155
155
  "test:system-messages:vitest": "pnpm exec vitest run test/systemMessages.test.ts",
156
156
  "test:tool-routing-semantic:vitest": "pnpm exec vitest run test/toolRoutingSemantic.test.ts",
157
157
  "test:tool-routing-semantic": "pnpm run test:tool-routing-semantic:vitest && npx tsx test/continuous-test-suite-tool-routing-semantic.ts",
158
- "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest && pnpm run test:model-pool:vitest && pnpm run test:litellm-context:vitest && pnpm run test:step-budget-guard:vitest && pnpm run test:system-messages:vitest && pnpm run test:tool-routing-semantic:vitest && pnpm run test:anthropic-tools-policy && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:excel-interop && pnpm run test:model-capabilities:vitest && pnpm run test:agent-runtime:vitest && pnpm run test:retry-after:vitest",
158
+ "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest && pnpm run test:model-pool:vitest && pnpm run test:litellm-context:vitest && pnpm run test:step-budget-guard:vitest && pnpm run test:system-messages:vitest && pnpm run test:tool-routing-semantic:vitest && pnpm run test:anthropic-tools-policy && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:excel-interop && pnpm run test:model-capabilities && pnpm run test:agent-runtime:vitest && pnpm run test:retry-after:vitest && pnpm run test:sampling-params && pnpm run test:structured-recovery && pnpm run test:prompt-redaction && pnpm run test:mcp-result-cache",
159
159
  "// CI tier — live providers, runs only when API keys are present (test:credentials and test:dynamic make real provider calls when keys are set, so they live here, not in test:unit)": "",
160
160
  "test:live": "pnpm run test:providers && pnpm run test:mcp:http && pnpm run test:mcp:sdk && pnpm run test:mcp:cli && pnpm run test:observability && pnpm run test:context && pnpm run test:memory && pnpm run test:tool-reliability && pnpm run test:evaluation && pnpm run test:autoresearch && pnpm run test:credentials && pnpm run test:dynamic",
161
161
  "// CI tier — product output (image/video/TTS/PPT) — costs $$ per run": "",
@@ -224,7 +224,11 @@
224
224
  "test:tts:unit": "npx tsx test/continuous-test-suite-tts-unit.ts",
225
225
  "test:video": "npx tsx test/continuous-test-suite-video.ts",
226
226
  "test:multimodal": "pnpm run test:audio && pnpm run test:video && pnpm run test:office && pnpm run test:tts:unit && pnpm run test:multimodal:sdk",
227
- "test:multimodal:sdk": "npx tsx test/continuous-test-suite-multimodal-sdk.ts"
227
+ "test:multimodal:sdk": "npx tsx test/continuous-test-suite-multimodal-sdk.ts",
228
+ "test:sampling-params": "npx tsx test/continuous-test-suite-sampling-params.ts",
229
+ "test:structured-recovery": "npx tsx test/continuous-test-suite-structured-recovery.ts",
230
+ "test:prompt-redaction": "npx tsx test/continuous-test-suite-prompt-redaction.ts",
231
+ "test:mcp-result-cache": "npx tsx test/continuous-test-suite-mcp-result-cache.ts"
228
232
  },
229
233
  "files": [
230
234
  "dist",