@juspay/neurolink 9.24.0 → 9.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/adapters/tts/googleTTSHandler.js +26 -1
  3. package/dist/adapters/video/vertexVideoHandler.js +23 -17
  4. package/dist/cli/commands/config.d.ts +3 -3
  5. package/dist/cli/commands/observability.d.ts +53 -0
  6. package/dist/cli/commands/observability.js +453 -0
  7. package/dist/cli/commands/telemetry.d.ts +63 -0
  8. package/dist/cli/commands/telemetry.js +689 -0
  9. package/dist/cli/factories/commandFactory.js +29 -15
  10. package/dist/cli/parser.js +6 -9
  11. package/dist/cli/utils/formatters.d.ts +13 -0
  12. package/dist/cli/utils/formatters.js +23 -0
  13. package/dist/constants/contextWindows.js +6 -0
  14. package/dist/constants/enums.d.ts +6 -0
  15. package/dist/constants/enums.js +8 -2
  16. package/dist/context/budgetChecker.js +75 -48
  17. package/dist/context/contextCompactor.js +135 -127
  18. package/dist/core/baseProvider.d.ts +5 -0
  19. package/dist/core/baseProvider.js +158 -102
  20. package/dist/core/conversationMemoryInitializer.js +7 -4
  21. package/dist/core/conversationMemoryManager.d.ts +2 -0
  22. package/dist/core/conversationMemoryManager.js +6 -2
  23. package/dist/core/modules/GenerationHandler.d.ts +2 -2
  24. package/dist/core/modules/GenerationHandler.js +12 -12
  25. package/dist/evaluation/ragasEvaluator.js +39 -19
  26. package/dist/evaluation/scoring.js +46 -20
  27. package/dist/features/ppt/presentationOrchestrator.js +23 -0
  28. package/dist/features/ppt/slideGenerator.js +13 -0
  29. package/dist/features/ppt/slideRenderers.d.ts +1 -1
  30. package/dist/features/ppt/slideRenderers.js +6 -4
  31. package/dist/features/ppt/slideTypeInference.d.ts +1 -1
  32. package/dist/features/ppt/slideTypeInference.js +75 -73
  33. package/dist/files/fileTools.d.ts +6 -6
  34. package/dist/index.d.ts +46 -12
  35. package/dist/index.js +79 -17
  36. package/dist/lib/adapters/tts/googleTTSHandler.js +26 -1
  37. package/dist/lib/adapters/video/vertexVideoHandler.js +23 -17
  38. package/dist/lib/constants/contextWindows.js +6 -0
  39. package/dist/lib/constants/enums.d.ts +6 -0
  40. package/dist/lib/constants/enums.js +8 -2
  41. package/dist/lib/context/budgetChecker.js +75 -48
  42. package/dist/lib/context/contextCompactor.js +135 -127
  43. package/dist/lib/core/baseProvider.d.ts +5 -0
  44. package/dist/lib/core/baseProvider.js +158 -102
  45. package/dist/lib/core/conversationMemoryInitializer.js +7 -4
  46. package/dist/lib/core/conversationMemoryManager.d.ts +2 -0
  47. package/dist/lib/core/conversationMemoryManager.js +6 -2
  48. package/dist/lib/core/modules/GenerationHandler.d.ts +2 -2
  49. package/dist/lib/core/modules/GenerationHandler.js +12 -12
  50. package/dist/lib/evaluation/ragasEvaluator.js +39 -19
  51. package/dist/lib/evaluation/scoring.js +46 -20
  52. package/dist/lib/features/ppt/presentationOrchestrator.js +23 -0
  53. package/dist/lib/features/ppt/slideGenerator.js +13 -0
  54. package/dist/lib/features/ppt/slideRenderers.d.ts +1 -1
  55. package/dist/lib/features/ppt/slideRenderers.js +6 -4
  56. package/dist/lib/features/ppt/slideTypeInference.d.ts +1 -1
  57. package/dist/lib/features/ppt/slideTypeInference.js +75 -73
  58. package/dist/lib/files/fileTools.d.ts +6 -6
  59. package/dist/lib/index.d.ts +46 -12
  60. package/dist/lib/index.js +79 -17
  61. package/dist/lib/mcp/httpRateLimiter.js +39 -12
  62. package/dist/lib/mcp/httpRetryHandler.js +22 -1
  63. package/dist/lib/mcp/mcpClientFactory.js +13 -15
  64. package/dist/lib/memory/memoryRetrievalTools.js +22 -0
  65. package/dist/lib/neurolink.d.ts +64 -72
  66. package/dist/lib/neurolink.js +984 -566
  67. package/dist/lib/observability/exporterRegistry.d.ts +152 -0
  68. package/dist/lib/observability/exporterRegistry.js +414 -0
  69. package/dist/lib/observability/exporters/arizeExporter.d.ts +32 -0
  70. package/dist/lib/observability/exporters/arizeExporter.js +139 -0
  71. package/dist/lib/observability/exporters/baseExporter.d.ts +117 -0
  72. package/dist/lib/observability/exporters/baseExporter.js +191 -0
  73. package/dist/lib/observability/exporters/braintrustExporter.d.ts +30 -0
  74. package/dist/lib/observability/exporters/braintrustExporter.js +155 -0
  75. package/dist/lib/observability/exporters/datadogExporter.d.ts +37 -0
  76. package/dist/lib/observability/exporters/datadogExporter.js +197 -0
  77. package/dist/lib/observability/exporters/index.d.ts +13 -0
  78. package/dist/lib/observability/exporters/index.js +14 -0
  79. package/dist/lib/observability/exporters/laminarExporter.d.ts +48 -0
  80. package/dist/lib/observability/exporters/laminarExporter.js +303 -0
  81. package/dist/lib/observability/exporters/langfuseExporter.d.ts +47 -0
  82. package/dist/lib/observability/exporters/langfuseExporter.js +200 -0
  83. package/dist/lib/observability/exporters/langsmithExporter.d.ts +26 -0
  84. package/dist/lib/observability/exporters/langsmithExporter.js +124 -0
  85. package/dist/lib/observability/exporters/otelExporter.d.ts +39 -0
  86. package/dist/lib/observability/exporters/otelExporter.js +165 -0
  87. package/dist/lib/observability/exporters/posthogExporter.d.ts +48 -0
  88. package/dist/lib/observability/exporters/posthogExporter.js +288 -0
  89. package/dist/lib/observability/exporters/sentryExporter.d.ts +32 -0
  90. package/dist/lib/observability/exporters/sentryExporter.js +166 -0
  91. package/dist/lib/observability/index.d.ts +25 -0
  92. package/dist/lib/observability/index.js +32 -0
  93. package/dist/lib/observability/metricsAggregator.d.ts +260 -0
  94. package/dist/lib/observability/metricsAggregator.js +553 -0
  95. package/dist/lib/observability/otelBridge.d.ts +49 -0
  96. package/dist/lib/observability/otelBridge.js +132 -0
  97. package/dist/lib/observability/retryPolicy.d.ts +192 -0
  98. package/dist/lib/observability/retryPolicy.js +384 -0
  99. package/dist/lib/observability/sampling/index.d.ts +4 -0
  100. package/dist/lib/observability/sampling/index.js +5 -0
  101. package/dist/lib/observability/sampling/samplers.d.ts +116 -0
  102. package/dist/lib/observability/sampling/samplers.js +217 -0
  103. package/dist/lib/observability/spanProcessor.d.ts +129 -0
  104. package/dist/lib/observability/spanProcessor.js +288 -0
  105. package/dist/lib/observability/tokenTracker.d.ts +156 -0
  106. package/dist/lib/observability/tokenTracker.js +414 -0
  107. package/dist/lib/observability/types/exporterTypes.d.ts +250 -0
  108. package/dist/lib/observability/types/exporterTypes.js +6 -0
  109. package/dist/lib/observability/types/index.d.ts +6 -0
  110. package/dist/lib/observability/types/index.js +5 -0
  111. package/dist/lib/observability/types/spanTypes.d.ts +244 -0
  112. package/dist/lib/observability/types/spanTypes.js +93 -0
  113. package/dist/lib/observability/utils/index.d.ts +4 -0
  114. package/dist/lib/observability/utils/index.js +5 -0
  115. package/dist/lib/observability/utils/spanSerializer.d.ts +115 -0
  116. package/dist/lib/observability/utils/spanSerializer.js +287 -0
  117. package/dist/lib/providers/amazonSagemaker.d.ts +5 -4
  118. package/dist/lib/providers/amazonSagemaker.js +3 -4
  119. package/dist/lib/providers/googleVertex.d.ts +7 -0
  120. package/dist/lib/providers/googleVertex.js +80 -2
  121. package/dist/lib/rag/pipeline/RAGPipeline.d.ts +0 -5
  122. package/dist/lib/rag/pipeline/RAGPipeline.js +122 -87
  123. package/dist/lib/rag/ragIntegration.js +30 -0
  124. package/dist/lib/rag/retrieval/hybridSearch.js +22 -0
  125. package/dist/lib/server/abstract/baseServerAdapter.js +51 -19
  126. package/dist/lib/server/middleware/common.js +44 -12
  127. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +2 -2
  128. package/dist/lib/services/server/ai/observability/instrumentation.js +10 -5
  129. package/dist/lib/types/conversationMemoryInterface.d.ts +2 -0
  130. package/dist/lib/types/modelTypes.d.ts +18 -18
  131. package/dist/lib/types/providers.d.ts +5 -0
  132. package/dist/lib/utils/pricing.js +25 -1
  133. package/dist/lib/utils/ttsProcessor.js +74 -59
  134. package/dist/lib/workflow/config.d.ts +36 -36
  135. package/dist/lib/workflow/core/ensembleExecutor.js +10 -0
  136. package/dist/lib/workflow/core/judgeScorer.js +20 -2
  137. package/dist/lib/workflow/core/workflowRunner.js +34 -1
  138. package/dist/mcp/httpRateLimiter.js +39 -12
  139. package/dist/mcp/httpRetryHandler.js +22 -1
  140. package/dist/mcp/mcpClientFactory.js +13 -15
  141. package/dist/memory/memoryRetrievalTools.js +22 -0
  142. package/dist/neurolink.d.ts +64 -72
  143. package/dist/neurolink.js +984 -566
  144. package/dist/observability/FEATURE-STATUS.md +269 -0
  145. package/dist/observability/exporterRegistry.d.ts +152 -0
  146. package/dist/observability/exporterRegistry.js +413 -0
  147. package/dist/observability/exporters/arizeExporter.d.ts +32 -0
  148. package/dist/observability/exporters/arizeExporter.js +138 -0
  149. package/dist/observability/exporters/baseExporter.d.ts +117 -0
  150. package/dist/observability/exporters/baseExporter.js +190 -0
  151. package/dist/observability/exporters/braintrustExporter.d.ts +30 -0
  152. package/dist/observability/exporters/braintrustExporter.js +154 -0
  153. package/dist/observability/exporters/datadogExporter.d.ts +37 -0
  154. package/dist/observability/exporters/datadogExporter.js +196 -0
  155. package/dist/observability/exporters/index.d.ts +13 -0
  156. package/dist/observability/exporters/index.js +13 -0
  157. package/dist/observability/exporters/laminarExporter.d.ts +48 -0
  158. package/dist/observability/exporters/laminarExporter.js +302 -0
  159. package/dist/observability/exporters/langfuseExporter.d.ts +47 -0
  160. package/dist/observability/exporters/langfuseExporter.js +199 -0
  161. package/dist/observability/exporters/langsmithExporter.d.ts +26 -0
  162. package/dist/observability/exporters/langsmithExporter.js +123 -0
  163. package/dist/observability/exporters/otelExporter.d.ts +39 -0
  164. package/dist/observability/exporters/otelExporter.js +164 -0
  165. package/dist/observability/exporters/posthogExporter.d.ts +48 -0
  166. package/dist/observability/exporters/posthogExporter.js +287 -0
  167. package/dist/observability/exporters/sentryExporter.d.ts +32 -0
  168. package/dist/observability/exporters/sentryExporter.js +165 -0
  169. package/dist/observability/index.d.ts +25 -0
  170. package/dist/observability/index.js +31 -0
  171. package/dist/observability/metricsAggregator.d.ts +260 -0
  172. package/dist/observability/metricsAggregator.js +552 -0
  173. package/dist/observability/otelBridge.d.ts +49 -0
  174. package/dist/observability/otelBridge.js +131 -0
  175. package/dist/observability/retryPolicy.d.ts +192 -0
  176. package/dist/observability/retryPolicy.js +383 -0
  177. package/dist/observability/sampling/index.d.ts +4 -0
  178. package/dist/observability/sampling/index.js +4 -0
  179. package/dist/observability/sampling/samplers.d.ts +116 -0
  180. package/dist/observability/sampling/samplers.js +216 -0
  181. package/dist/observability/spanProcessor.d.ts +129 -0
  182. package/dist/observability/spanProcessor.js +287 -0
  183. package/dist/observability/tokenTracker.d.ts +156 -0
  184. package/dist/observability/tokenTracker.js +413 -0
  185. package/dist/observability/types/exporterTypes.d.ts +250 -0
  186. package/dist/observability/types/exporterTypes.js +5 -0
  187. package/dist/observability/types/index.d.ts +6 -0
  188. package/dist/observability/types/index.js +4 -0
  189. package/dist/observability/types/spanTypes.d.ts +244 -0
  190. package/dist/observability/types/spanTypes.js +92 -0
  191. package/dist/observability/utils/index.d.ts +4 -0
  192. package/dist/observability/utils/index.js +4 -0
  193. package/dist/observability/utils/spanSerializer.d.ts +115 -0
  194. package/dist/observability/utils/spanSerializer.js +286 -0
  195. package/dist/providers/amazonSagemaker.d.ts +5 -4
  196. package/dist/providers/amazonSagemaker.js +3 -4
  197. package/dist/providers/googleVertex.d.ts +7 -0
  198. package/dist/providers/googleVertex.js +80 -2
  199. package/dist/rag/pipeline/RAGPipeline.d.ts +0 -5
  200. package/dist/rag/pipeline/RAGPipeline.js +122 -87
  201. package/dist/rag/ragIntegration.js +30 -0
  202. package/dist/rag/retrieval/hybridSearch.js +22 -0
  203. package/dist/server/abstract/baseServerAdapter.js +51 -19
  204. package/dist/server/middleware/common.js +44 -12
  205. package/dist/services/server/ai/observability/instrumentation.d.ts +2 -2
  206. package/dist/services/server/ai/observability/instrumentation.js +10 -5
  207. package/dist/types/conversationMemoryInterface.d.ts +2 -0
  208. package/dist/types/providers.d.ts +5 -0
  209. package/dist/utils/pricing.js +25 -1
  210. package/dist/utils/ttsProcessor.js +74 -59
  211. package/dist/workflow/config.d.ts +52 -52
  212. package/dist/workflow/core/ensembleExecutor.js +10 -0
  213. package/dist/workflow/core/judgeScorer.js +20 -2
  214. package/dist/workflow/core/workflowRunner.js +34 -1
  215. package/package.json +1 -1
