@juspay/neurolink 9.63.0 → 9.64.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 +12 -0
- package/dist/adapters/video/vertexVideoHandler.js +9 -2
- package/dist/browser/neurolink.min.js +1015 -1019
- package/dist/cli/factories/commandFactory.d.ts +14 -0
- package/dist/cli/factories/commandFactory.js +50 -25
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/loop/optionsSchema.js +12 -0
- package/dist/core/baseProvider.d.ts +1 -1
- package/dist/core/modules/MessageBuilder.js +20 -0
- package/dist/core/redisConversationMemoryManager.js +0 -3
- package/dist/factories/providerRegistry.js +5 -1
- package/dist/lib/adapters/video/vertexVideoHandler.js +9 -2
- package/dist/lib/core/baseProvider.d.ts +1 -1
- package/dist/lib/core/modules/MessageBuilder.js +20 -0
- package/dist/lib/core/redisConversationMemoryManager.js +0 -3
- package/dist/lib/factories/providerRegistry.js +5 -1
- package/dist/lib/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/lib/memory/hippocampusInitializer.js +32 -2
- package/dist/lib/middleware/builtin/lifecycle.js +19 -48
- package/dist/lib/neurolink.js +49 -2
- package/dist/lib/providers/googleAiStudio.d.ts +11 -3
- package/dist/lib/providers/googleAiStudio.js +292 -339
- package/dist/lib/providers/googleNativeGemini3.d.ts +83 -1
- package/dist/lib/providers/googleNativeGemini3.js +208 -4
- package/dist/lib/providers/googleVertex.d.ts +116 -129
- package/dist/lib/providers/googleVertex.js +2826 -1968
- package/dist/lib/providers/openRouter.js +7 -3
- package/dist/lib/types/aliases.d.ts +14 -0
- package/dist/lib/types/common.d.ts +0 -3
- package/dist/lib/types/conversation.d.ts +10 -3
- package/dist/lib/types/generate.d.ts +14 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.js +1 -0
- package/dist/lib/types/memory.d.ts +96 -0
- package/dist/lib/types/memory.js +23 -0
- package/dist/lib/types/providers.d.ts +140 -2
- package/dist/lib/types/stream.d.ts +6 -0
- package/dist/lib/utils/lifecycleCallbacks.d.ts +13 -0
- package/dist/lib/utils/lifecycleCallbacks.js +44 -0
- package/dist/lib/utils/messageBuilder.d.ts +10 -0
- package/dist/lib/utils/messageBuilder.js +40 -5
- package/dist/lib/utils/modelDetection.d.ts +11 -0
- package/dist/lib/utils/modelDetection.js +27 -0
- package/dist/lib/utils/providerHealth.js +7 -7
- package/dist/lib/utils/schemaConversion.d.ts +1 -1
- package/dist/lib/utils/schemaConversion.js +59 -4
- package/dist/lib/utils/tokenLimits.js +23 -32
- package/dist/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/memory/hippocampusInitializer.js +32 -2
- package/dist/middleware/builtin/lifecycle.js +19 -48
- package/dist/neurolink.js +49 -2
- package/dist/providers/googleAiStudio.d.ts +11 -3
- package/dist/providers/googleAiStudio.js +291 -339
- package/dist/providers/googleNativeGemini3.d.ts +83 -1
- package/dist/providers/googleNativeGemini3.js +208 -4
- package/dist/providers/googleVertex.d.ts +116 -129
- package/dist/providers/googleVertex.js +2824 -1967
- package/dist/providers/openRouter.js +7 -3
- package/dist/types/aliases.d.ts +14 -0
- package/dist/types/common.d.ts +0 -3
- package/dist/types/conversation.d.ts +10 -3
- package/dist/types/generate.d.ts +14 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/memory.d.ts +96 -0
- package/dist/types/memory.js +22 -0
- package/dist/types/providers.d.ts +140 -2
- package/dist/types/stream.d.ts +6 -0
- package/dist/utils/lifecycleCallbacks.d.ts +13 -0
- package/dist/utils/lifecycleCallbacks.js +43 -0
- package/dist/utils/messageBuilder.d.ts +10 -0
- package/dist/utils/messageBuilder.js +40 -5
- package/dist/utils/modelDetection.d.ts +11 -0
- package/dist/utils/modelDetection.js +27 -0
- package/dist/utils/providerHealth.js +7 -7
- package/dist/utils/schemaConversion.d.ts +1 -1
- package/dist/utils/schemaConversion.js +59 -4
- package/dist/utils/tokenLimits.js +23 -32
- package/package.json +11 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model detection utilities for capability checking
|
|
3
3
|
*/
|
|
4
|
+
import { IMAGE_GENERATION_MODELS } from "../core/constants.js";
|
|
4
5
|
/**
|
|
5
6
|
* Check if model name is valid for detection functions
|
|
6
7
|
*/
|
|
@@ -78,4 +79,30 @@ export function getModelFamily(modelName) {
|
|
|
78
79
|
}
|
|
79
80
|
return "unknown";
|
|
80
81
|
}
|
|
82
|
+
// Compiled once at module load — hasRestrictedOutputLimit() is called per
|
|
83
|
+
// request and previously rebuilt this array on every call.
|
|
84
|
+
const IMAGE_MODEL_PATTERNS = IMAGE_GENERATION_MODELS.map((m) => new RegExp(`^${m.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i"));
|
|
85
|
+
/**
|
|
86
|
+
* Check if a model has restricted output token limit (32768 max)
|
|
87
|
+
* This applies to:
|
|
88
|
+
* - All Gemini 3 models (gemini-3-flash, gemini-3-pro, etc.)
|
|
89
|
+
* - Image generation models (gemini-2.5-flash-image, gemini-3-pro-image-preview)
|
|
90
|
+
*/
|
|
91
|
+
export function hasRestrictedOutputLimit(modelName) {
|
|
92
|
+
if (!isValidModelName(modelName)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
// Check for Gemini 3 models (anchored regex for consistency)
|
|
96
|
+
if (/^gemini-3/i.test(modelName)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (IMAGE_MODEL_PATTERNS.some((pattern) => pattern.test(modelName))) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get the max output tokens for a model (32768 for restricted models)
|
|
106
|
+
*/
|
|
107
|
+
export const RESTRICTED_OUTPUT_TOKEN_LIMIT = 32768;
|
|
81
108
|
//# sourceMappingURL=modelDetection.js.map
|
|
@@ -349,7 +349,7 @@ export class ProviderHealthChecker {
|
|
|
349
349
|
` • claude-sonnet-4@20250514, claude-opus-4@20250514\n` +
|
|
350
350
|
` • claude-3-5-sonnet-20241022, claude-3-5-haiku-20241022\n` +
|
|
351
351
|
` • claude-3-sonnet-20240229, claude-3-haiku-20240307, claude-3-opus-20240229\n` +
|
|
352
|
-
` Implementation: Uses @ai-sdk
|
|
352
|
+
` Implementation: Uses native SDKs (@google/genai for Gemini, @anthropic-ai/vertex-sdk for Claude)\n` +
|
|
353
353
|
` Authentication: Requires Google Cloud project with Vertex AI API enabled\n` +
|
|
354
354
|
` Note: Anthropic models require Anthropic integration in your Google Cloud project`);
|
|
355
355
|
}
|
|
@@ -969,13 +969,13 @@ export class ProviderHealthChecker {
|
|
|
969
969
|
logger.debug("Starting comprehensive Vertex Anthropic support verification");
|
|
970
970
|
try {
|
|
971
971
|
// 1. Check SDK module availability
|
|
972
|
-
logger.debug("Checking @ai-sdk
|
|
973
|
-
const anthropicModule = await import("@ai-sdk
|
|
972
|
+
logger.debug("Checking @anthropic-ai/vertex-sdk module availability");
|
|
973
|
+
const anthropicModule = await import("@anthropic-ai/vertex-sdk");
|
|
974
974
|
result.hasCreateVertexAnthropic =
|
|
975
|
-
typeof anthropicModule.
|
|
976
|
-
result.hasCorrectTypes = true; // Types are bundled with the
|
|
975
|
+
typeof anthropicModule.AnthropicVertex === "function";
|
|
976
|
+
result.hasCorrectTypes = true; // Types are bundled with the class
|
|
977
977
|
if (!result.hasCreateVertexAnthropic) {
|
|
978
|
-
result.troubleshooting.push("📦
|
|
978
|
+
result.troubleshooting.push("📦 Install @anthropic-ai/vertex-sdk for Claude on Vertex AI support", "🔄 Run: npm install @anthropic-ai/vertex-sdk", "📖 See: https://docs.anthropic.com/en/api/claude-on-vertex-ai");
|
|
979
979
|
return result;
|
|
980
980
|
}
|
|
981
981
|
logger.debug("SDK module verified successfully");
|
|
@@ -1065,7 +1065,7 @@ export class ProviderHealthChecker {
|
|
|
1065
1065
|
error: error instanceof Error ? error.message : String(error),
|
|
1066
1066
|
stack: error instanceof Error ? error.stack : undefined,
|
|
1067
1067
|
});
|
|
1068
|
-
result.recommendations.push("❌ Comprehensive Anthropic support check failed", `🐛 Error: ${error instanceof Error ? error.message : String(error)}`, "", "🔧 Troubleshooting steps:", "1.
|
|
1068
|
+
result.recommendations.push("❌ Comprehensive Anthropic support check failed", `🐛 Error: ${error instanceof Error ? error.message : String(error)}`, "", "🔧 Troubleshooting steps:", "1. Verify @google-cloud/vertexai and @anthropic-ai/vertex-sdk are properly installed", "2. Verify Google Cloud authentication setup", "3. Check project ID and region configuration", "4. Enable Vertex AI API in Google Cloud Console", "5. Enable Anthropic integration in Vertex AI Model Garden");
|
|
1069
1069
|
}
|
|
1070
1070
|
return result;
|
|
1071
1071
|
}
|
|
@@ -27,7 +27,7 @@ export declare function ensureNestedSchemaTypes(schema: Record<string, unknown>)
|
|
|
27
27
|
* 2. AI SDK `jsonSchema()` wrappers (have `.jsonSchema` property) -- extracted directly
|
|
28
28
|
* 3. Plain JSON Schema objects (have `type`/`properties` but no `_def`) -- returned as-is
|
|
29
29
|
*/
|
|
30
|
-
export declare function convertZodToJsonSchema(zodSchema: ZodUnknownSchema): object;
|
|
30
|
+
export declare function convertZodToJsonSchema(zodSchema: ZodUnknownSchema, target?: "jsonSchema7" | "openApi3"): object;
|
|
31
31
|
export declare function normalizeJsonSchemaObject(schema: Record<string, unknown> | undefined | null): Record<string, unknown>;
|
|
32
32
|
/**
|
|
33
33
|
* Check if a value is a Zod schema
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
2
2
|
import { jsonSchemaToZod } from "json-schema-to-zod";
|
|
3
|
+
import * as zodModule from "zod";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { logger } from "./logger.js";
|
|
6
|
+
// Zod 4 ships a built-in `z.toJSONSchema(...)`. Zod 3 does not — it returned
|
|
7
|
+
// nothing of the sort and we relied entirely on `zod-to-json-schema`. The
|
|
8
|
+
// `zod-to-json-schema` package only understands Zod 3's internal `_def`
|
|
9
|
+
// shape, so feeding it a Zod 4 schema yields an empty `{}` (it silently
|
|
10
|
+
// produces `definitions: { ToolParameters: {} }`). When this happens
|
|
11
|
+
// downstream callers send an empty `responseSchema` to the model and get
|
|
12
|
+
// back arbitrary JSON, which is exactly how the Vertex Structured-Output
|
|
13
|
+
// regressions surfaced. Detect the Zod 4 helper at module load and prefer
|
|
14
|
+
// it for actual Zod schemas.
|
|
15
|
+
// Zod 4 spells the OpenAPI target as "openapi-3.0" (with a dot) while the
|
|
16
|
+
// third-party zod-to-json-schema package uses "openApi3". Internally we use
|
|
17
|
+
// the latter for backwards compatibility with existing call sites; this map
|
|
18
|
+
// translates to the dialect Zod 4 actually accepts. The Zod4Native* types
|
|
19
|
+
// live in src/lib/types/aliases.ts per project rule 2.
|
|
20
|
+
const zodToJsonSchemaV4 = typeof zodModule.toJSONSchema === "function"
|
|
21
|
+
? zodModule.toJSONSchema
|
|
22
|
+
: undefined;
|
|
5
23
|
/**
|
|
6
24
|
* Resolve a deep JSON pointer path within a schema.
|
|
7
25
|
* Handles paths like "#/definitions/ToolParameters/properties/foo/properties/bar"
|
|
@@ -96,7 +114,12 @@ export function inlineJsonSchema(schema, definitions, visited = new Set(), rootS
|
|
|
96
114
|
visited.delete(refPath);
|
|
97
115
|
return inlined;
|
|
98
116
|
}
|
|
99
|
-
|
|
117
|
+
// Unresolved $ref: warn and preserve the original node verbatim. Falling
|
|
118
|
+
// through to the copy loop below would strip the $ref key and silently
|
|
119
|
+
// turn a ref-only node into an empty {}, which broadens validation
|
|
120
|
+
// instead of failing closed.
|
|
121
|
+
logger.warn(`[SCHEMA-INLINE] Could not resolve $ref: ${refPath}`);
|
|
122
|
+
return { ...schema };
|
|
100
123
|
}
|
|
101
124
|
// Create result without $ref and definitions
|
|
102
125
|
const result = {};
|
|
@@ -279,7 +302,12 @@ export function ensureNestedSchemaTypes(schema) {
|
|
|
279
302
|
* 2. AI SDK `jsonSchema()` wrappers (have `.jsonSchema` property) -- extracted directly
|
|
280
303
|
* 3. Plain JSON Schema objects (have `type`/`properties` but no `_def`) -- returned as-is
|
|
281
304
|
*/
|
|
282
|
-
export function convertZodToJsonSchema(zodSchema
|
|
305
|
+
export function convertZodToJsonSchema(zodSchema,
|
|
306
|
+
// Default to JSON Schema draft-07 so non-Vertex consumers (Bedrock, MCP
|
|
307
|
+
// tool registration, etc.) keep their pre-migration dialect. Vertex/Gemini
|
|
308
|
+
// callers opt into "openApi3" explicitly to get `nullable: true` instead
|
|
309
|
+
// of `anyOf: [..., {type: "null"}]`.
|
|
310
|
+
target = "jsonSchema7") {
|
|
283
311
|
const schema = zodSchema;
|
|
284
312
|
if (!schema || typeof schema !== "object") {
|
|
285
313
|
return { type: "object", properties: {} };
|
|
@@ -295,14 +323,41 @@ export function convertZodToJsonSchema(zodSchema) {
|
|
|
295
323
|
if (!isZodSchema(schema)) {
|
|
296
324
|
return ensureNestedSchemaTypes(ensureTypeField(schema));
|
|
297
325
|
}
|
|
298
|
-
// Actual Zod schema —
|
|
326
|
+
// Actual Zod schema — prefer Zod 4's native `z.toJSONSchema` when
|
|
327
|
+
// available (the runtime version of `zod` here is Zod 4), then fall
|
|
328
|
+
// back to `zod-to-json-schema` for Zod 3 schemas that external callers
|
|
329
|
+
// might still pass in.
|
|
330
|
+
//
|
|
331
|
+
// Translate our `target` to Zod 4's native dialect identifier so the
|
|
332
|
+
// openApi3 path emits the OpenAPI 3 schema shape Vertex/Gemini expect
|
|
333
|
+
// (and not the default draft-07 anyOf/null union).
|
|
334
|
+
if (zodToJsonSchemaV4) {
|
|
335
|
+
const nativeTarget = target === "openApi3" ? "openapi-3.0" : "draft-07";
|
|
336
|
+
try {
|
|
337
|
+
const native = zodToJsonSchemaV4(zodSchema, {
|
|
338
|
+
target: nativeTarget,
|
|
339
|
+
});
|
|
340
|
+
// Drop the $schema metadata Vertex/Gemini doesn't need, then walk to
|
|
341
|
+
// backfill any missing nested types (Zod 4's output is already flat
|
|
342
|
+
// — no $defs/$ref by default — but the helper is cheap and matches
|
|
343
|
+
// the Zod 3 path's contract).
|
|
344
|
+
const flat = { ...native };
|
|
345
|
+
delete flat.$schema;
|
|
346
|
+
const inlined = inlineJsonSchema(flat);
|
|
347
|
+
return ensureNestedSchemaTypes(ensureTypeField(inlined));
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
logger.warn("Native z.toJSONSchema failed; falling back to zod-to-json-schema", { error: error instanceof Error ? error.message : String(error) });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// Zod 3 fallback path
|
|
299
354
|
try {
|
|
300
355
|
// Zod 4→3 boundary: zodToJsonSchema types reference Zod 3's ZodSchema via zod/v3.
|
|
301
356
|
// Runtime compatible — cast through unknown at this third-party boundary only.
|
|
302
357
|
const zodV3Schema = zodSchema;
|
|
303
358
|
const jsonSchema = zodToJsonSchema(zodV3Schema, {
|
|
304
359
|
name: "ToolParameters",
|
|
305
|
-
target
|
|
360
|
+
target,
|
|
306
361
|
errorMessages: true,
|
|
307
362
|
});
|
|
308
363
|
// zodToJsonSchema with 'name' produces { $ref: "#/definitions/ToolParameters", definitions: {...} }
|
|
@@ -2,30 +2,14 @@
|
|
|
2
2
|
* Provider-specific token limit utilities
|
|
3
3
|
* Provides safe maxTokens values based on provider and model capabilities
|
|
4
4
|
*/
|
|
5
|
-
import { PROVIDER_MAX_TOKENS
|
|
5
|
+
import { PROVIDER_MAX_TOKENS } from "../core/constants.js";
|
|
6
6
|
import { logger } from "./logger.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* - All Gemini 2.5 image generation models (gemini-2.5-flash-image)
|
|
14
|
-
*/
|
|
15
|
-
function hasRestrictedOutputLimit(model) {
|
|
16
|
-
if (!model) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
// Check for Gemini 3 models
|
|
20
|
-
if (model.includes("gemini-3")) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
// Check for image generation models (includes gemini-2.5-flash-image)
|
|
24
|
-
if (IMAGE_GENERATION_MODELS.some((m) => model.includes(m))) {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
7
|
+
import { hasRestrictedOutputLimit, RESTRICTED_OUTPUT_TOKEN_LIMIT, } from "./modelDetection.js";
|
|
8
|
+
// Restricted-model detection (Gemini 3.x + image-gen models capped at 32768
|
|
9
|
+
// output tokens) lives in modelDetection.ts. Importing the canonical helper
|
|
10
|
+
// keeps both call sites on one definition; the previous local copy used
|
|
11
|
+
// case-sensitive substring matches and could miss identifiers like
|
|
12
|
+
// "Gemini-3-Pro" which the canonical anchored, case-insensitive regex matches.
|
|
29
13
|
/**
|
|
30
14
|
* Get the safe maximum tokens for a provider and model
|
|
31
15
|
*/
|
|
@@ -33,19 +17,19 @@ export function getSafeMaxTokens(provider, model, requestedMaxTokens) {
|
|
|
33
17
|
// CRITICAL: Gemini 3 models AND image generation models have a hard limit of 32768 output tokens
|
|
34
18
|
// This check must happen FIRST, before any other logic, because these models
|
|
35
19
|
// will reject requests with maxOutputTokens > 32768
|
|
36
|
-
const isRestrictedModel = hasRestrictedOutputLimit(model);
|
|
20
|
+
const isRestrictedModel = model ? hasRestrictedOutputLimit(model) : false;
|
|
37
21
|
if (isRestrictedModel) {
|
|
38
22
|
// Explicit undefined/null check so a caller-supplied 0 is preserved
|
|
39
|
-
// (truthy
|
|
23
|
+
// (truthy guards would treat 0 as "unset" and silently fall back to the cap).
|
|
40
24
|
if (requestedMaxTokens !== undefined &&
|
|
41
25
|
requestedMaxTokens !== null &&
|
|
42
|
-
requestedMaxTokens >
|
|
43
|
-
logger.warn(`Requested maxTokens ${requestedMaxTokens} exceeds ${model} limit of ${
|
|
44
|
-
return
|
|
26
|
+
requestedMaxTokens > RESTRICTED_OUTPUT_TOKEN_LIMIT) {
|
|
27
|
+
logger.warn(`Requested maxTokens ${requestedMaxTokens} exceeds ${model} limit of ${RESTRICTED_OUTPUT_TOKEN_LIMIT}. Using ${RESTRICTED_OUTPUT_TOKEN_LIMIT} instead.`);
|
|
28
|
+
return RESTRICTED_OUTPUT_TOKEN_LIMIT;
|
|
45
29
|
}
|
|
46
30
|
// If no maxTokens specified, use the restricted limit as default
|
|
47
31
|
if (requestedMaxTokens === undefined || requestedMaxTokens === null) {
|
|
48
|
-
return
|
|
32
|
+
return RESTRICTED_OUTPUT_TOKEN_LIMIT;
|
|
49
33
|
}
|
|
50
34
|
// Otherwise, use the requested value (it's within limits, including 0)
|
|
51
35
|
return requestedMaxTokens;
|
|
@@ -54,7 +38,12 @@ export function getSafeMaxTokens(provider, model, requestedMaxTokens) {
|
|
|
54
38
|
const providerLimits = PROVIDER_MAX_TOKENS[provider];
|
|
55
39
|
if (!providerLimits) {
|
|
56
40
|
logger.warn(`Unknown provider ${provider}, no token limits enforced`);
|
|
57
|
-
|
|
41
|
+
// Explicit undefined/null check so a caller-supplied 0 is preserved
|
|
42
|
+
// (truthy guard would silently drop 0 here as it does in the restricted branch).
|
|
43
|
+
if (requestedMaxTokens === undefined || requestedMaxTokens === null) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return requestedMaxTokens;
|
|
58
47
|
}
|
|
59
48
|
// Get model-specific limit or provider default
|
|
60
49
|
let maxLimit;
|
|
@@ -73,8 +62,10 @@ export function getSafeMaxTokens(provider, model, requestedMaxTokens) {
|
|
|
73
62
|
else {
|
|
74
63
|
maxLimit = PROVIDER_MAX_TOKENS.default;
|
|
75
64
|
}
|
|
76
|
-
// If no specific maxTokens requested, return the provider limit
|
|
77
|
-
|
|
65
|
+
// If no specific maxTokens requested, return the provider limit.
|
|
66
|
+
// Use explicit undefined/null so a caller-supplied 0 is preserved
|
|
67
|
+
// (matches the restricted-model branch above).
|
|
68
|
+
if (requestedMaxTokens === undefined || requestedMaxTokens === null) {
|
|
78
69
|
return maxLimit;
|
|
79
70
|
}
|
|
80
71
|
// If requested maxTokens exceeds the limit, use the limit and warn
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function initializeHippocampus(config: HippocampusConfig):
|
|
1
|
+
import type { HippocampusConfig, HippocampusLike } from "../types/index.js";
|
|
2
|
+
export declare function initializeHippocampus(config: HippocampusConfig): HippocampusLike | null;
|
|
@@ -1,8 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
2
|
import { logger } from "../utils/logger.js";
|
|
3
|
+
// Lazy require so importing NeuroLink core does not fail when the optional
|
|
4
|
+
// peer @juspay/hippocampus is not installed. The package was previously a
|
|
5
|
+
// hard runtime dependency, but Hippocampus declares a peer on
|
|
6
|
+
// @juspay/neurolink which made pnpm pull a registry NeuroLink that
|
|
7
|
+
// transitively required @ai-sdk/google + @ai-sdk/google-vertex into the
|
|
8
|
+
// production graph. Making memory optional breaks that cycle while keeping
|
|
9
|
+
// the same runtime behavior whenever the package is installed.
|
|
10
|
+
const lazyRequire = createRequire(import.meta.url);
|
|
11
|
+
let cachedModule;
|
|
12
|
+
function loadHippocampusModule() {
|
|
13
|
+
if (cachedModule !== undefined) {
|
|
14
|
+
return cachedModule;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
cachedModule = lazyRequire("@juspay/hippocampus");
|
|
18
|
+
return cachedModule;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
cachedModule = null;
|
|
22
|
+
logger.debug("[memoryInitializer] @juspay/hippocampus is not installed; memory features disabled.", {
|
|
23
|
+
error: error instanceof Error ? error.message : String(error),
|
|
24
|
+
});
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
3
28
|
export function initializeHippocampus(config) {
|
|
29
|
+
const mod = loadHippocampusModule();
|
|
30
|
+
if (!mod) {
|
|
31
|
+
logger.warn("[memoryInitializer] Memory configuration provided but @juspay/hippocampus is not installed. Run `pnpm add @juspay/hippocampus` (or your package manager equivalent) to enable memory.");
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
4
34
|
try {
|
|
5
|
-
const instance = new Hippocampus(config);
|
|
35
|
+
const instance = new mod.Hippocampus(config);
|
|
6
36
|
logger.info("[memoryInitializer] Memory initialized successfully", {
|
|
7
37
|
storageType: config.storage?.type || "sqlite",
|
|
8
38
|
maxWords: config.maxWords || 50,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { logger } from "../../utils/logger.js";
|
|
11
11
|
import { isRecoverableError } from "../../utils/errorHandling.js";
|
|
12
|
+
import { fireOnErrorOnce } from "../../utils/lifecycleCallbacks.js";
|
|
12
13
|
export function createLifecycleMiddleware(config = {}) {
|
|
13
14
|
const metadata = {
|
|
14
15
|
id: "lifecycle",
|
|
@@ -50,22 +51,12 @@ export function createLifecycleMiddleware(config = {}) {
|
|
|
50
51
|
return result;
|
|
51
52
|
}
|
|
52
53
|
catch (error) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
recoverable: isRecoverableError(err),
|
|
60
|
-
});
|
|
61
|
-
Promise.resolve(callbackResult).catch((e) => {
|
|
62
|
-
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
54
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
55
|
+
fireOnErrorOnce(config.onError, error, {
|
|
56
|
+
error: err,
|
|
57
|
+
duration: Date.now() - startTime,
|
|
58
|
+
recoverable: isRecoverableError(err),
|
|
59
|
+
});
|
|
69
60
|
throw error;
|
|
70
61
|
}
|
|
71
62
|
},
|
|
@@ -102,22 +93,12 @@ export function createLifecycleMiddleware(config = {}) {
|
|
|
102
93
|
controller.enqueue(chunk);
|
|
103
94
|
}
|
|
104
95
|
catch (error) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
recoverable: isRecoverableError(err),
|
|
112
|
-
});
|
|
113
|
-
Promise.resolve(callbackResult).catch((e) => {
|
|
114
|
-
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
catch (e) {
|
|
118
|
-
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
96
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
97
|
+
fireOnErrorOnce(config.onError, error, {
|
|
98
|
+
error: err,
|
|
99
|
+
duration: Date.now() - startTime,
|
|
100
|
+
recoverable: isRecoverableError(err),
|
|
101
|
+
});
|
|
121
102
|
throw error;
|
|
122
103
|
}
|
|
123
104
|
},
|
|
@@ -144,22 +125,12 @@ export function createLifecycleMiddleware(config = {}) {
|
|
|
144
125
|
};
|
|
145
126
|
}
|
|
146
127
|
catch (error) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
recoverable: isRecoverableError(err),
|
|
154
|
-
});
|
|
155
|
-
Promise.resolve(callbackResult).catch((e) => {
|
|
156
|
-
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
128
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
129
|
+
fireOnErrorOnce(config.onError, error, {
|
|
130
|
+
error: err,
|
|
131
|
+
duration: Date.now() - startTime,
|
|
132
|
+
recoverable: isRecoverableError(err),
|
|
133
|
+
});
|
|
163
134
|
throw error;
|
|
164
135
|
}
|
|
165
136
|
},
|
package/dist/neurolink.js
CHANGED
|
@@ -64,6 +64,7 @@ import { getConversationMessages, storeConversationTurn, } from "./utils/convers
|
|
|
64
64
|
import { CircuitBreaker, ERROR_CODES, ErrorFactory, isAbortError, isRetriableError, logStructuredError, NeuroLinkError, withRetry, withTimeout, } from "./utils/errorHandling.js";
|
|
65
65
|
// Factory processing imports
|
|
66
66
|
import { createCleanStreamOptions, enhanceTextGenerationOptions, processFactoryOptions, processStreamingFactoryOptions, validateFactoryConfig, } from "./utils/factoryProcessing.js";
|
|
67
|
+
import { fireOnErrorOnce } from "./utils/lifecycleCallbacks.js";
|
|
67
68
|
import { logger, mcpLogger } from "./utils/logger.js";
|
|
68
69
|
import { extractMcpErrorText } from "./utils/mcpErrorText.js";
|
|
69
70
|
import { createCustomToolServerInfo, detectCategory, } from "./utils/mcpDefaults.js";
|
|
@@ -2753,7 +2754,29 @@ Current user's request: ${currentInput}`;
|
|
|
2753
2754
|
* @since 1.0.0
|
|
2754
2755
|
*/
|
|
2755
2756
|
async generate(optionsOrPrompt) {
|
|
2756
|
-
|
|
2757
|
+
const startTime = Date.now();
|
|
2758
|
+
try {
|
|
2759
|
+
return await this.runWithFallbackOrchestration(optionsOrPrompt, "generate", (opts) => tracers.sdk.startActiveSpan("neurolink.generate", { kind: SpanKind.INTERNAL }, (generateSpan) => this.executeGenerateWithMetricsContext(opts, generateSpan)));
|
|
2760
|
+
}
|
|
2761
|
+
catch (error) {
|
|
2762
|
+
// Fire `onError` lifecycle callback for ANY thrown error — including
|
|
2763
|
+
// ones raised before the provider is even instantiated (invalid
|
|
2764
|
+
// provider name, missing credentials, etc.). The downstream
|
|
2765
|
+
// LifecycleMiddleware only fires `onError` once it has wrapped the
|
|
2766
|
+
// AI SDK doGenerate, which is too late for early-resolution failures.
|
|
2767
|
+
// `fireOnErrorOnce` dedupes against the middleware path so the
|
|
2768
|
+
// consumer callback fires at most once per logical failure.
|
|
2769
|
+
const onError = typeof optionsOrPrompt === "object" && optionsOrPrompt !== null
|
|
2770
|
+
? optionsOrPrompt.onError
|
|
2771
|
+
: undefined;
|
|
2772
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2773
|
+
fireOnErrorOnce(onError, error, {
|
|
2774
|
+
error: err,
|
|
2775
|
+
duration: Date.now() - startTime,
|
|
2776
|
+
recoverable: false,
|
|
2777
|
+
});
|
|
2778
|
+
throw error;
|
|
2779
|
+
}
|
|
2757
2780
|
}
|
|
2758
2781
|
/**
|
|
2759
2782
|
* Curator P2-3: wraps a generate/stream call with the fallback
|
|
@@ -3167,6 +3190,12 @@ Current user's request: ${currentInput}`;
|
|
|
3167
3190
|
middleware: options.middleware,
|
|
3168
3191
|
conversationMessages: options.conversationMessages,
|
|
3169
3192
|
credentials: options.credentials,
|
|
3193
|
+
// Lifecycle callbacks must reach the provider so non-AI-SDK paths
|
|
3194
|
+
// (Vertex's native @google/genai, native Bedrock, Ollama, etc.) can
|
|
3195
|
+
// invoke them directly. Pipeline A also still receives them via the
|
|
3196
|
+
// wrapped middleware config set by applyGenerateLifecycleMiddleware.
|
|
3197
|
+
onFinish: options.onFinish,
|
|
3198
|
+
onError: options.onError,
|
|
3170
3199
|
};
|
|
3171
3200
|
const extraContext = options;
|
|
3172
3201
|
if (extraContext.sessionId || extraContext.userId) {
|
|
@@ -5032,7 +5061,25 @@ Current user's request: ${currentInput}`;
|
|
|
5032
5061
|
: [],
|
|
5033
5062
|
optionKeys: Object.keys(options),
|
|
5034
5063
|
});
|
|
5035
|
-
|
|
5064
|
+
const startTime = Date.now();
|
|
5065
|
+
try {
|
|
5066
|
+
return await this.streamWithIterationFallback(options);
|
|
5067
|
+
}
|
|
5068
|
+
catch (error) {
|
|
5069
|
+
// Fire `onError` for early-resolution failures (invalid provider,
|
|
5070
|
+
// missing credentials, etc.) that surface before the per-chunk
|
|
5071
|
+
// wrapper installed by GoogleVertex / LifecycleMiddleware can run.
|
|
5072
|
+
// `fireOnErrorOnce` dedupes against the middleware path so the
|
|
5073
|
+
// consumer callback fires at most once per logical failure.
|
|
5074
|
+
const onError = options.onError;
|
|
5075
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
5076
|
+
fireOnErrorOnce(onError, error, {
|
|
5077
|
+
error: err,
|
|
5078
|
+
duration: Date.now() - startTime,
|
|
5079
|
+
recoverable: false,
|
|
5080
|
+
});
|
|
5081
|
+
throw error;
|
|
5082
|
+
}
|
|
5036
5083
|
}
|
|
5037
5084
|
/**
|
|
5038
5085
|
* Curator P2-3 / Reviewer Finding #2: stream-fallback that also covers
|
|
@@ -41,7 +41,8 @@ export declare class GoogleAIStudioProvider extends BaseProvider {
|
|
|
41
41
|
getProviderName(): AIProviderName;
|
|
42
42
|
getDefaultModel(): string;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* AI SDK model instance — no longer used.
|
|
45
|
+
* All models are routed through native @google/genai SDK directly.
|
|
45
46
|
*/
|
|
46
47
|
getAISDKModel(): LanguageModel;
|
|
47
48
|
protected formatProviderError(error: unknown): Error;
|
|
@@ -62,8 +63,8 @@ export declare class GoogleAIStudioProvider extends BaseProvider {
|
|
|
62
63
|
private estimateTokenCount;
|
|
63
64
|
protected executeStream(options: StreamOptions, analysisSchema?: ZodUnknownSchema | Schema<unknown>): Promise<StreamResult>;
|
|
64
65
|
/**
|
|
65
|
-
* Execute stream using native @google/genai SDK
|
|
66
|
-
*
|
|
66
|
+
* Execute stream using native @google/genai SDK
|
|
67
|
+
* Uses @google/genai directly for all Gemini models (2.0, 2.5, 3.x)
|
|
67
68
|
*/
|
|
68
69
|
private executeNativeGemini3Stream;
|
|
69
70
|
/**
|
|
@@ -75,6 +76,13 @@ export declare class GoogleAIStudioProvider extends BaseProvider {
|
|
|
75
76
|
* Override generate to route Gemini 3 models with tools to native SDK
|
|
76
77
|
*/
|
|
77
78
|
generate(optionsOrPrompt: TextGenerationOptions | string): Promise<EnhancedGenerateResult | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Emit `generation:end` so the Pipeline B observability listener creates
|
|
81
|
+
* a `model.generation` span for native Google AI Studio generate calls.
|
|
82
|
+
* Without this hand-off the native path silently disappears from
|
|
83
|
+
* Pipeline B exporters (Langfuse, custom OTEL collectors).
|
|
84
|
+
*/
|
|
85
|
+
private emitPipelineBGenerationEvent;
|
|
78
86
|
private executeAudioStreamViaGeminiLive;
|
|
79
87
|
protected getDefaultEmbeddingModel(): string;
|
|
80
88
|
/**
|