@juspay/neurolink 9.29.1 → 9.30.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 +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/middleware/builtin/lifecycle.d.ts +11 -0
- package/dist/lib/middleware/builtin/lifecycle.js +165 -0
- package/dist/lib/middleware/factory.js +3 -0
- package/dist/lib/middleware/index.d.ts +1 -0
- package/dist/lib/middleware/index.js +2 -0
- package/dist/lib/neurolink.js +41 -1
- package/dist/lib/types/generateTypes.d.ts +7 -1
- package/dist/lib/types/middlewareTypes.d.ts +54 -1
- package/dist/lib/types/streamTypes.d.ts +7 -1
- package/dist/lib/utils/errorHandling.d.ts +5 -0
- package/dist/lib/utils/errorHandling.js +38 -0
- package/dist/middleware/builtin/lifecycle.d.ts +11 -0
- package/dist/middleware/builtin/lifecycle.js +164 -0
- package/dist/middleware/factory.js +3 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/neurolink.js +41 -1
- package/dist/types/generateTypes.d.ts +7 -1
- package/dist/types/middlewareTypes.d.ts +54 -1
- package/dist/types/streamTypes.d.ts +7 -1
- package/dist/utils/errorHandling.d.ts +5 -0
- package/dist/utils/errorHandling.js +38 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [9.30.0](https://github.com/juspay/neurolink/compare/v9.29.1...v9.30.0) (2026-03-21)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- **(middleware):** add lifecycle middleware with onFinish, onError, onChunk callbacks ([2d23087](https://github.com/juspay/neurolink/commit/2d230879272a8e67fd936ff087b232bb25f612c0))
|
|
6
|
+
|
|
1
7
|
## [9.29.1](https://github.com/juspay/neurolink/compare/v9.29.0...v9.29.1) (2026-03-20)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ import { createContextEnricher, flushOpenTelemetry, getLangfuseContext, getLangf
|
|
|
53
53
|
export type { LangfuseContext } from "./services/server/ai/observability/instrumentation.js";
|
|
54
54
|
export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, getLangfuseSpanProcessor, getTracerProvider, isOpenTelemetryInitialized, getSpanProcessors, createContextEnricher, isUsingExternalTracerProvider, getLangfuseContext, getTracer, runWithCurrentLangfuseContext, };
|
|
55
55
|
export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
|
|
56
|
+
export { createLifecycleMiddleware } from "./middleware/builtin/lifecycle.js";
|
|
56
57
|
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
57
58
|
export { ExporterRegistry } from "./observability/exporterRegistry.js";
|
|
58
59
|
export { NoOpExporter } from "./observability/exporters/baseExporter.js";
|
package/dist/index.js
CHANGED
|
@@ -71,6 +71,7 @@ getLangfuseContext, getTracer,
|
|
|
71
71
|
runWithCurrentLangfuseContext, };
|
|
72
72
|
// Analytics Middleware exports
|
|
73
73
|
export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
|
|
74
|
+
export { createLifecycleMiddleware } from "./middleware/builtin/lifecycle.js";
|
|
74
75
|
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
75
76
|
export { ExporterRegistry } from "./observability/exporterRegistry.js";
|
|
76
77
|
export { NoOpExporter } from "./observability/exporters/baseExporter.js";
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ import { createContextEnricher, flushOpenTelemetry, getLangfuseContext, getLangf
|
|
|
53
53
|
export type { LangfuseContext } from "./services/server/ai/observability/instrumentation.js";
|
|
54
54
|
export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, getLangfuseSpanProcessor, getTracerProvider, isOpenTelemetryInitialized, getSpanProcessors, createContextEnricher, isUsingExternalTracerProvider, getLangfuseContext, getTracer, runWithCurrentLangfuseContext, };
|
|
55
55
|
export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
|
|
56
|
+
export { createLifecycleMiddleware } from "./middleware/builtin/lifecycle.js";
|
|
56
57
|
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
57
58
|
export { ExporterRegistry } from "./observability/exporterRegistry.js";
|
|
58
59
|
export { NoOpExporter } from "./observability/exporters/baseExporter.js";
|
package/dist/lib/index.js
CHANGED
|
@@ -71,6 +71,7 @@ getLangfuseContext, getTracer,
|
|
|
71
71
|
runWithCurrentLangfuseContext, };
|
|
72
72
|
// Analytics Middleware exports
|
|
73
73
|
export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
|
|
74
|
+
export { createLifecycleMiddleware } from "./middleware/builtin/lifecycle.js";
|
|
74
75
|
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
75
76
|
export { ExporterRegistry } from "./observability/exporterRegistry.js";
|
|
76
77
|
export { NoOpExporter } from "./observability/exporters/baseExporter.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides onFinish, onError, and onChunk callbacks for observing
|
|
5
|
+
* generation and streaming lifecycle events.
|
|
6
|
+
*
|
|
7
|
+
* This middleware is automatically enabled when lifecycle callbacks
|
|
8
|
+
* (onFinish, onError, onChunk) are passed in GenerateOptions or StreamOptions.
|
|
9
|
+
*/
|
|
10
|
+
import type { NeuroLinkMiddleware, LifecycleMiddlewareConfig } from "../../types/middlewareTypes.js";
|
|
11
|
+
export declare function createLifecycleMiddleware(config?: LifecycleMiddlewareConfig): NeuroLinkMiddleware;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides onFinish, onError, and onChunk callbacks for observing
|
|
5
|
+
* generation and streaming lifecycle events.
|
|
6
|
+
*
|
|
7
|
+
* This middleware is automatically enabled when lifecycle callbacks
|
|
8
|
+
* (onFinish, onError, onChunk) are passed in GenerateOptions or StreamOptions.
|
|
9
|
+
*/
|
|
10
|
+
import { logger } from "../../utils/logger.js";
|
|
11
|
+
import { isRecoverableError } from "../../utils/errorHandling.js";
|
|
12
|
+
export function createLifecycleMiddleware(config = {}) {
|
|
13
|
+
const metadata = {
|
|
14
|
+
id: "lifecycle",
|
|
15
|
+
name: "Lifecycle Callbacks",
|
|
16
|
+
description: "Provides onFinish, onError, and onChunk callbacks for generation and streaming lifecycle events",
|
|
17
|
+
priority: 110,
|
|
18
|
+
defaultEnabled: false,
|
|
19
|
+
};
|
|
20
|
+
const middleware = {
|
|
21
|
+
wrapGenerate: async ({ doGenerate }) => {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
try {
|
|
24
|
+
const result = await doGenerate();
|
|
25
|
+
if (config.onFinish) {
|
|
26
|
+
try {
|
|
27
|
+
const callbackResult = config.onFinish({
|
|
28
|
+
text: result.text ?? "",
|
|
29
|
+
usage: result.usage
|
|
30
|
+
? {
|
|
31
|
+
promptTokens: result.usage.promptTokens ?? 0,
|
|
32
|
+
completionTokens: result.usage.completionTokens ?? 0,
|
|
33
|
+
}
|
|
34
|
+
: undefined,
|
|
35
|
+
duration: Date.now() - startTime,
|
|
36
|
+
finishReason: result.finishReason,
|
|
37
|
+
});
|
|
38
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
39
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (config.onError) {
|
|
50
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
51
|
+
try {
|
|
52
|
+
const callbackResult = config.onError({
|
|
53
|
+
error: err,
|
|
54
|
+
duration: Date.now() - startTime,
|
|
55
|
+
recoverable: isRecoverableError(err),
|
|
56
|
+
});
|
|
57
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
58
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
wrapStream: async ({ doStream }) => {
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
try {
|
|
71
|
+
const result = await doStream();
|
|
72
|
+
if (!config.onChunk && !config.onFinish && !config.onError) {
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
let sequenceNumber = 0;
|
|
76
|
+
let accumulatedText = "";
|
|
77
|
+
const transformStream = new TransformStream({
|
|
78
|
+
transform(chunk, controller) {
|
|
79
|
+
try {
|
|
80
|
+
if (chunk.type === "text-delta") {
|
|
81
|
+
accumulatedText += chunk.textDelta;
|
|
82
|
+
}
|
|
83
|
+
if (config.onChunk && chunk.type) {
|
|
84
|
+
try {
|
|
85
|
+
const callbackResult = config.onChunk({
|
|
86
|
+
type: chunk.type,
|
|
87
|
+
textDelta: chunk.type === "text-delta" ? chunk.textDelta : undefined,
|
|
88
|
+
sequenceNumber: sequenceNumber++,
|
|
89
|
+
});
|
|
90
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
91
|
+
logger.warn("[LifecycleMiddleware] onChunk callback error:", e);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
logger.warn("[LifecycleMiddleware] onChunk callback error:", e);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
controller.enqueue(chunk);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (config.onError) {
|
|
102
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
103
|
+
try {
|
|
104
|
+
const callbackResult = config.onError({
|
|
105
|
+
error: err,
|
|
106
|
+
duration: Date.now() - startTime,
|
|
107
|
+
recoverable: isRecoverableError(err),
|
|
108
|
+
});
|
|
109
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
110
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
flush() {
|
|
121
|
+
if (config.onFinish) {
|
|
122
|
+
try {
|
|
123
|
+
const callbackResult = config.onFinish({
|
|
124
|
+
text: accumulatedText,
|
|
125
|
+
duration: Date.now() - startTime,
|
|
126
|
+
});
|
|
127
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
128
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
...result,
|
|
139
|
+
stream: result.stream.pipeThrough(transformStream),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (config.onError) {
|
|
144
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
145
|
+
try {
|
|
146
|
+
const callbackResult = config.onError({
|
|
147
|
+
error: err,
|
|
148
|
+
duration: Date.now() - startTime,
|
|
149
|
+
recoverable: isRecoverableError(err),
|
|
150
|
+
});
|
|
151
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
152
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
return { ...middleware, metadata };
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -3,6 +3,7 @@ import { MiddlewareRegistry } from "./registry.js";
|
|
|
3
3
|
import { createAnalyticsMiddleware } from "./builtin/analytics.js";
|
|
4
4
|
import { createGuardrailsMiddleware } from "./builtin/guardrails.js";
|
|
5
5
|
import { createAutoEvaluationMiddleware } from "./builtin/autoEvaluation.js";
|
|
6
|
+
import { createLifecycleMiddleware } from "./builtin/lifecycle.js";
|
|
6
7
|
import { logger } from "../utils/logger.js";
|
|
7
8
|
/**
|
|
8
9
|
* Middleware factory for creating and applying middleware chains.
|
|
@@ -26,6 +27,7 @@ export class MiddlewareFactory {
|
|
|
26
27
|
analytics: createAnalyticsMiddleware,
|
|
27
28
|
guardrails: createGuardrailsMiddleware,
|
|
28
29
|
autoEvaluation: createAutoEvaluationMiddleware,
|
|
30
|
+
lifecycle: createLifecycleMiddleware,
|
|
29
31
|
};
|
|
30
32
|
// Register built-in presets
|
|
31
33
|
this.registerPreset({
|
|
@@ -142,6 +144,7 @@ export class MiddlewareFactory {
|
|
|
142
144
|
analytics: createAnalyticsMiddleware,
|
|
143
145
|
guardrails: createGuardrailsMiddleware,
|
|
144
146
|
autoEvaluation: createAutoEvaluationMiddleware,
|
|
147
|
+
lifecycle: createLifecycleMiddleware,
|
|
145
148
|
};
|
|
146
149
|
logger.debug("Getting creator for middleware ID:", id);
|
|
147
150
|
return builtInMiddlewareCreators[id];
|
|
@@ -9,4 +9,5 @@ import { MiddlewareFactory } from "./factory.js";
|
|
|
9
9
|
export type { NeuroLinkMiddleware, MiddlewareConfig, MiddlewareContext, MiddlewareConditions, MiddlewareRegistrationOptions, MiddlewareExecutionResult, MiddlewareChainStats, MiddlewarePreset, MiddlewareFactoryOptions, BuiltInMiddlewareType, } from "../types/middlewareTypes.js";
|
|
10
10
|
export type { LanguageModelV1Middleware } from "ai";
|
|
11
11
|
export { MiddlewareFactory };
|
|
12
|
+
export { createLifecycleMiddleware } from "./builtin/lifecycle.js";
|
|
12
13
|
export default MiddlewareFactory;
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import { MiddlewareFactory } from "./factory.js";
|
|
10
10
|
// Factory for creating and applying middleware chains
|
|
11
11
|
export { MiddlewareFactory };
|
|
12
|
+
// Built-in middleware creators
|
|
13
|
+
export { createLifecycleMiddleware } from "./builtin/lifecycle.js";
|
|
12
14
|
// Export the factory as the default export for clean, direct usage
|
|
13
15
|
export default MiddlewareFactory;
|
|
14
16
|
//# sourceMappingURL=index.js.map
|
package/dist/lib/neurolink.js
CHANGED
|
@@ -2209,6 +2209,26 @@ Current user's request: ${currentInput}`;
|
|
|
2209
2209
|
},
|
|
2210
2210
|
});
|
|
2211
2211
|
}
|
|
2212
|
+
// Auto-inject lifecycle middleware when callbacks are provided
|
|
2213
|
+
// (must happen before workflow/PPT early returns so those paths get middleware too)
|
|
2214
|
+
if (options.onFinish || options.onError) {
|
|
2215
|
+
options.middleware = {
|
|
2216
|
+
...options.middleware,
|
|
2217
|
+
middlewareConfig: {
|
|
2218
|
+
...options.middleware?.middlewareConfig,
|
|
2219
|
+
lifecycle: {
|
|
2220
|
+
...options.middleware?.middlewareConfig?.lifecycle,
|
|
2221
|
+
enabled: true,
|
|
2222
|
+
config: {
|
|
2223
|
+
...options.middleware?.middlewareConfig?.lifecycle
|
|
2224
|
+
?.config,
|
|
2225
|
+
onFinish: options.onFinish,
|
|
2226
|
+
onError: options.onError,
|
|
2227
|
+
},
|
|
2228
|
+
},
|
|
2229
|
+
},
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2212
2232
|
// Check if workflow is requested
|
|
2213
2233
|
if (options.workflow || options.workflowConfig) {
|
|
2214
2234
|
return await this.generateWithWorkflow(options);
|
|
@@ -2364,6 +2384,7 @@ Current user's request: ${currentInput}`;
|
|
|
2364
2384
|
fileRegistry: this.fileRegistry,
|
|
2365
2385
|
abortSignal: options.abortSignal,
|
|
2366
2386
|
skipToolPromptInjection: options.skipToolPromptInjection,
|
|
2387
|
+
middleware: options.middleware,
|
|
2367
2388
|
};
|
|
2368
2389
|
// Auto-map top-level sessionId/userId to context for convenience
|
|
2369
2390
|
// Tests and users may pass sessionId/userId as top-level options
|
|
@@ -2400,7 +2421,6 @@ Current user's request: ${currentInput}`;
|
|
|
2400
2421
|
toolResults: toolResults.length,
|
|
2401
2422
|
});
|
|
2402
2423
|
}
|
|
2403
|
-
// Use redesigned generation logic
|
|
2404
2424
|
const textResult = await this.generateTextInternal(textOptions);
|
|
2405
2425
|
// Emit generation completion event (NeuroLink format - enhanced with content)
|
|
2406
2426
|
this.emitter.emit("generation:end", {
|
|
@@ -4024,6 +4044,26 @@ Current user's request: ${currentInput}`;
|
|
|
4024
4044
|
});
|
|
4025
4045
|
}
|
|
4026
4046
|
this.emitStreamStartEvents(options, startTime);
|
|
4047
|
+
// Auto-inject lifecycle middleware when callbacks are provided
|
|
4048
|
+
// (must happen before workflow early return so that path gets middleware too)
|
|
4049
|
+
if (options.onFinish || options.onError || options.onChunk) {
|
|
4050
|
+
options.middleware = {
|
|
4051
|
+
...options.middleware,
|
|
4052
|
+
middlewareConfig: {
|
|
4053
|
+
...options.middleware?.middlewareConfig,
|
|
4054
|
+
lifecycle: {
|
|
4055
|
+
...options.middleware?.middlewareConfig?.lifecycle,
|
|
4056
|
+
enabled: true,
|
|
4057
|
+
config: {
|
|
4058
|
+
...options.middleware?.middlewareConfig?.lifecycle?.config,
|
|
4059
|
+
onFinish: options.onFinish,
|
|
4060
|
+
onError: options.onError,
|
|
4061
|
+
onChunk: options.onChunk,
|
|
4062
|
+
},
|
|
4063
|
+
},
|
|
4064
|
+
},
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4027
4067
|
// Check if workflow is requested
|
|
4028
4068
|
if (options.workflow || options.workflowConfig) {
|
|
4029
4069
|
const result = await this.streamWithWorkflow(options, startTime);
|
|
@@ -6,7 +6,7 @@ import type { JsonValue } from "./common.js";
|
|
|
6
6
|
import type { Content, ImageWithAltText } from "./content.js";
|
|
7
7
|
import type { ChatMessage, ConversationMemoryConfig } from "./conversation.js";
|
|
8
8
|
import type { EvaluationData } from "./evaluation.js";
|
|
9
|
-
import type { MiddlewareFactoryOptions } from "./middlewareTypes.js";
|
|
9
|
+
import type { MiddlewareFactoryOptions, OnFinishCallback, OnErrorCallback } from "./middlewareTypes.js";
|
|
10
10
|
import type { DirectorModeOptions, DirectorSegment, VideoGenerationResult, VideoOutputOptions } from "./multimodal.js";
|
|
11
11
|
import type { PPTGenerationResult, PPTOutputOptions } from "./pptTypes.js";
|
|
12
12
|
import type { TTSOptions, TTSResult } from "./ttsTypes.js";
|
|
@@ -399,6 +399,12 @@ export type GenerateOptions = {
|
|
|
399
399
|
* @internal Set by NeuroLink SDK — not typically used directly by consumers.
|
|
400
400
|
*/
|
|
401
401
|
fileRegistry?: unknown;
|
|
402
|
+
/** Per-call middleware configuration. */
|
|
403
|
+
middleware?: import("./middlewareTypes.js").MiddlewareFactoryOptions;
|
|
404
|
+
/** Callback invoked when generation completes successfully. */
|
|
405
|
+
onFinish?: OnFinishCallback;
|
|
406
|
+
/** Callback invoked when generation encounters an error. */
|
|
407
|
+
onError?: OnErrorCallback;
|
|
402
408
|
};
|
|
403
409
|
/**
|
|
404
410
|
* Generate function result type - Primary output format
|
|
@@ -110,7 +110,7 @@ export type MiddlewareChainStats = {
|
|
|
110
110
|
/**
|
|
111
111
|
* Built-in middleware types
|
|
112
112
|
*/
|
|
113
|
-
export type BuiltInMiddlewareType = "analytics" | "guardrails" | "logging" | "caching" | "rateLimit" | "retry" | "timeout" | "autoEvaluation";
|
|
113
|
+
export type BuiltInMiddlewareType = "analytics" | "guardrails" | "logging" | "caching" | "rateLimit" | "retry" | "timeout" | "autoEvaluation" | "lifecycle";
|
|
114
114
|
/**
|
|
115
115
|
* Middleware preset configurations
|
|
116
116
|
*/
|
|
@@ -233,3 +233,56 @@ export type MiddlewareChainConfig = {
|
|
|
233
233
|
timeout?: number;
|
|
234
234
|
retries?: number;
|
|
235
235
|
};
|
|
236
|
+
/**
|
|
237
|
+
* Payload delivered to onFinish callbacks after generation or streaming completes.
|
|
238
|
+
*/
|
|
239
|
+
export type LifecycleFinishPayload = {
|
|
240
|
+
/** The generated text content */
|
|
241
|
+
text: string;
|
|
242
|
+
/** Token usage from the provider */
|
|
243
|
+
usage?: {
|
|
244
|
+
promptTokens: number;
|
|
245
|
+
completionTokens: number;
|
|
246
|
+
};
|
|
247
|
+
/** Wall-clock duration in milliseconds */
|
|
248
|
+
duration: number;
|
|
249
|
+
/** Why generation stopped */
|
|
250
|
+
finishReason?: string;
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Payload delivered to onError callbacks when generation or streaming fails.
|
|
254
|
+
*/
|
|
255
|
+
export type LifecycleErrorPayload = {
|
|
256
|
+
/** The error that occurred */
|
|
257
|
+
error: Error;
|
|
258
|
+
/** Wall-clock duration until failure in milliseconds */
|
|
259
|
+
duration: number;
|
|
260
|
+
/** Whether the error is likely recoverable (rate limit, timeout, network) */
|
|
261
|
+
recoverable: boolean;
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Payload delivered to onChunk callbacks for each streaming chunk.
|
|
265
|
+
*/
|
|
266
|
+
export type LifecycleChunkPayload = {
|
|
267
|
+
/** Chunk type from the AI SDK stream */
|
|
268
|
+
type: string;
|
|
269
|
+
/** Text content for text-delta chunks */
|
|
270
|
+
textDelta?: string;
|
|
271
|
+
/** Zero-based chunk sequence number */
|
|
272
|
+
sequenceNumber: number;
|
|
273
|
+
};
|
|
274
|
+
/** Callback invoked when generation or streaming finishes successfully. */
|
|
275
|
+
export type OnFinishCallback = (payload: LifecycleFinishPayload) => void | Promise<void>;
|
|
276
|
+
/** Callback invoked when generation or streaming encounters an error. */
|
|
277
|
+
export type OnErrorCallback = (payload: LifecycleErrorPayload) => void | Promise<void>;
|
|
278
|
+
/** Callback invoked for each chunk during streaming. */
|
|
279
|
+
export type OnChunkCallback = (payload: LifecycleChunkPayload) => void | Promise<void>;
|
|
280
|
+
/**
|
|
281
|
+
* Configuration for the lifecycle middleware.
|
|
282
|
+
* Pass callbacks to observe generation/streaming lifecycle events.
|
|
283
|
+
*/
|
|
284
|
+
export type LifecycleMiddlewareConfig = {
|
|
285
|
+
onFinish?: OnFinishCallback;
|
|
286
|
+
onError?: OnErrorCallback;
|
|
287
|
+
onChunk?: OnChunkCallback;
|
|
288
|
+
};
|
|
@@ -3,7 +3,7 @@ import type { AIProviderName } from "../constants/enums.js";
|
|
|
3
3
|
import type { EvaluationData } from "../index.js";
|
|
4
4
|
import type { RAGConfig } from "../rag/types.js";
|
|
5
5
|
import type { AnalyticsData, ToolExecutionEvent, ToolExecutionSummary } from "../types/index.js";
|
|
6
|
-
import type { MiddlewareFactoryOptions } from "../types/middlewareTypes.js";
|
|
6
|
+
import type { MiddlewareFactoryOptions, OnFinishCallback, OnErrorCallback, OnChunkCallback } from "../types/middlewareTypes.js";
|
|
7
7
|
import type { TokenUsage } from "./analytics.js";
|
|
8
8
|
import type { JsonValue, UnknownRecord } from "./common.js";
|
|
9
9
|
import type { Content, ImageWithAltText } from "./content.js";
|
|
@@ -409,6 +409,12 @@ export type StreamOptions = {
|
|
|
409
409
|
* @internal Set by NeuroLink SDK — not typically used directly by consumers.
|
|
410
410
|
*/
|
|
411
411
|
fileRegistry?: unknown;
|
|
412
|
+
/** Callback invoked when streaming completes successfully. */
|
|
413
|
+
onFinish?: OnFinishCallback;
|
|
414
|
+
/** Callback invoked when streaming encounters an error. */
|
|
415
|
+
onError?: OnErrorCallback;
|
|
416
|
+
/** Callback invoked for each streaming chunk. */
|
|
417
|
+
onChunk?: OnChunkCallback;
|
|
412
418
|
};
|
|
413
419
|
/**
|
|
414
420
|
* Stream function result type - Primary output format for streaming
|
|
@@ -258,6 +258,11 @@ export declare function isAbortError(error: unknown): boolean;
|
|
|
258
258
|
* Error handler that decides whether to retry based on error type
|
|
259
259
|
*/
|
|
260
260
|
export declare function isRetriableError(error: Error): boolean;
|
|
261
|
+
/**
|
|
262
|
+
* Determines if an error is likely recoverable (rate limit, timeout, network issues).
|
|
263
|
+
* Useful for deciding whether to retry or fail fast.
|
|
264
|
+
*/
|
|
265
|
+
export declare function isRecoverableError(error: Error): boolean;
|
|
261
266
|
/**
|
|
262
267
|
* Enhanced error logger that provides structured logging
|
|
263
268
|
*/
|
|
@@ -865,6 +865,44 @@ export function isRetriableError(error) {
|
|
|
865
865
|
];
|
|
866
866
|
return retriablePatterns.some((pattern) => pattern.test(error.message));
|
|
867
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* Determines if an error is likely recoverable (rate limit, timeout, network issues).
|
|
870
|
+
* Useful for deciding whether to retry or fail fast.
|
|
871
|
+
*/
|
|
872
|
+
export function isRecoverableError(error) {
|
|
873
|
+
// Check NeuroLinkError.retriable first
|
|
874
|
+
const errorWithRetriable = error;
|
|
875
|
+
if ("retriable" in error &&
|
|
876
|
+
typeof errorWithRetriable.retriable === "boolean") {
|
|
877
|
+
return errorWithRetriable.retriable;
|
|
878
|
+
}
|
|
879
|
+
const message = error.message?.toLowerCase() || "";
|
|
880
|
+
// Rate limit errors
|
|
881
|
+
if (message.includes("rate limit") || message.includes("too many requests")) {
|
|
882
|
+
return true;
|
|
883
|
+
}
|
|
884
|
+
if (/\b429\b/.test(message)) {
|
|
885
|
+
return true;
|
|
886
|
+
}
|
|
887
|
+
// Timeout errors
|
|
888
|
+
if (message.includes("timeout") ||
|
|
889
|
+
message.includes("etimedout") ||
|
|
890
|
+
message.includes("timed out")) {
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
// Network errors
|
|
894
|
+
if (message.includes("econnreset") ||
|
|
895
|
+
message.includes("econnrefused") ||
|
|
896
|
+
message.includes("network") ||
|
|
897
|
+
message.includes("socket")) {
|
|
898
|
+
return true;
|
|
899
|
+
}
|
|
900
|
+
// Server errors (use word boundaries to avoid false matches)
|
|
901
|
+
if (/\b50[0234]\b/.test(message)) {
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
868
906
|
/**
|
|
869
907
|
* Enhanced error logger that provides structured logging
|
|
870
908
|
*/
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides onFinish, onError, and onChunk callbacks for observing
|
|
5
|
+
* generation and streaming lifecycle events.
|
|
6
|
+
*
|
|
7
|
+
* This middleware is automatically enabled when lifecycle callbacks
|
|
8
|
+
* (onFinish, onError, onChunk) are passed in GenerateOptions or StreamOptions.
|
|
9
|
+
*/
|
|
10
|
+
import type { NeuroLinkMiddleware, LifecycleMiddlewareConfig } from "../../types/middlewareTypes.js";
|
|
11
|
+
export declare function createLifecycleMiddleware(config?: LifecycleMiddlewareConfig): NeuroLinkMiddleware;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides onFinish, onError, and onChunk callbacks for observing
|
|
5
|
+
* generation and streaming lifecycle events.
|
|
6
|
+
*
|
|
7
|
+
* This middleware is automatically enabled when lifecycle callbacks
|
|
8
|
+
* (onFinish, onError, onChunk) are passed in GenerateOptions or StreamOptions.
|
|
9
|
+
*/
|
|
10
|
+
import { logger } from "../../utils/logger.js";
|
|
11
|
+
import { isRecoverableError } from "../../utils/errorHandling.js";
|
|
12
|
+
export function createLifecycleMiddleware(config = {}) {
|
|
13
|
+
const metadata = {
|
|
14
|
+
id: "lifecycle",
|
|
15
|
+
name: "Lifecycle Callbacks",
|
|
16
|
+
description: "Provides onFinish, onError, and onChunk callbacks for generation and streaming lifecycle events",
|
|
17
|
+
priority: 110,
|
|
18
|
+
defaultEnabled: false,
|
|
19
|
+
};
|
|
20
|
+
const middleware = {
|
|
21
|
+
wrapGenerate: async ({ doGenerate }) => {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
try {
|
|
24
|
+
const result = await doGenerate();
|
|
25
|
+
if (config.onFinish) {
|
|
26
|
+
try {
|
|
27
|
+
const callbackResult = config.onFinish({
|
|
28
|
+
text: result.text ?? "",
|
|
29
|
+
usage: result.usage
|
|
30
|
+
? {
|
|
31
|
+
promptTokens: result.usage.promptTokens ?? 0,
|
|
32
|
+
completionTokens: result.usage.completionTokens ?? 0,
|
|
33
|
+
}
|
|
34
|
+
: undefined,
|
|
35
|
+
duration: Date.now() - startTime,
|
|
36
|
+
finishReason: result.finishReason,
|
|
37
|
+
});
|
|
38
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
39
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (config.onError) {
|
|
50
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
51
|
+
try {
|
|
52
|
+
const callbackResult = config.onError({
|
|
53
|
+
error: err,
|
|
54
|
+
duration: Date.now() - startTime,
|
|
55
|
+
recoverable: isRecoverableError(err),
|
|
56
|
+
});
|
|
57
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
58
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
wrapStream: async ({ doStream }) => {
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
try {
|
|
71
|
+
const result = await doStream();
|
|
72
|
+
if (!config.onChunk && !config.onFinish && !config.onError) {
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
let sequenceNumber = 0;
|
|
76
|
+
let accumulatedText = "";
|
|
77
|
+
const transformStream = new TransformStream({
|
|
78
|
+
transform(chunk, controller) {
|
|
79
|
+
try {
|
|
80
|
+
if (chunk.type === "text-delta") {
|
|
81
|
+
accumulatedText += chunk.textDelta;
|
|
82
|
+
}
|
|
83
|
+
if (config.onChunk && chunk.type) {
|
|
84
|
+
try {
|
|
85
|
+
const callbackResult = config.onChunk({
|
|
86
|
+
type: chunk.type,
|
|
87
|
+
textDelta: chunk.type === "text-delta" ? chunk.textDelta : undefined,
|
|
88
|
+
sequenceNumber: sequenceNumber++,
|
|
89
|
+
});
|
|
90
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
91
|
+
logger.warn("[LifecycleMiddleware] onChunk callback error:", e);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
logger.warn("[LifecycleMiddleware] onChunk callback error:", e);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
controller.enqueue(chunk);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (config.onError) {
|
|
102
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
103
|
+
try {
|
|
104
|
+
const callbackResult = config.onError({
|
|
105
|
+
error: err,
|
|
106
|
+
duration: Date.now() - startTime,
|
|
107
|
+
recoverable: isRecoverableError(err),
|
|
108
|
+
});
|
|
109
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
110
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
flush() {
|
|
121
|
+
if (config.onFinish) {
|
|
122
|
+
try {
|
|
123
|
+
const callbackResult = config.onFinish({
|
|
124
|
+
text: accumulatedText,
|
|
125
|
+
duration: Date.now() - startTime,
|
|
126
|
+
});
|
|
127
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
128
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
logger.warn("[LifecycleMiddleware] onFinish callback error:", e);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
...result,
|
|
139
|
+
stream: result.stream.pipeThrough(transformStream),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (config.onError) {
|
|
144
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
145
|
+
try {
|
|
146
|
+
const callbackResult = config.onError({
|
|
147
|
+
error: err,
|
|
148
|
+
duration: Date.now() - startTime,
|
|
149
|
+
recoverable: isRecoverableError(err),
|
|
150
|
+
});
|
|
151
|
+
Promise.resolve(callbackResult).catch((e) => {
|
|
152
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
logger.warn("[LifecycleMiddleware] onError callback error:", e);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
return { ...middleware, metadata };
|
|
164
|
+
}
|
|
@@ -3,6 +3,7 @@ import { MiddlewareRegistry } from "./registry.js";
|
|
|
3
3
|
import { createAnalyticsMiddleware } from "./builtin/analytics.js";
|
|
4
4
|
import { createGuardrailsMiddleware } from "./builtin/guardrails.js";
|
|
5
5
|
import { createAutoEvaluationMiddleware } from "./builtin/autoEvaluation.js";
|
|
6
|
+
import { createLifecycleMiddleware } from "./builtin/lifecycle.js";
|
|
6
7
|
import { logger } from "../utils/logger.js";
|
|
7
8
|
/**
|
|
8
9
|
* Middleware factory for creating and applying middleware chains.
|
|
@@ -26,6 +27,7 @@ export class MiddlewareFactory {
|
|
|
26
27
|
analytics: createAnalyticsMiddleware,
|
|
27
28
|
guardrails: createGuardrailsMiddleware,
|
|
28
29
|
autoEvaluation: createAutoEvaluationMiddleware,
|
|
30
|
+
lifecycle: createLifecycleMiddleware,
|
|
29
31
|
};
|
|
30
32
|
// Register built-in presets
|
|
31
33
|
this.registerPreset({
|
|
@@ -142,6 +144,7 @@ export class MiddlewareFactory {
|
|
|
142
144
|
analytics: createAnalyticsMiddleware,
|
|
143
145
|
guardrails: createGuardrailsMiddleware,
|
|
144
146
|
autoEvaluation: createAutoEvaluationMiddleware,
|
|
147
|
+
lifecycle: createLifecycleMiddleware,
|
|
145
148
|
};
|
|
146
149
|
logger.debug("Getting creator for middleware ID:", id);
|
|
147
150
|
return builtInMiddlewareCreators[id];
|
|
@@ -9,4 +9,5 @@ import { MiddlewareFactory } from "./factory.js";
|
|
|
9
9
|
export type { NeuroLinkMiddleware, MiddlewareConfig, MiddlewareContext, MiddlewareConditions, MiddlewareRegistrationOptions, MiddlewareExecutionResult, MiddlewareChainStats, MiddlewarePreset, MiddlewareFactoryOptions, BuiltInMiddlewareType, } from "../types/middlewareTypes.js";
|
|
10
10
|
export type { LanguageModelV1Middleware } from "ai";
|
|
11
11
|
export { MiddlewareFactory };
|
|
12
|
+
export { createLifecycleMiddleware } from "./builtin/lifecycle.js";
|
|
12
13
|
export default MiddlewareFactory;
|
package/dist/middleware/index.js
CHANGED
|
@@ -9,5 +9,7 @@
|
|
|
9
9
|
import { MiddlewareFactory } from "./factory.js";
|
|
10
10
|
// Factory for creating and applying middleware chains
|
|
11
11
|
export { MiddlewareFactory };
|
|
12
|
+
// Built-in middleware creators
|
|
13
|
+
export { createLifecycleMiddleware } from "./builtin/lifecycle.js";
|
|
12
14
|
// Export the factory as the default export for clean, direct usage
|
|
13
15
|
export default MiddlewareFactory;
|
package/dist/neurolink.js
CHANGED
|
@@ -2209,6 +2209,26 @@ Current user's request: ${currentInput}`;
|
|
|
2209
2209
|
},
|
|
2210
2210
|
});
|
|
2211
2211
|
}
|
|
2212
|
+
// Auto-inject lifecycle middleware when callbacks are provided
|
|
2213
|
+
// (must happen before workflow/PPT early returns so those paths get middleware too)
|
|
2214
|
+
if (options.onFinish || options.onError) {
|
|
2215
|
+
options.middleware = {
|
|
2216
|
+
...options.middleware,
|
|
2217
|
+
middlewareConfig: {
|
|
2218
|
+
...options.middleware?.middlewareConfig,
|
|
2219
|
+
lifecycle: {
|
|
2220
|
+
...options.middleware?.middlewareConfig?.lifecycle,
|
|
2221
|
+
enabled: true,
|
|
2222
|
+
config: {
|
|
2223
|
+
...options.middleware?.middlewareConfig?.lifecycle
|
|
2224
|
+
?.config,
|
|
2225
|
+
onFinish: options.onFinish,
|
|
2226
|
+
onError: options.onError,
|
|
2227
|
+
},
|
|
2228
|
+
},
|
|
2229
|
+
},
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2212
2232
|
// Check if workflow is requested
|
|
2213
2233
|
if (options.workflow || options.workflowConfig) {
|
|
2214
2234
|
return await this.generateWithWorkflow(options);
|
|
@@ -2364,6 +2384,7 @@ Current user's request: ${currentInput}`;
|
|
|
2364
2384
|
fileRegistry: this.fileRegistry,
|
|
2365
2385
|
abortSignal: options.abortSignal,
|
|
2366
2386
|
skipToolPromptInjection: options.skipToolPromptInjection,
|
|
2387
|
+
middleware: options.middleware,
|
|
2367
2388
|
};
|
|
2368
2389
|
// Auto-map top-level sessionId/userId to context for convenience
|
|
2369
2390
|
// Tests and users may pass sessionId/userId as top-level options
|
|
@@ -2400,7 +2421,6 @@ Current user's request: ${currentInput}`;
|
|
|
2400
2421
|
toolResults: toolResults.length,
|
|
2401
2422
|
});
|
|
2402
2423
|
}
|
|
2403
|
-
// Use redesigned generation logic
|
|
2404
2424
|
const textResult = await this.generateTextInternal(textOptions);
|
|
2405
2425
|
// Emit generation completion event (NeuroLink format - enhanced with content)
|
|
2406
2426
|
this.emitter.emit("generation:end", {
|
|
@@ -4024,6 +4044,26 @@ Current user's request: ${currentInput}`;
|
|
|
4024
4044
|
});
|
|
4025
4045
|
}
|
|
4026
4046
|
this.emitStreamStartEvents(options, startTime);
|
|
4047
|
+
// Auto-inject lifecycle middleware when callbacks are provided
|
|
4048
|
+
// (must happen before workflow early return so that path gets middleware too)
|
|
4049
|
+
if (options.onFinish || options.onError || options.onChunk) {
|
|
4050
|
+
options.middleware = {
|
|
4051
|
+
...options.middleware,
|
|
4052
|
+
middlewareConfig: {
|
|
4053
|
+
...options.middleware?.middlewareConfig,
|
|
4054
|
+
lifecycle: {
|
|
4055
|
+
...options.middleware?.middlewareConfig?.lifecycle,
|
|
4056
|
+
enabled: true,
|
|
4057
|
+
config: {
|
|
4058
|
+
...options.middleware?.middlewareConfig?.lifecycle?.config,
|
|
4059
|
+
onFinish: options.onFinish,
|
|
4060
|
+
onError: options.onError,
|
|
4061
|
+
onChunk: options.onChunk,
|
|
4062
|
+
},
|
|
4063
|
+
},
|
|
4064
|
+
},
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4027
4067
|
// Check if workflow is requested
|
|
4028
4068
|
if (options.workflow || options.workflowConfig) {
|
|
4029
4069
|
const result = await this.streamWithWorkflow(options, startTime);
|
|
@@ -6,7 +6,7 @@ import type { JsonValue } from "./common.js";
|
|
|
6
6
|
import type { Content, ImageWithAltText } from "./content.js";
|
|
7
7
|
import type { ChatMessage, ConversationMemoryConfig } from "./conversation.js";
|
|
8
8
|
import type { EvaluationData } from "./evaluation.js";
|
|
9
|
-
import type { MiddlewareFactoryOptions } from "./middlewareTypes.js";
|
|
9
|
+
import type { MiddlewareFactoryOptions, OnFinishCallback, OnErrorCallback } from "./middlewareTypes.js";
|
|
10
10
|
import type { DirectorModeOptions, DirectorSegment, VideoGenerationResult, VideoOutputOptions } from "./multimodal.js";
|
|
11
11
|
import type { PPTGenerationResult, PPTOutputOptions } from "./pptTypes.js";
|
|
12
12
|
import type { TTSOptions, TTSResult } from "./ttsTypes.js";
|
|
@@ -399,6 +399,12 @@ export type GenerateOptions = {
|
|
|
399
399
|
* @internal Set by NeuroLink SDK — not typically used directly by consumers.
|
|
400
400
|
*/
|
|
401
401
|
fileRegistry?: unknown;
|
|
402
|
+
/** Per-call middleware configuration. */
|
|
403
|
+
middleware?: import("./middlewareTypes.js").MiddlewareFactoryOptions;
|
|
404
|
+
/** Callback invoked when generation completes successfully. */
|
|
405
|
+
onFinish?: OnFinishCallback;
|
|
406
|
+
/** Callback invoked when generation encounters an error. */
|
|
407
|
+
onError?: OnErrorCallback;
|
|
402
408
|
};
|
|
403
409
|
/**
|
|
404
410
|
* Generate function result type - Primary output format
|
|
@@ -110,7 +110,7 @@ export type MiddlewareChainStats = {
|
|
|
110
110
|
/**
|
|
111
111
|
* Built-in middleware types
|
|
112
112
|
*/
|
|
113
|
-
export type BuiltInMiddlewareType = "analytics" | "guardrails" | "logging" | "caching" | "rateLimit" | "retry" | "timeout" | "autoEvaluation";
|
|
113
|
+
export type BuiltInMiddlewareType = "analytics" | "guardrails" | "logging" | "caching" | "rateLimit" | "retry" | "timeout" | "autoEvaluation" | "lifecycle";
|
|
114
114
|
/**
|
|
115
115
|
* Middleware preset configurations
|
|
116
116
|
*/
|
|
@@ -233,3 +233,56 @@ export type MiddlewareChainConfig = {
|
|
|
233
233
|
timeout?: number;
|
|
234
234
|
retries?: number;
|
|
235
235
|
};
|
|
236
|
+
/**
|
|
237
|
+
* Payload delivered to onFinish callbacks after generation or streaming completes.
|
|
238
|
+
*/
|
|
239
|
+
export type LifecycleFinishPayload = {
|
|
240
|
+
/** The generated text content */
|
|
241
|
+
text: string;
|
|
242
|
+
/** Token usage from the provider */
|
|
243
|
+
usage?: {
|
|
244
|
+
promptTokens: number;
|
|
245
|
+
completionTokens: number;
|
|
246
|
+
};
|
|
247
|
+
/** Wall-clock duration in milliseconds */
|
|
248
|
+
duration: number;
|
|
249
|
+
/** Why generation stopped */
|
|
250
|
+
finishReason?: string;
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Payload delivered to onError callbacks when generation or streaming fails.
|
|
254
|
+
*/
|
|
255
|
+
export type LifecycleErrorPayload = {
|
|
256
|
+
/** The error that occurred */
|
|
257
|
+
error: Error;
|
|
258
|
+
/** Wall-clock duration until failure in milliseconds */
|
|
259
|
+
duration: number;
|
|
260
|
+
/** Whether the error is likely recoverable (rate limit, timeout, network) */
|
|
261
|
+
recoverable: boolean;
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Payload delivered to onChunk callbacks for each streaming chunk.
|
|
265
|
+
*/
|
|
266
|
+
export type LifecycleChunkPayload = {
|
|
267
|
+
/** Chunk type from the AI SDK stream */
|
|
268
|
+
type: string;
|
|
269
|
+
/** Text content for text-delta chunks */
|
|
270
|
+
textDelta?: string;
|
|
271
|
+
/** Zero-based chunk sequence number */
|
|
272
|
+
sequenceNumber: number;
|
|
273
|
+
};
|
|
274
|
+
/** Callback invoked when generation or streaming finishes successfully. */
|
|
275
|
+
export type OnFinishCallback = (payload: LifecycleFinishPayload) => void | Promise<void>;
|
|
276
|
+
/** Callback invoked when generation or streaming encounters an error. */
|
|
277
|
+
export type OnErrorCallback = (payload: LifecycleErrorPayload) => void | Promise<void>;
|
|
278
|
+
/** Callback invoked for each chunk during streaming. */
|
|
279
|
+
export type OnChunkCallback = (payload: LifecycleChunkPayload) => void | Promise<void>;
|
|
280
|
+
/**
|
|
281
|
+
* Configuration for the lifecycle middleware.
|
|
282
|
+
* Pass callbacks to observe generation/streaming lifecycle events.
|
|
283
|
+
*/
|
|
284
|
+
export type LifecycleMiddlewareConfig = {
|
|
285
|
+
onFinish?: OnFinishCallback;
|
|
286
|
+
onError?: OnErrorCallback;
|
|
287
|
+
onChunk?: OnChunkCallback;
|
|
288
|
+
};
|
|
@@ -3,7 +3,7 @@ import type { AIProviderName } from "../constants/enums.js";
|
|
|
3
3
|
import type { EvaluationData } from "../index.js";
|
|
4
4
|
import type { RAGConfig } from "../rag/types.js";
|
|
5
5
|
import type { AnalyticsData, ToolExecutionEvent, ToolExecutionSummary } from "../types/index.js";
|
|
6
|
-
import type { MiddlewareFactoryOptions } from "../types/middlewareTypes.js";
|
|
6
|
+
import type { MiddlewareFactoryOptions, OnFinishCallback, OnErrorCallback, OnChunkCallback } from "../types/middlewareTypes.js";
|
|
7
7
|
import type { TokenUsage } from "./analytics.js";
|
|
8
8
|
import type { JsonValue, UnknownRecord } from "./common.js";
|
|
9
9
|
import type { Content, ImageWithAltText } from "./content.js";
|
|
@@ -409,6 +409,12 @@ export type StreamOptions = {
|
|
|
409
409
|
* @internal Set by NeuroLink SDK — not typically used directly by consumers.
|
|
410
410
|
*/
|
|
411
411
|
fileRegistry?: unknown;
|
|
412
|
+
/** Callback invoked when streaming completes successfully. */
|
|
413
|
+
onFinish?: OnFinishCallback;
|
|
414
|
+
/** Callback invoked when streaming encounters an error. */
|
|
415
|
+
onError?: OnErrorCallback;
|
|
416
|
+
/** Callback invoked for each streaming chunk. */
|
|
417
|
+
onChunk?: OnChunkCallback;
|
|
412
418
|
};
|
|
413
419
|
/**
|
|
414
420
|
* Stream function result type - Primary output format for streaming
|
|
@@ -258,6 +258,11 @@ export declare function isAbortError(error: unknown): boolean;
|
|
|
258
258
|
* Error handler that decides whether to retry based on error type
|
|
259
259
|
*/
|
|
260
260
|
export declare function isRetriableError(error: Error): boolean;
|
|
261
|
+
/**
|
|
262
|
+
* Determines if an error is likely recoverable (rate limit, timeout, network issues).
|
|
263
|
+
* Useful for deciding whether to retry or fail fast.
|
|
264
|
+
*/
|
|
265
|
+
export declare function isRecoverableError(error: Error): boolean;
|
|
261
266
|
/**
|
|
262
267
|
* Enhanced error logger that provides structured logging
|
|
263
268
|
*/
|
|
@@ -865,6 +865,44 @@ export function isRetriableError(error) {
|
|
|
865
865
|
];
|
|
866
866
|
return retriablePatterns.some((pattern) => pattern.test(error.message));
|
|
867
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* Determines if an error is likely recoverable (rate limit, timeout, network issues).
|
|
870
|
+
* Useful for deciding whether to retry or fail fast.
|
|
871
|
+
*/
|
|
872
|
+
export function isRecoverableError(error) {
|
|
873
|
+
// Check NeuroLinkError.retriable first
|
|
874
|
+
const errorWithRetriable = error;
|
|
875
|
+
if ("retriable" in error &&
|
|
876
|
+
typeof errorWithRetriable.retriable === "boolean") {
|
|
877
|
+
return errorWithRetriable.retriable;
|
|
878
|
+
}
|
|
879
|
+
const message = error.message?.toLowerCase() || "";
|
|
880
|
+
// Rate limit errors
|
|
881
|
+
if (message.includes("rate limit") || message.includes("too many requests")) {
|
|
882
|
+
return true;
|
|
883
|
+
}
|
|
884
|
+
if (/\b429\b/.test(message)) {
|
|
885
|
+
return true;
|
|
886
|
+
}
|
|
887
|
+
// Timeout errors
|
|
888
|
+
if (message.includes("timeout") ||
|
|
889
|
+
message.includes("etimedout") ||
|
|
890
|
+
message.includes("timed out")) {
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
// Network errors
|
|
894
|
+
if (message.includes("econnreset") ||
|
|
895
|
+
message.includes("econnrefused") ||
|
|
896
|
+
message.includes("network") ||
|
|
897
|
+
message.includes("socket")) {
|
|
898
|
+
return true;
|
|
899
|
+
}
|
|
900
|
+
// Server errors (use word boundaries to avoid false matches)
|
|
901
|
+
if (/\b50[0234]\b/.test(message)) {
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
868
906
|
/**
|
|
869
907
|
* Enhanced error logger that provides structured logging
|
|
870
908
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.30.0",
|
|
4
4
|
"description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 13 providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Juspay Technologies",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"test:mcp": "npx tsx test/continuous-test-suite-mcp-http.ts",
|
|
67
67
|
"test:media": "npx tsx test/continuous-test-suite-media-gen.ts",
|
|
68
68
|
"test:memory": "npx tsx test/continuous-test-suite-memory.ts",
|
|
69
|
+
"test:middleware": "npx tsx test/continuous-test-suite-middleware.ts",
|
|
69
70
|
"test:observability": "npx tsx test/continuous-test-suite-observability.ts",
|
|
70
71
|
"test:ppt": "npx tsx test/continuous-test-suite-ppt.ts",
|
|
71
72
|
"test:providers": "npx tsx test/continuous-test-suite-providers.ts",
|