@@ -0,0 +1,131 @@
1
+ /**
2
+ * OpenTelemetry Bridge
3
+ * Bidirectional context propagation between NeuroLink and OpenTelemetry
4
+ */
5
+ import { context, propagation, trace, } from "@opentelemetry/api";
6
+ import { SpanStatus } from "./types/spanTypes.js";
7
+ import { SpanSerializer } from "./utils/spanSerializer.js";
8
+ /**
9
+ * Bridge for bidirectional context propagation between
10
+ * NeuroLink's observability system and OpenTelemetry
11
+ */
12
+ export class OtelBridge {
13
+ tracer = trace.getTracer("neurolink-bridge");
14
+ /**
15
+ * Extract trace context from incoming request headers
16
+ */
17
+ extractContext(headers) {
18
+ const extractedContext = propagation.extract(context.active(), headers);
19
+ const spanContext = trace.getSpanContext(extractedContext);
20
+ return spanContext ?? null;
21
+ }
22
+ /**
23
+ * Inject trace context into outgoing request headers
24
+ */
25
+ injectContext(headers) {
26
+ propagation.inject(context.active(), headers);
27
+ return headers;
28
+ }
29
+ /**
30
+ * Create a NeuroLink span from OpenTelemetry context
31
+ */
32
+ createSpanFromOtelContext(spanContext, type, name) {
33
+ return SpanSerializer.createSpan(type, name, {}, undefined, spanContext.traceId);
34
+ }
35
+ /**
36
+ * Wrap a function with OpenTelemetry tracing that also creates NeuroLink spans
37
+ */
38
+ async wrapWithTracing(name, type, fn, onSpanEnd) {
39
+ const otelSpan = this.tracer.startSpan(name);
40
+ const neuroLinkSpan = SpanSerializer.createSpan(type, name, {}, undefined, otelSpan.spanContext().traceId);
41
+ try {
42
+ const result = await context.with(trace.setSpan(context.active(), otelSpan), () => fn(neuroLinkSpan));
43
+ const endedSpan = SpanSerializer.endSpan(neuroLinkSpan, SpanStatus.OK);
44
+ otelSpan.setStatus({ code: 1 }); // OK
45
+ if (onSpanEnd) {
46
+ onSpanEnd(endedSpan);
47
+ }
48
+ return result;
49
+ }
50
+ catch (error) {
51
+ const endedSpan = SpanSerializer.endSpan(neuroLinkSpan, SpanStatus.ERROR, error instanceof Error ? error.message : String(error));
52
+ otelSpan.setStatus({
53
+ code: 2, // ERROR
54
+ message: error instanceof Error ? error.message : String(error),
55
+ });
56
+ otelSpan.recordException(error);
57
+ if (onSpanEnd) {
58
+ onSpanEnd(endedSpan);
59
+ }
60
+ throw error;
61
+ }
62
+ finally {
63
+ otelSpan.end();
64
+ }
65
+ }
66
+ /**
67
+ * Convert NeuroLink span to OpenTelemetry span and export
68
+ */
69
+ exportToOtel(span) {
70
+ const otelSpan = this.tracer.startSpan(span.name, {
71
+ startTime: new Date(span.startTime),
72
+ attributes: this.filterAttributes(span.attributes),
73
+ });
74
+ // Add events
75
+ for (const event of span.events) {
76
+ otelSpan.addEvent(event.name, this.filterEventAttributes(event.attributes), new Date(event.timestamp));
77
+ }
78
+ // Set status (map NeuroLink status to OTel status)
79
+ const otelStatusCode = span.status === SpanStatus.ERROR ? 2 : 1;
80
+ otelSpan.setStatus({
81
+ code: otelStatusCode,
82
+ message: span.statusMessage,
83
+ });
84
+ // End span
85
+ if (span.endTime) {
86
+ otelSpan.end(new Date(span.endTime));
87
+ }
88
+ else {
89
+ otelSpan.end();
90
+ }
91
+ }
92
+ /**
93
+ * Get current trace context for correlation
94
+ */
95
+ getCurrentTraceContext() {
96
+ const spanContext = trace.getActiveSpan()?.spanContext();
97
+ if (!spanContext) {
98
+ return null;
99
+ }
100
+ return {
101
+ traceId: spanContext.traceId,
102
+ spanId: spanContext.spanId,
103
+ };
104
+ }
105
+ /**
106
+ * Filter attributes to only include OTel-compatible types
107
+ */
108
+ filterAttributes(attrs) {
109
+ const result = {};
110
+ for (const [key, value] of Object.entries(attrs)) {
111
+ if (typeof value === "string" ||
112
+ typeof value === "number" ||
113
+ typeof value === "boolean") {
114
+ result[key] = value;
115
+ }
116
+ else if (value !== undefined && value !== null) {
117
+ result[key] = JSON.stringify(value);
118
+ }
119
+ }
120
+ return result;
121
+ }
122
+ /**
123
+ * Filter event attributes
124
+ */
125
+ filterEventAttributes(attrs) {
126
+ if (!attrs) {
127
+ return undefined;
128
+ }
129
+ return this.filterAttributes(attrs);
130
+ }
131
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Retry Policy
3
+ * Configurable retry strategies for observability exporters
4
+ */
5
+ /**
6
+ * Result of a retry decision
7
+ */
8
+ export type RetryDecision = {
9
+ /** Whether to retry */
10
+ shouldRetry: boolean;
11
+ /** Delay before retry in milliseconds */
12
+ delayMs: number;
13
+ /** Reason for the decision */
14
+ reason: string;
15
+ };
16
+ /**
17
+ * Context for retry decision making
18
+ */
19
+ export type RetryContext = {
20
+ /** Current attempt number (0-indexed) */
21
+ attempt: number;
22
+ /** The error that triggered the retry */
23
+ error: Error;
24
+ /** Total elapsed time since first attempt */
25
+ elapsedMs: number;
26
+ /** Operation name for logging */
27
+ operationName: string;
28
+ /** Additional metadata */
29
+ metadata?: Record<string, unknown>;
30
+ };
31
+ /**
32
+ * Retry policy interface
33
+ */
34
+ export interface RetryPolicy {
35
+ /** Policy name for identification */
36
+ readonly name: string;
37
+ /** Decide whether to retry */
38
+ shouldRetry(context: RetryContext): RetryDecision;
39
+ /** Maximum attempts allowed */
40
+ readonly maxAttempts: number;
41
+ /** Maximum total time allowed for retries */
42
+ readonly maxTotalTimeMs: number;
43
+ }
44
+ /**
45
+ * Base retry policy with common configuration
46
+ */
47
+ export declare abstract class BaseRetryPolicy implements RetryPolicy {
48
+ abstract readonly name: string;
49
+ readonly maxAttempts: number;
50
+ readonly maxTotalTimeMs: number;
51
+ protected readonly retryableErrors: Set<string>;
52
+ protected readonly nonRetryableErrors: Set<string>;
53
+ constructor(config: {
54
+ maxAttempts?: number;
55
+ maxTotalTimeMs?: number;
56
+ retryableErrors?: string[];
57
+ nonRetryableErrors?: string[];
58
+ });
59
+ abstract shouldRetry(context: RetryContext): RetryDecision;
60
+ protected isRetryableError(error: Error): boolean;
61
+ protected extractErrorCode(error: Error): string;
62
+ }
63
+ /**
64
+ * Exponential backoff retry policy
65
+ * Delay increases exponentially with each attempt
66
+ */
67
+ export declare class ExponentialBackoffPolicy extends BaseRetryPolicy {
68
+ readonly name = "exponential-backoff";
69
+ private readonly baseDelayMs;
70
+ private readonly maxDelayMs;
71
+ private readonly jitterFactor;
72
+ constructor(config?: {
73
+ maxAttempts?: number;
74
+ maxTotalTimeMs?: number;
75
+ baseDelayMs?: number;
76
+ maxDelayMs?: number;
77
+ jitterFactor?: number;
78
+ retryableErrors?: string[];
79
+ nonRetryableErrors?: string[];
80
+ });
81
+ shouldRetry(context: RetryContext): RetryDecision;
82
+ }
83
+ /**
84
+ * Linear backoff retry policy
85
+ * Delay increases linearly with each attempt
86
+ */
87
+ export declare class LinearBackoffPolicy extends BaseRetryPolicy {
88
+ readonly name = "linear-backoff";
89
+ private readonly delayIncrementMs;
90
+ private readonly initialDelayMs;
91
+ constructor(config?: {
92
+ maxAttempts?: number;
93
+ maxTotalTimeMs?: number;
94
+ initialDelayMs?: number;
95
+ delayIncrementMs?: number;
96
+ retryableErrors?: string[];
97
+ nonRetryableErrors?: string[];
98
+ });
99
+ shouldRetry(context: RetryContext): RetryDecision;
100
+ }
101
+ /**
102
+ * Fixed delay retry policy
103
+ * Same delay for all retry attempts
104
+ */
105
+ export declare class FixedDelayPolicy extends BaseRetryPolicy {
106
+ readonly name = "fixed-delay";
107
+ private readonly delayMs;
108
+ constructor(config?: {
109
+ maxAttempts?: number;
110
+ maxTotalTimeMs?: number;
111
+ delayMs?: number;
112
+ retryableErrors?: string[];
113
+ nonRetryableErrors?: string[];
114
+ });
115
+ shouldRetry(context: RetryContext): RetryDecision;
116
+ }
117
+ /**
118
+ * No retry policy - never retries
119
+ */
120
+ export declare class NoRetryPolicy implements RetryPolicy {
121
+ readonly name = "no-retry";
122
+ readonly maxAttempts = 1;
123
+ readonly maxTotalTimeMs = 0;
124
+ shouldRetry(_context: RetryContext): RetryDecision;
125
+ }
126
+ /**
127
+ * Circuit breaker aware retry policy
128
+ * Works with circuit breaker state to prevent retries when circuit is open
129
+ */
130
+ export declare class CircuitBreakerAwarePolicy extends BaseRetryPolicy {
131
+ readonly name = "circuit-breaker-aware";
132
+ private readonly innerPolicy;
133
+ private failures;
134
+ private lastFailure;
135
+ private circuitState;
136
+ private readonly failureThreshold;
137
+ private readonly resetTimeoutMs;
138
+ constructor(config?: {
139
+ innerPolicy?: RetryPolicy;
140
+ failureThreshold?: number;
141
+ resetTimeoutMs?: number;
142
+ maxAttempts?: number;
143
+ maxTotalTimeMs?: number;
144
+ retryableErrors?: string[];
145
+ nonRetryableErrors?: string[];
146
+ });
147
+ shouldRetry(context: RetryContext): RetryDecision;
148
+ recordFailure(): void;
149
+ recordSuccess(): void;
150
+ getCircuitState(): "closed" | "open" | "half-open";
151
+ reset(): void;
152
+ }
153
+ /**
154
+ * Retry executor - executes operations with retry policy
155
+ */
156
+ export declare class RetryExecutor {
157
+ private readonly policy;
158
+ constructor(policy: RetryPolicy);
159
+ /**
160
+ * Execute an operation with retry
161
+ * @param operation - The async operation to execute
162
+ * @param operationName - Name for logging
163
+ * @returns The result of the operation
164
+ * @throws The last error if all retries fail
165
+ */
166
+ execute<T>(operation: () => Promise<T>, operationName: string): Promise<T>;
167
+ private delay;
168
+ }
169
+ /**
170
+ * Factory for creating retry policies
171
+ */
172
+ export declare class RetryPolicyFactory {
173
+ /**
174
+ * Create a default policy for production use
175
+ */
176
+ static createDefault(): ExponentialBackoffPolicy;
177
+ /**
178
+ * Create an aggressive policy for critical operations
179
+ */
180
+ static createAggressive(): ExponentialBackoffPolicy;
181
+ /**
182
+ * Create a conservative policy for non-critical operations
183
+ */
184
+ static createConservative(): LinearBackoffPolicy;
185
+ /**
186
+ * Create a circuit breaker aware policy
187
+ */
188
+ static createWithCircuitBreaker(config?: {
189
+ failureThreshold?: number;
190
+ resetTimeoutMs?: number;
191
+ }): CircuitBreakerAwarePolicy;
192
+ }
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Retry Policy
3
+ * Configurable retry strategies for observability exporters
4
+ */
5
+ import { logger } from "../utils/logger.js";
6
+ /**
7
+ * Base retry policy with common configuration
8
+ */
9
+ export class BaseRetryPolicy {
10
+ maxAttempts;
11
+ maxTotalTimeMs;
12
+ retryableErrors;
13
+ nonRetryableErrors;
14
+ constructor(config) {
15
+ this.maxAttempts = config.maxAttempts ?? 3;
16
+ this.maxTotalTimeMs = config.maxTotalTimeMs ?? 60000; // 1 minute
17
+ this.retryableErrors = new Set(config.retryableErrors ?? [
18
+ "ECONNRESET",
19
+ "ETIMEDOUT",
20
+ "ECONNREFUSED",
21
+ "EPIPE",
22
+ "ENOTFOUND",
23
+ "ENETUNREACH",
24
+ "EAI_AGAIN",
25
+ "429", // Rate limit
26
+ "500", // Internal server error
27
+ "502", // Bad gateway
28
+ "503", // Service unavailable
29
+ "504", // Gateway timeout
30
+ ]);
31
+ this.nonRetryableErrors = new Set(config.nonRetryableErrors ?? [
32
+ "400", // Bad request
33
+ "401", // Unauthorized
34
+ "403", // Forbidden
35
+ "404", // Not found
36
+ "422", // Unprocessable entity
37
+ ]);
38
+ }
39
+ isRetryableError(error) {
40
+ const errorCode = this.extractErrorCode(error);
41
+ // Check if explicitly non-retryable
42
+ if (this.nonRetryableErrors.has(errorCode)) {
43
+ return false;
44
+ }
45
+ // Check if explicitly retryable
46
+ if (this.retryableErrors.has(errorCode)) {
47
+ return true;
48
+ }
49
+ // Default: retry on network-like errors
50
+ return (error.message.includes("timeout") ||
51
+ error.message.includes("ECONNRESET") ||
52
+ error.message.includes("network"));
53
+ }
54
+ extractErrorCode(error) {
55
+ // Check for HTTP status code in error
56
+ const httpMatch = error.message.match(/(\d{3})/);
57
+ if (httpMatch) {
58
+ return httpMatch[1];
59
+ }
60
+ // Check for Node.js error code
61
+ const nodeError = error;
62
+ if (nodeError.code) {
63
+ return nodeError.code;
64
+ }
65
+ return "UNKNOWN";
66
+ }
67
+ }
68
+ /**
69
+ * Exponential backoff retry policy
70
+ * Delay increases exponentially with each attempt
71
+ */
72
+ export class ExponentialBackoffPolicy extends BaseRetryPolicy {
73
+ name = "exponential-backoff";
74
+ baseDelayMs;
75
+ maxDelayMs;
76
+ jitterFactor;
77
+ constructor(config) {
78
+ super(config ?? {});
79
+ this.baseDelayMs = config?.baseDelayMs ?? 1000;
80
+ this.maxDelayMs = config?.maxDelayMs ?? 30000;
81
+ this.jitterFactor = config?.jitterFactor ?? 0.1;
82
+ }
83
+ shouldRetry(context) {
84
+ // Check max attempts
85
+ if (context.attempt >= this.maxAttempts) {
86
+ return {
87
+ shouldRetry: false,
88
+ delayMs: 0,
89
+ reason: `Max attempts (${this.maxAttempts}) exceeded`,
90
+ };
91
+ }
92
+ // Check max total time
93
+ if (context.elapsedMs >= this.maxTotalTimeMs) {
94
+ return {
95
+ shouldRetry: false,
96
+ delayMs: 0,
97
+ reason: `Max total time (${this.maxTotalTimeMs}ms) exceeded`,
98
+ };
99
+ }
100
+ // Check if error is retryable
101
+ if (!this.isRetryableError(context.error)) {
102
+ return {
103
+ shouldRetry: false,
104
+ delayMs: 0,
105
+ reason: `Error not retryable: ${context.error.message}`,
106
+ };
107
+ }
108
+ // Calculate delay with exponential backoff
109
+ const exponentialDelay = this.baseDelayMs * 2 ** context.attempt;
110
+ const cappedDelay = Math.min(exponentialDelay, this.maxDelayMs);
111
+ // Add jitter
112
+ const jitter = cappedDelay * this.jitterFactor * (Math.random() * 2 - 1);
113
+ const delayMs = Math.max(0, Math.round(cappedDelay + jitter));
114
+ return {
115
+ shouldRetry: true,
116
+ delayMs,
117
+ reason: `Retrying after ${delayMs}ms (attempt ${context.attempt + 1}/${this.maxAttempts})`,
118
+ };
119
+ }
120
+ }
121
+ /**
122
+ * Linear backoff retry policy
123
+ * Delay increases linearly with each attempt
124
+ */
125
+ export class LinearBackoffPolicy extends BaseRetryPolicy {
126
+ name = "linear-backoff";
127
+ delayIncrementMs;
128
+ initialDelayMs;
129
+ constructor(config) {
130
+ super(config ?? {});
131
+ this.initialDelayMs = config?.initialDelayMs ?? 1000;
132
+ this.delayIncrementMs = config?.delayIncrementMs ?? 1000;
133
+ }
134
+ shouldRetry(context) {
135
+ if (context.attempt >= this.maxAttempts) {
136
+ return {
137
+ shouldRetry: false,
138
+ delayMs: 0,
139
+ reason: `Max attempts (${this.maxAttempts}) exceeded`,
140
+ };
141
+ }
142
+ if (context.elapsedMs >= this.maxTotalTimeMs) {
143
+ return {
144
+ shouldRetry: false,
145
+ delayMs: 0,
146
+ reason: `Max total time (${this.maxTotalTimeMs}ms) exceeded`,
147
+ };
148
+ }
149
+ if (!this.isRetryableError(context.error)) {
150
+ return {
151
+ shouldRetry: false,
152
+ delayMs: 0,
153
+ reason: `Error not retryable: ${context.error.message}`,
154
+ };
155
+ }
156
+ const delayMs = this.initialDelayMs + context.attempt * this.delayIncrementMs;
157
+ return {
158
+ shouldRetry: true,
159
+ delayMs,
160
+ reason: `Retrying after ${delayMs}ms (attempt ${context.attempt + 1}/${this.maxAttempts})`,
161
+ };
162
+ }
163
+ }
164
+ /**
165
+ * Fixed delay retry policy
166
+ * Same delay for all retry attempts
167
+ */
168
+ export class FixedDelayPolicy extends BaseRetryPolicy {
169
+ name = "fixed-delay";
170
+ delayMs;
171
+ constructor(config) {
172
+ super(config ?? {});
173
+ this.delayMs = config?.delayMs ?? 1000;
174
+ }
175
+ shouldRetry(context) {
176
+ if (context.attempt >= this.maxAttempts) {
177
+ return {
178
+ shouldRetry: false,
179
+ delayMs: 0,
180
+ reason: `Max attempts (${this.maxAttempts}) exceeded`,
181
+ };
182
+ }
183
+ if (context.elapsedMs >= this.maxTotalTimeMs) {
184
+ return {
185
+ shouldRetry: false,
186
+ delayMs: 0,
187
+ reason: `Max total time (${this.maxTotalTimeMs}ms) exceeded`,
188
+ };
189
+ }
190
+ if (!this.isRetryableError(context.error)) {
191
+ return {
192
+ shouldRetry: false,
193
+ delayMs: 0,
194
+ reason: `Error not retryable: ${context.error.message}`,
195
+ };
196
+ }
197
+ return {
198
+ shouldRetry: true,
199
+ delayMs: this.delayMs,
200
+ reason: `Retrying after ${this.delayMs}ms (attempt ${context.attempt + 1}/${this.maxAttempts})`,
201
+ };
202
+ }
203
+ }
204
+ /**
205
+ * No retry policy - never retries
206
+ */
207
+ export class NoRetryPolicy {
208
+ name = "no-retry";
209
+ maxAttempts = 1;
210
+ maxTotalTimeMs = 0;
211
+ shouldRetry(_context) {
212
+ return {
213
+ shouldRetry: false,
214
+ delayMs: 0,
215
+ reason: "Retry disabled",
216
+ };
217
+ }
218
+ }
219
+ /**
220
+ * Circuit breaker aware retry policy
221
+ * Works with circuit breaker state to prevent retries when circuit is open
222
+ */
223
+ export class CircuitBreakerAwarePolicy extends BaseRetryPolicy {
224
+ name = "circuit-breaker-aware";
225
+ innerPolicy;
226
+ failures = 0;
227
+ lastFailure = 0;
228
+ circuitState = "closed";
229
+ failureThreshold;
230
+ resetTimeoutMs;
231
+ constructor(config) {
232
+ super(config ?? {});
233
+ this.innerPolicy = config?.innerPolicy ?? new ExponentialBackoffPolicy();
234
+ this.failureThreshold = config?.failureThreshold ?? 5;
235
+ this.resetTimeoutMs = config?.resetTimeoutMs ?? 30000;
236
+ }
237
+ shouldRetry(context) {
238
+ // Check circuit state
239
+ if (this.circuitState === "open") {
240
+ if (Date.now() - this.lastFailure > this.resetTimeoutMs) {
241
+ this.circuitState = "half-open";
242
+ logger.info(`[${this.name}] Circuit half-open, allowing probe request`);
243
+ }
244
+ else {
245
+ return {
246
+ shouldRetry: false,
247
+ delayMs: 0,
248
+ reason: "Circuit breaker open",
249
+ };
250
+ }
251
+ }
252
+ // Delegate to inner policy
253
+ const decision = this.innerPolicy.shouldRetry(context);
254
+ // Record result for circuit breaker
255
+ if (!decision.shouldRetry) {
256
+ this.recordFailure();
257
+ }
258
+ return decision;
259
+ }
260
+ recordFailure() {
261
+ this.failures++;
262
+ this.lastFailure = Date.now();
263
+ if (this.failures >= this.failureThreshold) {
264
+ this.circuitState = "open";
265
+ logger.warn(`[${this.name}] Circuit opened after ${this.failures} failures`);
266
+ }
267
+ }
268
+ recordSuccess() {
269
+ if (this.circuitState === "half-open") {
270
+ logger.info(`[${this.name}] Circuit closed after successful request`);
271
+ }
272
+ this.failures = 0;
273
+ this.circuitState = "closed";
274
+ }
275
+ getCircuitState() {
276
+ return this.circuitState;
277
+ }
278
+ reset() {
279
+ this.failures = 0;
280
+ this.circuitState = "closed";
281
+ this.lastFailure = 0;
282
+ }
283
+ }
284
+ /**
285
+ * Retry executor - executes operations with retry policy
286
+ */
287
+ export class RetryExecutor {
288
+ policy;
289
+ constructor(policy) {
290
+ this.policy = policy;
291
+ }
292
+ /**
293
+ * Execute an operation with retry
294
+ * @param operation - The async operation to execute
295
+ * @param operationName - Name for logging
296
+ * @returns The result of the operation
297
+ * @throws The last error if all retries fail
298
+ */
299
+ async execute(operation, operationName) {
300
+ const startTime = Date.now();
301
+ let lastError;
302
+ let attempt = 0;
303
+ while (attempt < this.policy.maxAttempts) {
304
+ try {
305
+ const result = await operation();
306
+ // Record success if policy supports it
307
+ if (this.policy instanceof CircuitBreakerAwarePolicy) {
308
+ this.policy.recordSuccess();
309
+ }
310
+ return result;
311
+ }
312
+ catch (error) {
313
+ lastError = error;
314
+ const elapsed = Date.now() - startTime;
315
+ const decision = this.policy.shouldRetry({
316
+ attempt,
317
+ error: lastError,
318
+ elapsedMs: elapsed,
319
+ operationName,
320
+ });
321
+ if (!decision.shouldRetry) {
322
+ logger.warn(`[RetryExecutor] ${operationName}: ${decision.reason}`);
323
+ break;
324
+ }
325
+ logger.debug(`[RetryExecutor] ${operationName}: ${decision.reason}`);
326
+ await this.delay(decision.delayMs);
327
+ attempt++;
328
+ }
329
+ }
330
+ throw (lastError ??
331
+ new Error(`${operationName} failed after ${attempt} attempts`));
332
+ }
333
+ delay(ms) {
334
+ return new Promise((resolve) => setTimeout(resolve, ms));
335
+ }
336
+ }
337
+ /**
338
+ * Factory for creating retry policies
339
+ */
340
+ export class RetryPolicyFactory {
341
+ /**
342
+ * Create a default policy for production use
343
+ */
344
+ static createDefault() {
345
+ return new ExponentialBackoffPolicy({
346
+ maxAttempts: 3,
347
+ baseDelayMs: 1000,
348
+ maxDelayMs: 30000,
349
+ });
350
+ }
351
+ /**
352
+ * Create an aggressive policy for critical operations
353
+ */
354
+ static createAggressive() {
355
+ return new ExponentialBackoffPolicy({
356
+ maxAttempts: 5,
357
+ maxTotalTimeMs: 120000,
358
+ baseDelayMs: 500,
359
+ maxDelayMs: 60000,
360
+ });
361
+ }
362
+ /**
363
+ * Create a conservative policy for non-critical operations
364
+ */
365
+ static createConservative() {
366
+ return new LinearBackoffPolicy({
367
+ maxAttempts: 2,
368
+ maxTotalTimeMs: 10000,
369
+ initialDelayMs: 500,
370
+ delayIncrementMs: 500,
371
+ });
372
+ }
373
+ /**
374
+ * Create a circuit breaker aware policy
375
+ */
376
+ static createWithCircuitBreaker(config) {
377
+ return new CircuitBreakerAwarePolicy({
378
+ innerPolicy: RetryPolicyFactory.createDefault(),
379
+ failureThreshold: config?.failureThreshold ?? 5,
380
+ resetTimeoutMs: config?.resetTimeoutMs ?? 30000,
381
+ });
382
+ }
383
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Sampling strategies exports
3
+ */
4
+ export { AlwaysSampler, AttributeBasedSampler, CompositeSampler, CustomSampler, ErrorOnlySampler, NeverSampler, PrioritySampler, RatioSampler, type Sampler, SamplerFactory, TraceIdRatioSampler, } from "./samplers.js";