@juspay/neurolink 9.23.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 (225) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +10 -13
  3. package/dist/adapters/tts/googleTTSHandler.js +26 -1
  4. package/dist/adapters/video/vertexVideoHandler.js +23 -17
  5. package/dist/cli/commands/config.d.ts +3 -3
  6. package/dist/cli/commands/observability.d.ts +53 -0
  7. package/dist/cli/commands/observability.js +453 -0
  8. package/dist/cli/commands/telemetry.d.ts +63 -0
  9. package/dist/cli/commands/telemetry.js +689 -0
  10. package/dist/cli/factories/commandFactory.d.ts +34 -0
  11. package/dist/cli/factories/commandFactory.js +321 -116
  12. package/dist/cli/parser.js +6 -9
  13. package/dist/cli/utils/formatters.d.ts +13 -0
  14. package/dist/cli/utils/formatters.js +23 -0
  15. package/dist/constants/contextWindows.js +6 -0
  16. package/dist/constants/enums.d.ts +6 -0
  17. package/dist/constants/enums.js +8 -2
  18. package/dist/context/budgetChecker.js +75 -48
  19. package/dist/context/contextCompactor.js +135 -127
  20. package/dist/core/baseProvider.d.ts +5 -0
  21. package/dist/core/baseProvider.js +158 -102
  22. package/dist/core/conversationMemoryInitializer.js +7 -4
  23. package/dist/core/conversationMemoryManager.d.ts +2 -0
  24. package/dist/core/conversationMemoryManager.js +6 -2
  25. package/dist/core/modules/GenerationHandler.d.ts +2 -2
  26. package/dist/core/modules/GenerationHandler.js +12 -12
  27. package/dist/evaluation/ragasEvaluator.js +39 -19
  28. package/dist/evaluation/scoring.js +46 -20
  29. package/dist/features/ppt/index.d.ts +1 -1
  30. package/dist/features/ppt/index.js +1 -1
  31. package/dist/features/ppt/presentationOrchestrator.js +23 -0
  32. package/dist/features/ppt/slideGenerator.js +13 -0
  33. package/dist/features/ppt/slideRenderers.d.ts +1 -1
  34. package/dist/features/ppt/slideRenderers.js +6 -4
  35. package/dist/features/ppt/slideTypeInference.d.ts +1 -1
  36. package/dist/features/ppt/slideTypeInference.js +75 -73
  37. package/dist/files/fileTools.d.ts +6 -6
  38. package/dist/index.d.ts +46 -12
  39. package/dist/index.js +79 -17
  40. package/dist/lib/adapters/tts/googleTTSHandler.js +26 -1
  41. package/dist/lib/adapters/video/vertexVideoHandler.js +23 -17
  42. package/dist/lib/constants/contextWindows.js +6 -0
  43. package/dist/lib/constants/enums.d.ts +6 -0
  44. package/dist/lib/constants/enums.js +8 -2
  45. package/dist/lib/context/budgetChecker.js +75 -48
  46. package/dist/lib/context/contextCompactor.js +135 -127
  47. package/dist/lib/core/baseProvider.d.ts +5 -0
  48. package/dist/lib/core/baseProvider.js +158 -102
  49. package/dist/lib/core/conversationMemoryInitializer.js +7 -4
  50. package/dist/lib/core/conversationMemoryManager.d.ts +2 -0
  51. package/dist/lib/core/conversationMemoryManager.js +6 -2
  52. package/dist/lib/core/modules/GenerationHandler.d.ts +2 -2
  53. package/dist/lib/core/modules/GenerationHandler.js +12 -12
  54. package/dist/lib/evaluation/ragasEvaluator.js +39 -19
  55. package/dist/lib/evaluation/scoring.js +46 -20
  56. package/dist/lib/features/ppt/index.d.ts +1 -1
  57. package/dist/lib/features/ppt/index.js +1 -1
  58. package/dist/lib/features/ppt/presentationOrchestrator.js +23 -0
  59. package/dist/lib/features/ppt/slideGenerator.js +13 -0
  60. package/dist/lib/features/ppt/slideRenderers.d.ts +1 -1
  61. package/dist/lib/features/ppt/slideRenderers.js +6 -4
  62. package/dist/lib/features/ppt/slideTypeInference.d.ts +1 -1
  63. package/dist/lib/features/ppt/slideTypeInference.js +75 -73
  64. package/dist/lib/files/fileTools.d.ts +6 -6
  65. package/dist/lib/index.d.ts +46 -12
  66. package/dist/lib/index.js +79 -17
  67. package/dist/lib/mcp/httpRateLimiter.js +39 -12
  68. package/dist/lib/mcp/httpRetryHandler.js +22 -1
  69. package/dist/lib/mcp/mcpClientFactory.js +13 -15
  70. package/dist/lib/memory/memoryRetrievalTools.js +22 -0
  71. package/dist/lib/neurolink.d.ts +64 -72
  72. package/dist/lib/neurolink.js +984 -566
  73. package/dist/lib/observability/exporterRegistry.d.ts +152 -0
  74. package/dist/lib/observability/exporterRegistry.js +414 -0
  75. package/dist/lib/observability/exporters/arizeExporter.d.ts +32 -0
  76. package/dist/lib/observability/exporters/arizeExporter.js +139 -0
  77. package/dist/lib/observability/exporters/baseExporter.d.ts +117 -0
  78. package/dist/lib/observability/exporters/baseExporter.js +191 -0
  79. package/dist/lib/observability/exporters/braintrustExporter.d.ts +30 -0
  80. package/dist/lib/observability/exporters/braintrustExporter.js +155 -0
  81. package/dist/lib/observability/exporters/datadogExporter.d.ts +37 -0
  82. package/dist/lib/observability/exporters/datadogExporter.js +197 -0
  83. package/dist/lib/observability/exporters/index.d.ts +13 -0
  84. package/dist/lib/observability/exporters/index.js +14 -0
  85. package/dist/lib/observability/exporters/laminarExporter.d.ts +48 -0
  86. package/dist/lib/observability/exporters/laminarExporter.js +303 -0
  87. package/dist/lib/observability/exporters/langfuseExporter.d.ts +47 -0
  88. package/dist/lib/observability/exporters/langfuseExporter.js +200 -0
  89. package/dist/lib/observability/exporters/langsmithExporter.d.ts +26 -0
  90. package/dist/lib/observability/exporters/langsmithExporter.js +124 -0
  91. package/dist/lib/observability/exporters/otelExporter.d.ts +39 -0
  92. package/dist/lib/observability/exporters/otelExporter.js +165 -0
  93. package/dist/lib/observability/exporters/posthogExporter.d.ts +48 -0
  94. package/dist/lib/observability/exporters/posthogExporter.js +288 -0
  95. package/dist/lib/observability/exporters/sentryExporter.d.ts +32 -0
  96. package/dist/lib/observability/exporters/sentryExporter.js +166 -0
  97. package/dist/lib/observability/index.d.ts +25 -0
  98. package/dist/lib/observability/index.js +32 -0
  99. package/dist/lib/observability/metricsAggregator.d.ts +260 -0
  100. package/dist/lib/observability/metricsAggregator.js +553 -0
  101. package/dist/lib/observability/otelBridge.d.ts +49 -0
  102. package/dist/lib/observability/otelBridge.js +132 -0
  103. package/dist/lib/observability/retryPolicy.d.ts +192 -0
  104. package/dist/lib/observability/retryPolicy.js +384 -0
  105. package/dist/lib/observability/sampling/index.d.ts +4 -0
  106. package/dist/lib/observability/sampling/index.js +5 -0
  107. package/dist/lib/observability/sampling/samplers.d.ts +116 -0
  108. package/dist/lib/observability/sampling/samplers.js +217 -0
  109. package/dist/lib/observability/spanProcessor.d.ts +129 -0
  110. package/dist/lib/observability/spanProcessor.js +288 -0
  111. package/dist/lib/observability/tokenTracker.d.ts +156 -0
  112. package/dist/lib/observability/tokenTracker.js +414 -0
  113. package/dist/lib/observability/types/exporterTypes.d.ts +250 -0
  114. package/dist/lib/observability/types/exporterTypes.js +6 -0
  115. package/dist/lib/observability/types/index.d.ts +6 -0
  116. package/dist/lib/observability/types/index.js +5 -0
  117. package/dist/lib/observability/types/spanTypes.d.ts +244 -0
  118. package/dist/lib/observability/types/spanTypes.js +93 -0
  119. package/dist/lib/observability/utils/index.d.ts +4 -0
  120. package/dist/lib/observability/utils/index.js +5 -0
  121. package/dist/lib/observability/utils/spanSerializer.d.ts +115 -0
  122. package/dist/lib/observability/utils/spanSerializer.js +287 -0
  123. package/dist/lib/providers/amazonSagemaker.d.ts +5 -4
  124. package/dist/lib/providers/amazonSagemaker.js +3 -4
  125. package/dist/lib/providers/googleVertex.d.ts +7 -0
  126. package/dist/lib/providers/googleVertex.js +80 -2
  127. package/dist/lib/rag/pipeline/RAGPipeline.d.ts +0 -5
  128. package/dist/lib/rag/pipeline/RAGPipeline.js +122 -87
  129. package/dist/lib/rag/ragIntegration.js +30 -0
  130. package/dist/lib/rag/retrieval/hybridSearch.js +22 -0
  131. package/dist/lib/server/abstract/baseServerAdapter.js +51 -19
  132. package/dist/lib/server/middleware/common.js +44 -12
  133. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +2 -2
  134. package/dist/lib/services/server/ai/observability/instrumentation.js +10 -5
  135. package/dist/lib/types/cli.d.ts +18 -2
  136. package/dist/lib/types/conversationMemoryInterface.d.ts +2 -0
  137. package/dist/lib/types/generateTypes.d.ts +2 -2
  138. package/dist/lib/types/modelTypes.d.ts +18 -18
  139. package/dist/lib/types/providers.d.ts +5 -0
  140. package/dist/lib/utils/pricing.js +25 -1
  141. package/dist/lib/utils/ttsProcessor.js +74 -59
  142. package/dist/lib/workflow/config.d.ts +36 -36
  143. package/dist/lib/workflow/core/ensembleExecutor.js +10 -0
  144. package/dist/lib/workflow/core/judgeScorer.js +20 -2
  145. package/dist/lib/workflow/core/workflowRunner.js +34 -1
  146. package/dist/mcp/httpRateLimiter.js +39 -12
  147. package/dist/mcp/httpRetryHandler.js +22 -1
  148. package/dist/mcp/mcpClientFactory.js +13 -15
  149. package/dist/memory/memoryRetrievalTools.js +22 -0
  150. package/dist/neurolink.d.ts +64 -72
  151. package/dist/neurolink.js +984 -566
  152. package/dist/observability/FEATURE-STATUS.md +269 -0
  153. package/dist/observability/exporterRegistry.d.ts +152 -0
  154. package/dist/observability/exporterRegistry.js +413 -0
  155. package/dist/observability/exporters/arizeExporter.d.ts +32 -0
  156. package/dist/observability/exporters/arizeExporter.js +138 -0
  157. package/dist/observability/exporters/baseExporter.d.ts +117 -0
  158. package/dist/observability/exporters/baseExporter.js +190 -0
  159. package/dist/observability/exporters/braintrustExporter.d.ts +30 -0
  160. package/dist/observability/exporters/braintrustExporter.js +154 -0
  161. package/dist/observability/exporters/datadogExporter.d.ts +37 -0
  162. package/dist/observability/exporters/datadogExporter.js +196 -0
  163. package/dist/observability/exporters/index.d.ts +13 -0
  164. package/dist/observability/exporters/index.js +13 -0
  165. package/dist/observability/exporters/laminarExporter.d.ts +48 -0
  166. package/dist/observability/exporters/laminarExporter.js +302 -0
  167. package/dist/observability/exporters/langfuseExporter.d.ts +47 -0
  168. package/dist/observability/exporters/langfuseExporter.js +199 -0
  169. package/dist/observability/exporters/langsmithExporter.d.ts +26 -0
  170. package/dist/observability/exporters/langsmithExporter.js +123 -0
  171. package/dist/observability/exporters/otelExporter.d.ts +39 -0
  172. package/dist/observability/exporters/otelExporter.js +164 -0
  173. package/dist/observability/exporters/posthogExporter.d.ts +48 -0
  174. package/dist/observability/exporters/posthogExporter.js +287 -0
  175. package/dist/observability/exporters/sentryExporter.d.ts +32 -0
  176. package/dist/observability/exporters/sentryExporter.js +165 -0
  177. package/dist/observability/index.d.ts +25 -0
  178. package/dist/observability/index.js +31 -0
  179. package/dist/observability/metricsAggregator.d.ts +260 -0
  180. package/dist/observability/metricsAggregator.js +552 -0
  181. package/dist/observability/otelBridge.d.ts +49 -0
  182. package/dist/observability/otelBridge.js +131 -0
  183. package/dist/observability/retryPolicy.d.ts +192 -0
  184. package/dist/observability/retryPolicy.js +383 -0
  185. package/dist/observability/sampling/index.d.ts +4 -0
  186. package/dist/observability/sampling/index.js +4 -0
  187. package/dist/observability/sampling/samplers.d.ts +116 -0
  188. package/dist/observability/sampling/samplers.js +216 -0
  189. package/dist/observability/spanProcessor.d.ts +129 -0
  190. package/dist/observability/spanProcessor.js +287 -0
  191. package/dist/observability/tokenTracker.d.ts +156 -0
  192. package/dist/observability/tokenTracker.js +413 -0
  193. package/dist/observability/types/exporterTypes.d.ts +250 -0
  194. package/dist/observability/types/exporterTypes.js +5 -0
  195. package/dist/observability/types/index.d.ts +6 -0
  196. package/dist/observability/types/index.js +4 -0
  197. package/dist/observability/types/spanTypes.d.ts +244 -0
  198. package/dist/observability/types/spanTypes.js +92 -0
  199. package/dist/observability/utils/index.d.ts +4 -0
  200. package/dist/observability/utils/index.js +4 -0
  201. package/dist/observability/utils/spanSerializer.d.ts +115 -0
  202. package/dist/observability/utils/spanSerializer.js +286 -0
  203. package/dist/providers/amazonSagemaker.d.ts +5 -4
  204. package/dist/providers/amazonSagemaker.js +3 -4
  205. package/dist/providers/googleVertex.d.ts +7 -0
  206. package/dist/providers/googleVertex.js +80 -2
  207. package/dist/rag/pipeline/RAGPipeline.d.ts +0 -5
  208. package/dist/rag/pipeline/RAGPipeline.js +122 -87
  209. package/dist/rag/ragIntegration.js +30 -0
  210. package/dist/rag/retrieval/hybridSearch.js +22 -0
  211. package/dist/server/abstract/baseServerAdapter.js +51 -19
  212. package/dist/server/middleware/common.js +44 -12
  213. package/dist/services/server/ai/observability/instrumentation.d.ts +2 -2
  214. package/dist/services/server/ai/observability/instrumentation.js +10 -5
  215. package/dist/types/cli.d.ts +18 -2
  216. package/dist/types/conversationMemoryInterface.d.ts +2 -0
  217. package/dist/types/generateTypes.d.ts +2 -2
  218. package/dist/types/providers.d.ts +5 -0
  219. package/dist/utils/pricing.js +25 -1
  220. package/dist/utils/ttsProcessor.js +74 -59
  221. package/dist/workflow/config.d.ts +52 -52
  222. package/dist/workflow/core/ensembleExecutor.js +10 -0
  223. package/dist/workflow/core/judgeScorer.js +20 -2
  224. package/dist/workflow/core/workflowRunner.js +34 -1
  225. package/package.json +1 -1
