@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,302 @@
1
+ /**
2
+ * Laminar Exporter
3
+ * Exports spans to Laminar LLM tracing platform
4
+ * @see https://docs.laminar.run/
5
+ */
6
+ import { logger } from "../../utils/logger.js";
7
+ import { SpanStatus, SpanType } from "../types/spanTypes.js";
8
+ import { BaseExporter } from "./baseExporter.js";
9
+ /**
10
+ * Laminar exporter for LLM pipeline tracing and monitoring
11
+ * Supports detailed traces with input/output tracking
12
+ */
13
+ export class LaminarExporter extends BaseExporter {
14
+ apiKey;
15
+ projectApiKey;
16
+ baseUrl;
17
+ constructor(config) {
18
+ super("laminar", config);
19
+ this.apiKey = config.apiKey;
20
+ this.projectApiKey = config.projectApiKey;
21
+ this.baseUrl = config.baseUrl ?? "https://api.laminar.run";
22
+ }
23
+ async initialize() {
24
+ if (this.initialized) {
25
+ return;
26
+ }
27
+ // Verify API key by making a test call
28
+ try {
29
+ const response = await fetch(`${this.baseUrl}/v1/health`, {
30
+ headers: this.getHeaders(),
31
+ });
32
+ if (!response.ok) {
33
+ logger.warn("[Laminar] Could not verify API connection:", response.statusText);
34
+ }
35
+ }
36
+ catch (error) {
37
+ logger.warn("[Laminar] Could not verify API connection:", error instanceof Error ? error.message : error);
38
+ }
39
+ this.initialized = true;
40
+ this.startFlushInterval(this.config.flushIntervalMs ?? 5000);
41
+ }
42
+ /**
43
+ * Get authorization headers
44
+ */
45
+ getHeaders() {
46
+ const headers = {
47
+ "Content-Type": "application/json",
48
+ Authorization: `Bearer ${this.apiKey}`,
49
+ };
50
+ // Add project API key if provided
51
+ if (this.projectApiKey) {
52
+ headers["X-Project-Api-Key"] = this.projectApiKey;
53
+ }
54
+ return headers;
55
+ }
56
+ async exportSpan(span) {
57
+ const startTime = Date.now();
58
+ try {
59
+ const trace = this.convertToLaminarTrace(span);
60
+ const response = await fetch(`${this.baseUrl}/v1/traces`, {
61
+ method: "POST",
62
+ headers: this.getHeaders(),
63
+ body: JSON.stringify(trace),
64
+ });
65
+ if (!response.ok) {
66
+ throw new Error(`Export failed: ${response.statusText}`);
67
+ }
68
+ return this.createSuccessResult(1, Date.now() - startTime);
69
+ }
70
+ catch (error) {
71
+ return this.createFailureResult([span.spanId], error instanceof Error ? error.message : String(error), Date.now() - startTime);
72
+ }
73
+ }
74
+ async exportBatch(spans) {
75
+ const startTime = Date.now();
76
+ try {
77
+ const traces = spans.map((s) => this.convertToLaminarTrace(s));
78
+ const response = await fetch(`${this.baseUrl}/v1/traces/batch`, {
79
+ method: "POST",
80
+ headers: this.getHeaders(),
81
+ body: JSON.stringify({ traces }),
82
+ });
83
+ if (!response.ok) {
84
+ throw new Error(`Batch export failed: ${response.statusText}`);
85
+ }
86
+ return this.createSuccessResult(spans.length, Date.now() - startTime);
87
+ }
88
+ catch (error) {
89
+ return this.createFailureResult(spans.map((s) => s.spanId), error instanceof Error ? error.message : String(error), Date.now() - startTime);
90
+ }
91
+ }
92
+ async flush() {
93
+ if (this.buffer.length > 0) {
94
+ const spans = [...this.buffer];
95
+ this.buffer = [];
96
+ await this.exportBatch(spans);
97
+ }
98
+ }
99
+ async shutdown() {
100
+ await this.flush();
101
+ this.stopFlushInterval();
102
+ this.initialized = false;
103
+ }
104
+ async healthCheck() {
105
+ try {
106
+ await this.withRetry(() => this.ping(), "health check");
107
+ return this.createHealthStatus(true);
108
+ }
109
+ catch {
110
+ return this.createHealthStatus(false, ["Health check failed"]);
111
+ }
112
+ }
113
+ /**
114
+ * Verify connectivity to Laminar API
115
+ */
116
+ async ping() {
117
+ const response = await fetch(`${this.baseUrl}/v1/health`, {
118
+ headers: this.getHeaders(),
119
+ });
120
+ if (!response.ok) {
121
+ throw new Error(`Laminar API unreachable: ${response.status}`);
122
+ }
123
+ }
124
+ /**
125
+ * Convert span to Laminar trace format
126
+ */
127
+ convertToLaminarTrace(span) {
128
+ return {
129
+ // Core identifiers
130
+ trace_id: span.traceId,
131
+ span_id: span.spanId,
132
+ parent_span_id: span.parentSpanId,
133
+ // Trace metadata
134
+ name: span.name,
135
+ type: this.mapSpanTypeToLaminarType(span.type),
136
+ start_time: span.startTime,
137
+ end_time: span.endTime,
138
+ duration_ms: span.durationMs,
139
+ // Status
140
+ status: this.mapSpanStatus(span.status),
141
+ status_message: span.statusMessage,
142
+ // Model information
143
+ model: {
144
+ provider: span.attributes["ai.provider"],
145
+ name: span.attributes["ai.model"],
146
+ version: span.attributes["ai.model.version"],
147
+ },
148
+ // Token usage
149
+ usage: {
150
+ input_tokens: span.attributes["ai.tokens.input"],
151
+ output_tokens: span.attributes["ai.tokens.output"],
152
+ total_tokens: span.attributes["ai.tokens.total"],
153
+ cache_read_tokens: span.attributes["ai.tokens.cache_read"],
154
+ cache_creation_tokens: span.attributes["ai.tokens.cache_creation"],
155
+ reasoning_tokens: span.attributes["ai.tokens.reasoning"],
156
+ },
157
+ // Cost tracking
158
+ cost: {
159
+ input: span.attributes["ai.cost.input"],
160
+ output: span.attributes["ai.cost.output"],
161
+ total: span.attributes["ai.cost.total"],
162
+ currency: span.attributes["ai.cost.currency"] || "USD",
163
+ },
164
+ // Generation parameters
165
+ parameters: {
166
+ temperature: span.attributes["ai.temperature"],
167
+ max_tokens: span.attributes["ai.max_tokens"],
168
+ top_p: span.attributes["ai.top_p"],
169
+ stop_sequences: span.attributes["ai.stop_sequences"],
170
+ },
171
+ // Input/Output
172
+ input: span.attributes["input"],
173
+ output: span.attributes["output"],
174
+ // Tool information
175
+ tool: span.attributes["tool.name"]
176
+ ? {
177
+ name: span.attributes["tool.name"],
178
+ server: span.attributes["tool.server"],
179
+ success: span.attributes["tool.success"],
180
+ }
181
+ : undefined,
182
+ // Error information
183
+ error: span.status === SpanStatus.ERROR
184
+ ? {
185
+ type: span.attributes["error.type"],
186
+ message: span.attributes["error.message"],
187
+ stack: span.attributes["error.stack"],
188
+ }
189
+ : undefined,
190
+ // User and session context
191
+ context: {
192
+ user_id: span.attributes["user.id"],
193
+ session_id: span.attributes["session.id"],
194
+ environment: span.attributes["deployment.environment"] || this.config.environment,
195
+ service: {
196
+ name: span.attributes["service.name"],
197
+ version: span.attributes["service.version"] || this.config.version,
198
+ },
199
+ },
200
+ // Events
201
+ events: span.events.map((event) => ({
202
+ name: event.name,
203
+ timestamp: event.timestamp,
204
+ attributes: event.attributes,
205
+ })),
206
+ // Links to related spans
207
+ links: span.links.map((link) => ({
208
+ trace_id: link.traceId,
209
+ span_id: link.spanId,
210
+ attributes: link.attributes,
211
+ })),
212
+ // Additional metadata
213
+ metadata: this.extractMetadata(span.attributes),
214
+ };
215
+ }
216
+ /**
217
+ * Map NeuroLink span type to Laminar type
218
+ */
219
+ mapSpanTypeToLaminarType(type) {
220
+ const typeMap = {
221
+ [SpanType.AGENT_RUN]: "agent",
222
+ [SpanType.WORKFLOW_STEP]: "workflow",
223
+ [SpanType.TOOL_CALL]: "tool",
224
+ [SpanType.MODEL_GENERATION]: "llm",
225
+ [SpanType.EMBEDDING]: "embedding",
226
+ [SpanType.RETRIEVAL]: "retrieval",
227
+ [SpanType.MEMORY]: "memory",
228
+ [SpanType.CONTEXT_COMPACTION]: "custom",
229
+ [SpanType.RAG]: "retrieval",
230
+ [SpanType.EVALUATION]: "custom",
231
+ [SpanType.MCP_TRANSPORT]: "tool",
232
+ [SpanType.MEDIA_GENERATION]: "llm",
233
+ [SpanType.PPT_GENERATION]: "custom",
234
+ [SpanType.WORKFLOW]: "workflow",
235
+ [SpanType.TTS]: "custom",
236
+ [SpanType.SERVER_REQUEST]: "custom",
237
+ [SpanType.CUSTOM]: "custom",
238
+ };
239
+ return typeMap[type] || "custom";
240
+ }
241
+ /**
242
+ * Map span status to Laminar status format
243
+ */
244
+ mapSpanStatus(status) {
245
+ switch (status) {
246
+ case SpanStatus.OK:
247
+ return "success";
248
+ case SpanStatus.ERROR:
249
+ return "error";
250
+ default:
251
+ return "unset";
252
+ }
253
+ }
254
+ /**
255
+ * Extract additional metadata from span attributes
256
+ * Filters out standard attributes that are already handled
257
+ */
258
+ extractMetadata(attributes) {
259
+ const standardKeys = new Set([
260
+ "service.name",
261
+ "service.version",
262
+ "deployment.environment",
263
+ "user.id",
264
+ "session.id",
265
+ "ai.provider",
266
+ "ai.model",
267
+ "ai.model.version",
268
+ "ai.tokens.input",
269
+ "ai.tokens.output",
270
+ "ai.tokens.total",
271
+ "ai.tokens.cache_read",
272
+ "ai.tokens.cache_creation",
273
+ "ai.tokens.reasoning",
274
+ "ai.cost.input",
275
+ "ai.cost.output",
276
+ "ai.cost.total",
277
+ "ai.cost.currency",
278
+ "ai.temperature",
279
+ "ai.max_tokens",
280
+ "ai.top_p",
281
+ "ai.stop_sequences",
282
+ "tool.name",
283
+ "tool.server",
284
+ "tool.success",
285
+ "error.type",
286
+ "error.message",
287
+ "error.stack",
288
+ "error",
289
+ "input",
290
+ "output",
291
+ "expected",
292
+ "scores",
293
+ ]);
294
+ const metadata = {};
295
+ for (const [key, value] of Object.entries(attributes)) {
296
+ if (!standardKeys.has(key) && value !== undefined) {
297
+ metadata[key] = value;
298
+ }
299
+ }
300
+ return metadata;
301
+ }
302
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Langfuse Exporter
3
+ * Exports spans to Langfuse observability platform
4
+ */
5
+ import type { ExporterHealthStatus, ExportResult, LangfuseExporterConfig, SpanData } from "../types/index.js";
6
+ import { BaseExporter } from "./baseExporter.js";
7
+ /**
8
+ * Langfuse exporter for LLM observability
9
+ * Supports traces, generations, spans, and scores
10
+ */
11
+ export declare class LangfuseExporter extends BaseExporter {
12
+ private readonly publicKey;
13
+ private readonly secretKey;
14
+ private readonly baseUrl;
15
+ private readonly release?;
16
+ constructor(config: LangfuseExporterConfig);
17
+ initialize(): Promise<void>;
18
+ exportSpan(span: SpanData): Promise<ExportResult>;
19
+ exportBatch(spans: SpanData[]): Promise<ExportResult>;
20
+ flush(): Promise<void>;
21
+ shutdown(): Promise<void>;
22
+ healthCheck(): Promise<ExporterHealthStatus>;
23
+ /**
24
+ * Verify connectivity to Langfuse API
25
+ */
26
+ protected ping(): Promise<void>;
27
+ /**
28
+ * Create a Langfuse trace
29
+ */
30
+ private createTrace;
31
+ /**
32
+ * Create a Langfuse span
33
+ */
34
+ private createSpan;
35
+ /**
36
+ * Create a Langfuse generation (for LLM calls)
37
+ */
38
+ private createGeneration;
39
+ /**
40
+ * Make API call to Langfuse
41
+ */
42
+ private apiCall;
43
+ /**
44
+ * Extract tags from span attributes
45
+ */
46
+ private extractTags;
47
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Langfuse Exporter
3
+ * Exports spans to Langfuse observability platform
4
+ */
5
+ import { SpanType } from "../types/spanTypes.js";
6
+ import { SpanSerializer } from "../utils/spanSerializer.js";
7
+ import { BaseExporter } from "./baseExporter.js";
8
+ /**
9
+ * Langfuse exporter for LLM observability
10
+ * Supports traces, generations, spans, and scores
11
+ */
12
+ export class LangfuseExporter extends BaseExporter {
13
+ publicKey;
14
+ secretKey;
15
+ baseUrl;
16
+ release;
17
+ constructor(config) {
18
+ super("langfuse", config);
19
+ this.publicKey = config.publicKey;
20
+ this.secretKey = config.secretKey;
21
+ this.baseUrl = config.baseUrl ?? "https://cloud.langfuse.com";
22
+ this.release = config.release;
23
+ }
24
+ async initialize() {
25
+ if (this.initialized) {
26
+ return;
27
+ }
28
+ // Verify credentials with a simple check
29
+ if (!this.publicKey || !this.secretKey) {
30
+ throw new Error("Langfuse publicKey and secretKey are required");
31
+ }
32
+ this.initialized = true;
33
+ this.startFlushInterval(this.config.flushIntervalMs ?? 5000);
34
+ }
35
+ async exportSpan(span) {
36
+ const startTime = Date.now();
37
+ try {
38
+ // Create trace if this is a root span
39
+ if (!span.parentSpanId) {
40
+ await this.createTrace(span);
41
+ }
42
+ // Create span/generation based on type
43
+ if (span.type === SpanType.MODEL_GENERATION) {
44
+ await this.createGeneration(span);
45
+ }
46
+ else {
47
+ await this.createSpan(span);
48
+ }
49
+ return this.createSuccessResult(1, Date.now() - startTime);
50
+ }
51
+ catch (error) {
52
+ return this.createFailureResult([span.spanId], error instanceof Error ? error.message : String(error), Date.now() - startTime);
53
+ }
54
+ }
55
+ async exportBatch(spans) {
56
+ const startTime = Date.now();
57
+ const results = await Promise.allSettled(spans.map((s) => this.exportSpan(s)));
58
+ const successful = results.filter((r) => r.status === "fulfilled" && r.value.success).length;
59
+ const failed = spans.length - successful;
60
+ return {
61
+ success: failed === 0,
62
+ exportedCount: successful,
63
+ failedCount: failed,
64
+ errors: results
65
+ .filter((r) => r.status === "rejected" ||
66
+ (r.status === "fulfilled" && !r.value.success))
67
+ .map((r, i) => ({
68
+ spanId: spans[i].spanId,
69
+ error: r.status === "rejected" ? String(r.reason) : "Export failed",
70
+ retryable: true,
71
+ })),
72
+ durationMs: Date.now() - startTime,
73
+ };
74
+ }
75
+ async flush() {
76
+ if (this.buffer.length > 0) {
77
+ const spans = [...this.buffer];
78
+ this.buffer = [];
79
+ await this.exportBatch(spans);
80
+ }
81
+ }
82
+ async shutdown() {
83
+ await this.flush();
84
+ this.stopFlushInterval();
85
+ this.initialized = false;
86
+ }
87
+ async healthCheck() {
88
+ try {
89
+ await this.withRetry(() => this.ping(), "health check");
90
+ return this.createHealthStatus(true);
91
+ }
92
+ catch {
93
+ return this.createHealthStatus(false, ["Health check failed"]);
94
+ }
95
+ }
96
+ /**
97
+ * Verify connectivity to Langfuse API
98
+ */
99
+ async ping() {
100
+ const credentials = Buffer.from(`${this.publicKey}:${this.secretKey}`).toString("base64");
101
+ const response = await fetch(`${this.baseUrl}/api/public/health`, {
102
+ method: "GET",
103
+ headers: {
104
+ Authorization: `Basic ${credentials}`,
105
+ },
106
+ });
107
+ if (!response.ok && response.status !== 404) {
108
+ // 404 is acceptable as health endpoint may not exist
109
+ throw new Error(`Langfuse API unreachable: ${response.status}`);
110
+ }
111
+ }
112
+ /**
113
+ * Create a Langfuse trace
114
+ */
115
+ async createTrace(span) {
116
+ const body = {
117
+ id: span.traceId,
118
+ name: span.name,
119
+ userId: span.attributes["user.id"],
120
+ sessionId: span.attributes["session.id"],
121
+ metadata: span.attributes,
122
+ release: this.release,
123
+ tags: this.extractTags(span),
124
+ };
125
+ await this.apiCall("/api/public/traces", body);
126
+ }
127
+ /**
128
+ * Create a Langfuse span
129
+ */
130
+ async createSpan(span) {
131
+ const langfuseSpan = SpanSerializer.toLangfuseFormat(span);
132
+ const body = {
133
+ ...langfuseSpan,
134
+ traceId: span.traceId,
135
+ };
136
+ await this.apiCall("/api/public/spans", body);
137
+ }
138
+ /**
139
+ * Create a Langfuse generation (for LLM calls)
140
+ */
141
+ async createGeneration(span) {
142
+ const langfuseSpan = SpanSerializer.toLangfuseFormat(span);
143
+ const body = {
144
+ traceId: span.traceId,
145
+ id: langfuseSpan.id,
146
+ parentObservationId: langfuseSpan.parentObservationId,
147
+ name: langfuseSpan.name,
148
+ startTime: langfuseSpan.startTime,
149
+ endTime: langfuseSpan.endTime,
150
+ model: span.attributes["ai.model"],
151
+ modelParameters: {
152
+ temperature: span.attributes["ai.temperature"],
153
+ maxTokens: span.attributes["ai.max_tokens"],
154
+ topP: span.attributes["ai.top_p"],
155
+ },
156
+ input: langfuseSpan.input,
157
+ output: langfuseSpan.output,
158
+ usage: langfuseSpan.usage,
159
+ metadata: langfuseSpan.metadata,
160
+ level: langfuseSpan.level,
161
+ statusMessage: langfuseSpan.statusMessage,
162
+ };
163
+ await this.apiCall("/api/public/generations", body);
164
+ }
165
+ /**
166
+ * Make API call to Langfuse
167
+ */
168
+ async apiCall(path, body) {
169
+ const credentials = Buffer.from(`${this.publicKey}:${this.secretKey}`).toString("base64");
170
+ const response = await fetch(`${this.baseUrl}${path}`, {
171
+ method: "POST",
172
+ headers: {
173
+ "Content-Type": "application/json",
174
+ Authorization: `Basic ${credentials}`,
175
+ },
176
+ body: JSON.stringify(body),
177
+ });
178
+ if (!response.ok) {
179
+ const text = await response.text();
180
+ throw new Error(`Langfuse API error: ${response.status} - ${text}`);
181
+ }
182
+ }
183
+ /**
184
+ * Extract tags from span attributes
185
+ */
186
+ extractTags(span) {
187
+ const tags = [];
188
+ if (span.attributes["ai.provider"]) {
189
+ tags.push(`provider:${span.attributes["ai.provider"]}`);
190
+ }
191
+ if (span.attributes["ai.model"]) {
192
+ tags.push(`model:${span.attributes["ai.model"]}`);
193
+ }
194
+ if (span.attributes["deployment.environment"]) {
195
+ tags.push(`env:${span.attributes["deployment.environment"]}`);
196
+ }
197
+ return tags;
198
+ }
199
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * LangSmith Exporter
3
+ * Exports spans to LangSmith observability platform
4
+ */
5
+ import type { ExporterHealthStatus, ExportResult, LangSmithExporterConfig, SpanData } from "../types/index.js";
6
+ import { BaseExporter } from "./baseExporter.js";
7
+ /**
8
+ * LangSmith exporter for LangChain ecosystem observability
9
+ * Supports runs with proper type mapping
10
+ */
11
+ export declare class LangSmithExporter extends BaseExporter {
12
+ private readonly apiKey;
13
+ private readonly projectName;
14
+ private readonly endpoint;
15
+ constructor(config: LangSmithExporterConfig);
16
+ initialize(): Promise<void>;
17
+ exportSpan(span: SpanData): Promise<ExportResult>;
18
+ exportBatch(spans: SpanData[]): Promise<ExportResult>;
19
+ flush(): Promise<void>;
20
+ shutdown(): Promise<void>;
21
+ healthCheck(): Promise<ExporterHealthStatus>;
22
+ /**
23
+ * Verify connectivity to LangSmith API
24
+ */
25
+ protected ping(): Promise<void>;
26
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * LangSmith Exporter
3
+ * Exports spans to LangSmith observability platform
4
+ */
5
+ import { logger } from "../../utils/logger.js";
6
+ import { SpanSerializer } from "../utils/spanSerializer.js";
7
+ import { BaseExporter } from "./baseExporter.js";
8
+ /**
9
+ * LangSmith exporter for LangChain ecosystem observability
10
+ * Supports runs with proper type mapping
11
+ */
12
+ export class LangSmithExporter extends BaseExporter {
13
+ apiKey;
14
+ projectName;
15
+ endpoint;
16
+ constructor(config) {
17
+ super("langsmith", config);
18
+ this.apiKey = config.apiKey;
19
+ this.projectName = config.projectName ?? "default";
20
+ this.endpoint = config.endpoint ?? "https://api.smith.langchain.com";
21
+ }
22
+ async initialize() {
23
+ if (this.initialized) {
24
+ return;
25
+ }
26
+ // Verify API key with a test request
27
+ try {
28
+ const response = await fetch(`${this.endpoint}/api/v1/info`, {
29
+ headers: { "x-api-key": this.apiKey },
30
+ });
31
+ if (!response.ok) {
32
+ throw new Error(`LangSmith initialization failed: ${response.statusText}`);
33
+ }
34
+ }
35
+ catch (error) {
36
+ // Allow initialization to proceed even if API is unreachable
37
+ // This enables offline/test scenarios
38
+ logger.warn("[LangSmith] Could not verify API connection:", error instanceof Error ? error.message : error);
39
+ }
40
+ this.initialized = true;
41
+ this.startFlushInterval(this.config.flushIntervalMs ?? 5000);
42
+ }
43
+ async exportSpan(span) {
44
+ const startTime = Date.now();
45
+ try {
46
+ const langsmithRun = SpanSerializer.toLangSmithFormat(span);
47
+ const response = await fetch(`${this.endpoint}/api/v1/runs`, {
48
+ method: "POST",
49
+ headers: {
50
+ "Content-Type": "application/json",
51
+ "x-api-key": this.apiKey,
52
+ },
53
+ body: JSON.stringify({
54
+ ...langsmithRun,
55
+ session_name: this.projectName,
56
+ }),
57
+ });
58
+ if (!response.ok) {
59
+ throw new Error(`Export failed: ${response.statusText}`);
60
+ }
61
+ return this.createSuccessResult(1, Date.now() - startTime);
62
+ }
63
+ catch (error) {
64
+ return this.createFailureResult([span.spanId], error instanceof Error ? error.message : String(error), Date.now() - startTime);
65
+ }
66
+ }
67
+ async exportBatch(spans) {
68
+ const startTime = Date.now();
69
+ try {
70
+ const runs = spans.map((s) => ({
71
+ ...SpanSerializer.toLangSmithFormat(s),
72
+ session_name: this.projectName,
73
+ }));
74
+ const response = await fetch(`${this.endpoint}/api/v1/runs/batch`, {
75
+ method: "POST",
76
+ headers: {
77
+ "Content-Type": "application/json",
78
+ "x-api-key": this.apiKey,
79
+ },
80
+ body: JSON.stringify({ runs }),
81
+ });
82
+ if (!response.ok) {
83
+ throw new Error(`Batch export failed: ${response.statusText}`);
84
+ }
85
+ return this.createSuccessResult(spans.length, Date.now() - startTime);
86
+ }
87
+ catch (error) {
88
+ return this.createFailureResult(spans.map((s) => s.spanId), error instanceof Error ? error.message : String(error), Date.now() - startTime);
89
+ }
90
+ }
91
+ async flush() {
92
+ if (this.buffer.length > 0) {
93
+ const spans = [...this.buffer];
94
+ this.buffer = [];
95
+ await this.exportBatch(spans);
96
+ }
97
+ }
98
+ async shutdown() {
99
+ await this.flush();
100
+ this.stopFlushInterval();
101
+ this.initialized = false;
102
+ }
103
+ async healthCheck() {
104
+ try {
105
+ await this.withRetry(() => this.ping(), "health check");
106
+ return this.createHealthStatus(true);
107
+ }
108
+ catch {
109
+ return this.createHealthStatus(false, ["Health check failed"]);
110
+ }
111
+ }
112
+ /**
113
+ * Verify connectivity to LangSmith API
114
+ */
115
+ async ping() {
116
+ const response = await fetch(`${this.endpoint}/api/v1/info`, {
117
+ headers: { "x-api-key": this.apiKey },
118
+ });
119
+ if (!response.ok) {
120
+ throw new Error(`LangSmith API unreachable: ${response.status}`);
121
+ }
122
+ }
123
+ }