@@ -0,0 +1,413 @@
1
+ /**
2
+ * Exporter Registry
3
+ * Manages multiple observability exporters with circuit breaker protection
4
+ */
5
+ import { logger } from "../utils/logger.js";
6
+ import { AlwaysSampler } from "./sampling/samplers.js";
7
+ /** Default timeout for exporter API calls (30 seconds) */
8
+ const DEFAULT_EXPORT_TIMEOUT_MS = 30_000;
9
+ /**
10
+ * Wrap a promise with a timeout. Rejects with a descriptive error if the
11
+ * promise does not settle within `timeoutMs` milliseconds.
12
+ */
13
+ function withExportTimeout(promise, timeoutMs, label) {
14
+ return new Promise((resolve, reject) => {
15
+ const timer = setTimeout(() => {
16
+ reject(new Error(`Export to '${label}' timed out after ${timeoutMs}ms`));
17
+ }, timeoutMs);
18
+ promise.then((v) => {
19
+ clearTimeout(timer);
20
+ resolve(v);
21
+ }, (e) => {
22
+ clearTimeout(timer);
23
+ reject(e);
24
+ });
25
+ });
26
+ }
27
+ /**
28
+ * Registry for managing multiple observability exporters
29
+ * Includes circuit breaker protection to prevent cascading failures
30
+ */
31
+ export class ExporterRegistry {
32
+ exporters = new Map();
33
+ defaultExporter = null;
34
+ sampler = new AlwaysSampler();
35
+ circuitBreakers = new Map();
36
+ circuitBreakerConfig = {
37
+ failureThreshold: 5,
38
+ resetTimeout: 30000, // 30 seconds
39
+ };
40
+ /**
41
+ * Register an exporter
42
+ */
43
+ register(exporter) {
44
+ this.exporters.set(exporter.getName(), exporter);
45
+ }
46
+ /**
47
+ * Unregister an exporter
48
+ */
49
+ unregister(name) {
50
+ return this.exporters.delete(name);
51
+ }
52
+ /**
53
+ * Get an exporter by name
54
+ */
55
+ get(name) {
56
+ return this.exporters.get(name);
57
+ }
58
+ /**
59
+ * Get all registered exporter names
60
+ */
61
+ getNames() {
62
+ return Array.from(this.exporters.keys());
63
+ }
64
+ /**
65
+ * Get total exporter count
66
+ */
67
+ getCount() {
68
+ return this.exporters.size;
69
+ }
70
+ /**
71
+ * Set the default exporter
72
+ */
73
+ setDefault(name) {
74
+ if (!this.exporters.has(name)) {
75
+ throw new Error(`Exporter '${name}' not registered`);
76
+ }
77
+ this.defaultExporter = name;
78
+ }
79
+ /**
80
+ * Get the default exporter
81
+ */
82
+ getDefault() {
83
+ if (!this.defaultExporter) {
84
+ return undefined;
85
+ }
86
+ return this.exporters.get(this.defaultExporter);
87
+ }
88
+ /**
89
+ * Set the sampler for the registry
90
+ */
91
+ setSampler(sampler) {
92
+ this.sampler = sampler;
93
+ }
94
+ /**
95
+ * Get the current sampler
96
+ */
97
+ getSampler() {
98
+ return this.sampler;
99
+ }
100
+ /**
101
+ * Configure the circuit breaker settings
102
+ * @param config - Partial circuit breaker configuration
103
+ */
104
+ configureCircuitBreaker(config) {
105
+ Object.assign(this.circuitBreakerConfig, config);
106
+ }
107
+ /**
108
+ * Check if circuit is open for an exporter
109
+ * @param exporterName - Name of the exporter
110
+ * @returns true if circuit is open (exporter should be skipped)
111
+ */
112
+ isCircuitOpen(exporterName) {
113
+ const breaker = this.circuitBreakers.get(exporterName);
114
+ if (!breaker) {
115
+ return false;
116
+ }
117
+ if (breaker.state === "open") {
118
+ // Check if we should try half-open
119
+ if (Date.now() - breaker.lastFailure >
120
+ this.circuitBreakerConfig.resetTimeout) {
121
+ breaker.state = "half-open";
122
+ logger.info(`[ExporterRegistry] Circuit half-open for ${exporterName}, attempting recovery`);
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+ return false;
128
+ }
129
+ /**
130
+ * Record a failure for an exporter's circuit breaker
131
+ * @param exporterName - Name of the exporter
132
+ */
133
+ recordFailure(exporterName) {
134
+ let breaker = this.circuitBreakers.get(exporterName);
135
+ if (!breaker) {
136
+ breaker = { failures: 0, lastFailure: 0, state: "closed" };
137
+ this.circuitBreakers.set(exporterName, breaker);
138
+ }
139
+ breaker.failures++;
140
+ breaker.lastFailure = Date.now();
141
+ if (breaker.failures >= this.circuitBreakerConfig.failureThreshold) {
142
+ breaker.state = "open";
143
+ logger.warn(`[ExporterRegistry] Circuit opened for ${exporterName} after ${breaker.failures} failures`);
144
+ }
145
+ }
146
+ /**
147
+ * Record a success for an exporter's circuit breaker
148
+ * Resets the circuit to closed state
149
+ * @param exporterName - Name of the exporter
150
+ */
151
+ recordSuccess(exporterName) {
152
+ const breaker = this.circuitBreakers.get(exporterName);
153
+ if (breaker) {
154
+ if (breaker.state === "half-open") {
155
+ logger.info(`[ExporterRegistry] Circuit closed for ${exporterName} after successful recovery`);
156
+ }
157
+ breaker.failures = 0;
158
+ breaker.state = "closed";
159
+ }
160
+ }
161
+ /**
162
+ * Get circuit breaker status for an exporter
163
+ * @param exporterName - Name of the exporter
164
+ * @returns Circuit breaker state or undefined if not tracked
165
+ */
166
+ getCircuitBreakerStatus(exporterName) {
167
+ return this.circuitBreakers.get(exporterName);
168
+ }
169
+ /**
170
+ * Reset circuit breaker for an exporter
171
+ * @param exporterName - Name of the exporter
172
+ */
173
+ resetCircuitBreaker(exporterName) {
174
+ this.circuitBreakers.delete(exporterName);
175
+ }
176
+ /**
177
+ * Export span to all registered exporters
178
+ * Applies sampling and circuit breaker protection before export
179
+ */
180
+ async exportToAll(span) {
181
+ const results = new Map();
182
+ // Apply sampling
183
+ if (!this.sampler.shouldSample(span)) {
184
+ // Return empty results if not sampled
185
+ return results;
186
+ }
187
+ const exportPromises = Array.from(this.exporters.entries()).map(async ([name, exporter]) => {
188
+ // Check circuit breaker before attempting export
189
+ if (this.isCircuitOpen(name)) {
190
+ results.set(name, {
191
+ success: false,
192
+ exportedCount: 0,
193
+ failedCount: 1,
194
+ errors: [
195
+ {
196
+ spanId: span.spanId,
197
+ error: "Circuit breaker open - exporter temporarily disabled",
198
+ retryable: true,
199
+ },
200
+ ],
201
+ durationMs: 0,
202
+ });
203
+ return;
204
+ }
205
+ if (exporter.isInitialized()) {
206
+ try {
207
+ const result = await withExportTimeout(exporter.exportSpan(span), DEFAULT_EXPORT_TIMEOUT_MS, name);
208
+ results.set(name, result);
209
+ // Record success or failure based on result
210
+ if (result.success) {
211
+ this.recordSuccess(name);
212
+ }
213
+ else {
214
+ this.recordFailure(name);
215
+ }
216
+ }
217
+ catch (error) {
218
+ this.recordFailure(name);
219
+ results.set(name, {
220
+ success: false,
221
+ exportedCount: 0,
222
+ failedCount: 1,
223
+ errors: [
224
+ {
225
+ spanId: span.spanId,
226
+ error: error instanceof Error ? error.message : String(error),
227
+ retryable: true,
228
+ },
229
+ ],
230
+ durationMs: 0,
231
+ });
232
+ }
233
+ }
234
+ else {
235
+ logger.debug(`[ExporterRegistry] Skipping uninitialized exporter '${name}' for span ${span.spanId}`);
236
+ }
237
+ });
238
+ await Promise.all(exportPromises);
239
+ return results;
240
+ }
241
+ /**
242
+ * Export span to a specific exporter
243
+ * Applies sampling and circuit breaker protection
244
+ */
245
+ async exportTo(name, span) {
246
+ const exporter = this.exporters.get(name);
247
+ if (!exporter) {
248
+ return null;
249
+ }
250
+ if (!exporter.isInitialized()) {
251
+ logger.debug(`[ExporterRegistry] Skipping uninitialized exporter '${name}' for span ${span.spanId}`);
252
+ return null;
253
+ }
254
+ // Check circuit breaker before attempting export
255
+ if (this.isCircuitOpen(name)) {
256
+ return {
257
+ success: false,
258
+ exportedCount: 0,
259
+ failedCount: 1,
260
+ errors: [
261
+ {
262
+ spanId: span.spanId,
263
+ error: "Circuit breaker open - exporter temporarily disabled",
264
+ retryable: true,
265
+ },
266
+ ],
267
+ durationMs: 0,
268
+ };
269
+ }
270
+ // Apply sampling
271
+ if (!this.sampler.shouldSample(span)) {
272
+ return {
273
+ success: true,
274
+ exportedCount: 0,
275
+ failedCount: 0,
276
+ durationMs: 0,
277
+ };
278
+ }
279
+ try {
280
+ const result = await withExportTimeout(exporter.exportSpan(span), DEFAULT_EXPORT_TIMEOUT_MS, name);
281
+ if (result.success) {
282
+ this.recordSuccess(name);
283
+ }
284
+ else {
285
+ this.recordFailure(name);
286
+ }
287
+ return result;
288
+ }
289
+ catch (error) {
290
+ this.recordFailure(name);
291
+ return {
292
+ success: false,
293
+ exportedCount: 0,
294
+ failedCount: 1,
295
+ errors: [
296
+ {
297
+ spanId: span.spanId,
298
+ error: error instanceof Error ? error.message : String(error),
299
+ retryable: true,
300
+ },
301
+ ],
302
+ durationMs: 0,
303
+ };
304
+ }
305
+ }
306
+ /**
307
+ * Initialize all exporters
308
+ */
309
+ async initializeAll() {
310
+ const results = await Promise.allSettled(Array.from(this.exporters.entries()).map(([name, e]) => e.initialize().catch((err) => {
311
+ logger.error(`[ExporterRegistry] Failed to initialize exporter '${name}':`, err);
312
+ throw err;
313
+ })));
314
+ for (const result of results) {
315
+ if (result.status === "rejected") {
316
+ logger.warn(`[ExporterRegistry] One or more exporters failed to initialize`);
317
+ break;
318
+ }
319
+ }
320
+ }
321
+ /**
322
+ * Shutdown all exporters
323
+ */
324
+ async shutdownAll() {
325
+ const results = await Promise.allSettled(Array.from(this.exporters.entries()).map(([name, e]) => e.shutdown().catch((err) => {
326
+ logger.error(`[ExporterRegistry] Failed to shutdown exporter '${name}':`, err);
327
+ throw err;
328
+ })));
329
+ for (const result of results) {
330
+ if (result.status === "rejected") {
331
+ logger.warn(`[ExporterRegistry] One or more exporters failed to shutdown`);
332
+ break;
333
+ }
334
+ }
335
+ }
336
+ /**
337
+ * Flush all exporters
338
+ */
339
+ async flushAll() {
340
+ const results = await Promise.allSettled(Array.from(this.exporters.entries()).map(([name, e]) => e.flush().catch((err) => {
341
+ logger.error(`[ExporterRegistry] Failed to flush exporter '${name}':`, err);
342
+ throw err;
343
+ })));
344
+ for (const result of results) {
345
+ if (result.status === "rejected") {
346
+ logger.warn(`[ExporterRegistry] One or more exporters failed to flush`);
347
+ break;
348
+ }
349
+ }
350
+ }
351
+ /**
352
+ * Get health status of all exporters
353
+ */
354
+ async healthCheckAll() {
355
+ const results = new Map();
356
+ const healthPromises = Array.from(this.exporters.entries()).map(async ([name, exporter]) => {
357
+ const status = await exporter.healthCheck();
358
+ results.set(name, status);
359
+ });
360
+ await Promise.all(healthPromises);
361
+ return results;
362
+ }
363
+ /**
364
+ * Check if all exporters are healthy
365
+ */
366
+ async isHealthy() {
367
+ const statuses = await this.healthCheckAll();
368
+ return Array.from(statuses.values()).every((s) => s.healthy);
369
+ }
370
+ /**
371
+ * Get total pending spans across all exporters
372
+ */
373
+ getTotalPendingSpans() {
374
+ let total = 0;
375
+ const exporterArray = Array.from(this.exporters.values());
376
+ for (const exporter of exporterArray) {
377
+ total += exporter.getPendingCount();
378
+ }
379
+ return total;
380
+ }
381
+ /**
382
+ * Clear all registered exporters and reset state
383
+ * (For testing and cleanup)
384
+ */
385
+ clear() {
386
+ this.exporters.clear();
387
+ this.defaultExporter = null;
388
+ this.circuitBreakers.clear();
389
+ this.sampler = new AlwaysSampler();
390
+ }
391
+ }
392
+ /**
393
+ * Global exporter registry instance (singleton pattern from main)
394
+ */
395
+ let globalRegistry = null;
396
+ /**
397
+ * Get the global exporter registry instance
398
+ */
399
+ export function getExporterRegistry() {
400
+ if (!globalRegistry) {
401
+ globalRegistry = new ExporterRegistry();
402
+ }
403
+ return globalRegistry;
404
+ }
405
+ /**
406
+ * Reset the global exporter registry (for testing)
407
+ */
408
+ export function resetExporterRegistry() {
409
+ if (globalRegistry) {
410
+ globalRegistry.clear();
411
+ }
412
+ globalRegistry = null;
413
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Arize Exporter
3
+ * Exports spans to Arize ML monitoring platform
4
+ */
5
+ import type { ArizeExporterConfig, ExporterHealthStatus, ExportResult, SpanData } from "../types/index.js";
6
+ import { BaseExporter } from "./baseExporter.js";
7
+ /**
8
+ * Arize exporter for ML monitoring and prediction logs
9
+ * Supports feature tracking and model performance monitoring
10
+ */
11
+ export declare class ArizeExporter extends BaseExporter {
12
+ private readonly spaceKey;
13
+ private readonly apiKey;
14
+ private readonly modelId;
15
+ private readonly modelVersion;
16
+ private readonly endpoint;
17
+ constructor(config: ArizeExporterConfig);
18
+ initialize(): Promise<void>;
19
+ exportSpan(span: SpanData): Promise<ExportResult>;
20
+ exportBatch(spans: SpanData[]): Promise<ExportResult>;
21
+ flush(): Promise<void>;
22
+ shutdown(): Promise<void>;
23
+ healthCheck(): Promise<ExporterHealthStatus>;
24
+ /**
25
+ * Verify connectivity to Arize API
26
+ */
27
+ protected ping(): Promise<void>;
28
+ /**
29
+ * Convert span to Arize prediction log format
30
+ */
31
+ private convertToArizePrediction;
32
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Arize Exporter
3
+ * Exports spans to Arize ML monitoring platform
4
+ */
5
+ import { BaseExporter } from "./baseExporter.js";
6
+ /**
7
+ * Arize exporter for ML monitoring and prediction logs
8
+ * Supports feature tracking and model performance monitoring
9
+ */
10
+ export class ArizeExporter extends BaseExporter {
11
+ spaceKey;
12
+ apiKey;
13
+ modelId;
14
+ modelVersion;
15
+ endpoint = "https://api.arize.com/v1";
16
+ constructor(config) {
17
+ super("arize", config);
18
+ this.spaceKey = config.spaceKey;
19
+ this.apiKey = config.apiKey;
20
+ this.modelId = config.modelId ?? "neurolink-ai";
21
+ this.modelVersion = config.modelVersion ?? "1.0.0";
22
+ }
23
+ async initialize() {
24
+ if (this.initialized) {
25
+ return;
26
+ }
27
+ this.initialized = true;
28
+ this.startFlushInterval(this.config.flushIntervalMs ?? 10000);
29
+ }
30
+ async exportSpan(span) {
31
+ const startTime = Date.now();
32
+ try {
33
+ const prediction = this.convertToArizePrediction(span);
34
+ const response = await fetch(`${this.endpoint}/log`, {
35
+ method: "POST",
36
+ headers: {
37
+ "Content-Type": "application/json",
38
+ Authorization: `Bearer ${this.apiKey}`,
39
+ "space-key": this.spaceKey,
40
+ },
41
+ body: JSON.stringify(prediction),
42
+ });
43
+ if (!response.ok) {
44
+ throw new Error(`Export failed: ${response.statusText}`);
45
+ }
46
+ return this.createSuccessResult(1, Date.now() - startTime);
47
+ }
48
+ catch (error) {
49
+ return this.createFailureResult([span.spanId], error instanceof Error ? error.message : String(error), Date.now() - startTime);
50
+ }
51
+ }
52
+ async exportBatch(spans) {
53
+ // Arize's /v1/log endpoint does not support batch payloads, so we send
54
+ // individual requests in parallel. This is intentional — not a missed
55
+ // optimization.
56
+ const results = await Promise.all(spans.map((s) => this.exportSpan(s)));
57
+ const successful = results.filter((r) => r.success).length;
58
+ const failed = spans.length - successful;
59
+ return {
60
+ success: failed === 0,
61
+ exportedCount: successful,
62
+ failedCount: failed,
63
+ errors: results.flatMap((r) => r.errors ?? []),
64
+ durationMs: results.reduce((sum, r) => sum + r.durationMs, 0),
65
+ };
66
+ }
67
+ async flush() {
68
+ if (!this.initialized || this.buffer.length === 0) {
69
+ return;
70
+ }
71
+ const spans = [...this.buffer];
72
+ this.buffer = [];
73
+ await this.exportBatch(spans);
74
+ }
75
+ async shutdown() {
76
+ await this.flush();
77
+ this.stopFlushInterval();
78
+ this.initialized = false;
79
+ }
80
+ async healthCheck() {
81
+ try {
82
+ await this.withRetry(() => this.ping(), "health check");
83
+ return this.createHealthStatus(true);
84
+ }
85
+ catch {
86
+ return this.createHealthStatus(false, ["Health check failed"]);
87
+ }
88
+ }
89
+ /**
90
+ * Verify connectivity to Arize API
91
+ */
92
+ async ping() {
93
+ const response = await fetch(`${this.endpoint}/health`, {
94
+ method: "GET",
95
+ headers: {
96
+ Authorization: `Bearer ${this.apiKey}`,
97
+ "space-key": this.spaceKey,
98
+ },
99
+ });
100
+ if (!response.ok && response.status !== 404) {
101
+ // 404 is acceptable as health endpoint may not exist
102
+ throw new Error(`Arize API unreachable: ${response.status}`);
103
+ }
104
+ }
105
+ /**
106
+ * Convert span to Arize prediction log format
107
+ */
108
+ convertToArizePrediction(span) {
109
+ return {
110
+ space_key: this.spaceKey,
111
+ model_id: span.attributes["ai.model"] ?? this.modelId,
112
+ model_version: this.modelVersion,
113
+ prediction_id: span.spanId,
114
+ prediction_timestamp: new Date(span.startTime).getTime(),
115
+ features: {
116
+ provider: span.attributes["ai.provider"],
117
+ temperature: span.attributes["ai.temperature"],
118
+ max_tokens: span.attributes["ai.max_tokens"],
119
+ user_id: span.attributes["user.id"],
120
+ session_id: span.attributes["session.id"],
121
+ },
122
+ prediction: {
123
+ input: span.attributes["input"],
124
+ output: span.attributes["output"],
125
+ },
126
+ tags: {
127
+ span_type: span.type,
128
+ environment: this.config.environment,
129
+ },
130
+ latency_ms: span.durationMs,
131
+ token_count: {
132
+ prompt: span.attributes["ai.tokens.input"],
133
+ completion: span.attributes["ai.tokens.output"],
134
+ total: span.attributes["ai.tokens.total"],
135
+ },
136
+ };
137
+ }
138
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Abstract base class for all observability exporters
3
+ * Follows NeuroLink's factory pattern and Mastra's unified exporter interface
4
+ */
5
+ import type { ExporterConfig, ExporterHealthStatus, ExportResult, SpanData } from "../types/index.js";
6
+ /**
7
+ * Abstract base class for all observability exporters
8
+ * Provides common functionality: buffering, flush intervals, health checks, retry logic
9
+ */
10
+ export declare abstract class BaseExporter {
11
+ protected readonly name: string;
12
+ protected readonly config: ExporterConfig;
13
+ protected initialized: boolean;
14
+ protected buffer: SpanData[];
15
+ protected readonly maxBufferSize: number;
16
+ protected readonly retries: number;
17
+ protected flushInterval: ReturnType<typeof setInterval> | null;
18
+ protected lastExportTime: number;
19
+ constructor(name: string, config: ExporterConfig);
20
+ /**
21
+ * Initialize the exporter connection
22
+ * Must be called before exporting spans
23
+ */
24
+ abstract initialize(): Promise<void>;
25
+ /**
26
+ * Export a single span
27
+ * @param span - The span data to export
28
+ */
29
+ abstract exportSpan(span: SpanData): Promise<ExportResult>;
30
+ /**
31
+ * Export multiple spans in batch
32
+ * @param spans - Array of span data to export
33
+ */
34
+ abstract exportBatch(spans: SpanData[]): Promise<ExportResult>;
35
+ /**
36
+ * Flush all buffered spans
37
+ */
38
+ abstract flush(): Promise<void>;
39
+ /**
40
+ * Shutdown the exporter gracefully
41
+ * Should flush remaining spans before closing
42
+ */
43
+ abstract shutdown(): Promise<void>;
44
+ /**
45
+ * Check exporter health status
46
+ * Implementations should make an actual API call to verify connectivity
47
+ */
48
+ abstract healthCheck(): Promise<ExporterHealthStatus>;
49
+ /**
50
+ * Ping the exporter's backend to verify connectivity
51
+ * Override this in subclasses to provide backend-specific health check
52
+ */
53
+ protected ping(): Promise<void>;
54
+ /**
55
+ * Buffer a span for batch export
56
+ * Triggers flush if buffer is full
57
+ */
58
+ protected bufferSpan(span: SpanData): void;
59
+ /**
60
+ * Start automatic flush interval
61
+ * @param intervalMs - Interval in milliseconds between flushes
62
+ */
63
+ protected startFlushInterval(intervalMs: number): void;
64
+ /**
65
+ * Stop the automatic flush interval
66
+ */
67
+ protected stopFlushInterval(): void;
68
+ /**
69
+ * Get exporter name
70
+ */
71
+ getName(): string;
72
+ /**
73
+ * Check if exporter is initialized
74
+ */
75
+ isInitialized(): boolean;
76
+ /**
77
+ * Get number of pending spans in buffer
78
+ */
79
+ getPendingCount(): number;
80
+ /**
81
+ * Get last export timestamp
82
+ */
83
+ getLastExportTime(): number;
84
+ /**
85
+ * Create a standard export result for success
86
+ */
87
+ protected createSuccessResult(exportedCount: number, durationMs: number): ExportResult;
88
+ /**
89
+ * Create a standard export result for failure
90
+ */
91
+ protected createFailureResult(spanIds: string[], error: string, durationMs: number, retryable?: boolean): ExportResult;
92
+ /**
93
+ * Create a standard health status
94
+ */
95
+ protected createHealthStatus(healthy: boolean, errors?: string[]): ExporterHealthStatus;
96
+ /**
97
+ * Execute an operation with exponential backoff retry
98
+ * @param operation - The async operation to execute
99
+ * @param operationName - Name for logging purposes
100
+ * @returns The result of the operation
101
+ * @throws The last error if all retries fail
102
+ */
103
+ protected withRetry<T>(operation: () => Promise<T>, operationName: string): Promise<T>;
104
+ }
105
+ /**
106
+ * No-op exporter for when observability is disabled
107
+ * Provides zero-overhead behavior
108
+ */
109
+ export declare class NoOpExporter extends BaseExporter {
110
+ constructor();
111
+ initialize(): Promise<void>;
112
+ exportSpan(_span: SpanData): Promise<ExportResult>;
113
+ exportBatch(_spans: SpanData[]): Promise<ExportResult>;
114
+ flush(): Promise<void>;
115
+ shutdown(): Promise<void>;
116
+ healthCheck(): Promise<ExporterHealthStatus>;
117
+ }