@respan/tracing 1.0.45

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 (75) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +860 -0
  3. package/dist/constants/index.d.ts +8 -0
  4. package/dist/constants/index.js +9 -0
  5. package/dist/constants/index.js.map +1 -0
  6. package/dist/contexts/index.d.ts +1 -0
  7. package/dist/contexts/index.js +2 -0
  8. package/dist/contexts/index.js.map +1 -0
  9. package/dist/contexts/span.d.ts +9 -0
  10. package/dist/contexts/span.js +39 -0
  11. package/dist/contexts/span.js.map +1 -0
  12. package/dist/decorators/base.d.ts +32 -0
  13. package/dist/decorators/base.js +242 -0
  14. package/dist/decorators/base.js.map +1 -0
  15. package/dist/decorators/index.d.ts +1 -0
  16. package/dist/decorators/index.js +2 -0
  17. package/dist/decorators/index.js.map +1 -0
  18. package/dist/index.d.ts +10 -0
  19. package/dist/index.js +8 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/instrumentation/index.d.ts +2 -0
  22. package/dist/instrumentation/index.js +3 -0
  23. package/dist/instrumentation/index.js.map +1 -0
  24. package/dist/instrumentation/loader.d.ts +5 -0
  25. package/dist/instrumentation/loader.js +104 -0
  26. package/dist/instrumentation/loader.js.map +1 -0
  27. package/dist/instrumentation/manager.d.ts +29 -0
  28. package/dist/instrumentation/manager.js +564 -0
  29. package/dist/instrumentation/manager.js.map +1 -0
  30. package/dist/main.d.ts +162 -0
  31. package/dist/main.js +212 -0
  32. package/dist/main.js.map +1 -0
  33. package/dist/processor/composite.d.ts +29 -0
  34. package/dist/processor/composite.js +106 -0
  35. package/dist/processor/composite.js.map +1 -0
  36. package/dist/processor/filtering.d.ts +19 -0
  37. package/dist/processor/filtering.js +78 -0
  38. package/dist/processor/filtering.js.map +1 -0
  39. package/dist/processor/index.d.ts +3 -0
  40. package/dist/processor/index.js +4 -0
  41. package/dist/processor/index.js.map +1 -0
  42. package/dist/processor/manager.d.ts +61 -0
  43. package/dist/processor/manager.js +111 -0
  44. package/dist/processor/manager.js.map +1 -0
  45. package/dist/types/clientTypes.d.ts +188 -0
  46. package/dist/types/clientTypes.js +22 -0
  47. package/dist/types/clientTypes.js.map +1 -0
  48. package/dist/types/decoratorTypes.d.ts +6 -0
  49. package/dist/types/decoratorTypes.js +2 -0
  50. package/dist/types/decoratorTypes.js.map +1 -0
  51. package/dist/types/index.d.ts +3 -0
  52. package/dist/types/index.js +4 -0
  53. package/dist/types/index.js.map +1 -0
  54. package/dist/types/instrumentationTypes.d.ts +25 -0
  55. package/dist/types/instrumentationTypes.js +82 -0
  56. package/dist/types/instrumentationTypes.js.map +1 -0
  57. package/dist/utils/client.d.ts +168 -0
  58. package/dist/utils/client.js +151 -0
  59. package/dist/utils/client.js.map +1 -0
  60. package/dist/utils/context.d.ts +28 -0
  61. package/dist/utils/context.js +44 -0
  62. package/dist/utils/context.js.map +1 -0
  63. package/dist/utils/index.d.ts +5 -0
  64. package/dist/utils/index.js +8 -0
  65. package/dist/utils/index.js.map +1 -0
  66. package/dist/utils/span.d.ts +65 -0
  67. package/dist/utils/span.js +269 -0
  68. package/dist/utils/span.js.map +1 -0
  69. package/dist/utils/spanBuffer.d.ts +94 -0
  70. package/dist/utils/spanBuffer.js +147 -0
  71. package/dist/utils/spanBuffer.js.map +1 -0
  72. package/dist/utils/tracing.d.ts +31 -0
  73. package/dist/utils/tracing.js +239 -0
  74. package/dist/utils/tracing.js.map +1 -0
  75. package/package.json +72 -0
package/README.md ADDED
@@ -0,0 +1,860 @@
1
+ # KeywordsAI Tracing SDK
2
+
3
+ A lightweight OpenTelemetry-based tracing SDK for KeywordsAI, built with minimal dependencies and optional instrumentation support.
4
+ Inspired by [Openllmetry](https://github.com/traceloop/openllmetry-js)
5
+
6
+ ## Features
7
+
8
+ - **Lightweight Core**: Minimal dependencies for browser and Node.js compatibility
9
+ - **Optional Instrumentations**: Install only the instrumentations you need
10
+ - **OpenTelemetry Native**: Built directly on OpenTelemetry without wrapper dependencies
11
+ - **Decorator Pattern**: Easy-to-use decorators for workflows, tasks, agents, and tools
12
+ - **Dynamic Loading**: Instrumentations are loaded on-demand
13
+ - **Manual Instrumentation**: Support for manual instrumentation (Next.js compatible)
14
+ - **Span Management**: Full control over spans with `getClient()` API
15
+ - **Multi-Processor Routing**: Route spans to multiple destinations
16
+ - **Span Buffering**: Manual control over span export timing
17
+ - **KeywordsAI Parameters**: Add customer identifiers and trace group identifiers
18
+
19
+ ## Installation
20
+
21
+ ### Core Package
22
+ ```bash
23
+ npm install @keywordsai/tracing
24
+ ```
25
+
26
+ ### Optional Instrumentations
27
+ Install only the instrumentations you need:
28
+
29
+ ```bash
30
+ # OpenAI
31
+ npm install @traceloop/instrumentation-openai
32
+
33
+ # Anthropic
34
+ npm install @traceloop/instrumentation-anthropic
35
+
36
+ # Azure OpenAI
37
+ npm install @traceloop/instrumentation-azure
38
+
39
+ # AWS Bedrock
40
+ npm install @traceloop/instrumentation-bedrock
41
+
42
+ # Cohere
43
+ npm install @traceloop/instrumentation-cohere
44
+
45
+ # LangChain
46
+ npm install @traceloop/instrumentation-langchain
47
+
48
+ # LlamaIndex
49
+ npm install @traceloop/instrumentation-llamaindex
50
+
51
+ # Vector Databases
52
+ npm install @traceloop/instrumentation-pinecone
53
+ npm install @traceloop/instrumentation-chromadb
54
+ npm install @traceloop/instrumentation-qdrant
55
+
56
+ # Other providers
57
+ npm install @traceloop/instrumentation-together
58
+ npm install @traceloop/instrumentation-vertexai
59
+ ```
60
+
61
+ ## Quick Start
62
+
63
+ ### Method 1: Dynamic Instrumentation (Recommended for Node.js)
64
+
65
+ ```typescript
66
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
67
+ import OpenAI from 'openai';
68
+
69
+ // Initialize the SDK
70
+ const keywordsAi = new KeywordsAITelemetry({
71
+ apiKey: process.env.KEYWORDSAI_API_KEY,
72
+ baseURL: process.env.KEYWORDSAI_BASE_URL,
73
+ appName: 'my-app'
74
+ });
75
+
76
+ // Enable instrumentations you need
77
+ await keywordsAi.enableInstrumentation('openai');
78
+
79
+ const openai = new OpenAI();
80
+
81
+ // Use decorators to trace your functions
82
+ const generateJoke = async () => {
83
+ return await keywordsAi.withTask(
84
+ { name: 'joke_generation' },
85
+ async () => {
86
+ const completion = await openai.chat.completions.create({
87
+ messages: [{ role: 'user', content: 'Tell me a joke' }],
88
+ model: 'gpt-3.5-turbo'
89
+ });
90
+ return completion.choices[0].message.content;
91
+ }
92
+ );
93
+ };
94
+ ```
95
+
96
+ ### Method 2: Manual Instrumentation (Recommended for Next.js)
97
+
98
+ ```typescript
99
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
100
+ import OpenAI from 'openai';
101
+ import Anthropic from '@anthropic-ai/sdk';
102
+
103
+ // Manual instrumentation - pass the actual imported modules
104
+ const keywordsAi = new KeywordsAITelemetry({
105
+ apiKey: process.env.KEYWORDSAI_API_KEY,
106
+ baseURL: process.env.KEYWORDSAI_BASE_URL,
107
+ appName: 'my-app',
108
+ // Specify modules to instrument manually
109
+ instrumentModules: {
110
+ openAI: OpenAI,
111
+ anthropic: Anthropic,
112
+ // Add other modules as needed
113
+ }
114
+ });
115
+
116
+ // Wait for initialization (optional but recommended)
117
+ await keywordsAi.initialize();
118
+
119
+ // Create clients - they will be automatically instrumented
120
+ const openai = new OpenAI();
121
+ const anthropic = new Anthropic();
122
+
123
+ // Use decorators to trace your functions
124
+ const generateContent = async () => {
125
+ return await keywordsAi.withWorkflow(
126
+ { name: 'content_generation', version: 1 },
127
+ async () => {
128
+ const result = await openai.chat.completions.create({
129
+ messages: [{ role: 'user', content: 'Generate content' }],
130
+ model: 'gpt-3.5-turbo'
131
+ });
132
+ return result.choices[0].message.content;
133
+ }
134
+ );
135
+ };
136
+ ```
137
+
138
+ ## When to Use Each Method
139
+
140
+ ### Dynamic Instrumentation
141
+ - **Best for**: Standard Node.js applications, serverless functions
142
+ - **Pros**: Simple setup, automatic loading
143
+ - **Cons**: May not work in all bundling environments
144
+
145
+ ### Manual Instrumentation
146
+ - **Best for**: Next.js, Webpack bundled apps, environments with import restrictions
147
+ - **Pros**: Works in all environments, explicit control, better for tree-shaking
148
+ - **Cons**: Requires importing modules explicitly
149
+
150
+ ## API Reference
151
+
152
+ ### KeywordsAITelemetry
153
+
154
+ #### Constructor Options
155
+
156
+ ```typescript
157
+ interface KeywordsAIOptions {
158
+ appName?: string; // App name for traces
159
+ apiKey?: string; // KeywordsAI API key
160
+ baseURL?: string; // KeywordsAI base URL
161
+ disableBatch?: boolean; // Disable batching for development
162
+ logLevel?: "debug" | "info" | "warn" | "error";
163
+ traceContent?: boolean; // Log prompts and completions
164
+ tracingEnabled?: boolean; // Enable/disable tracing
165
+ silenceInitializationMessage?: boolean;
166
+
167
+ // Advanced options
168
+ resourceAttributes?: Record<string, string>; // Custom resource attributes
169
+ spanPostprocessCallback?: (span: any) => void; // Span postprocessing callback
170
+
171
+ // Manual instrumentation modules
172
+ instrumentModules?: {
173
+ openAI?: typeof OpenAI;
174
+ anthropic?: typeof Anthropic;
175
+ azureOpenAI?: typeof AzureOpenAI;
176
+ cohere?: typeof Cohere;
177
+ bedrock?: typeof BedrockRuntime;
178
+ google_vertexai?: typeof VertexAI;
179
+ google_aiplatform?: typeof AIPlatform;
180
+ pinecone?: typeof Pinecone;
181
+ together?: typeof Together;
182
+ langchain?: {
183
+ chainsModule?: typeof ChainsModule;
184
+ agentsModule?: typeof AgentsModule;
185
+ toolsModule?: typeof ToolsModule;
186
+ runnablesModule?: typeof RunnableModule;
187
+ vectorStoreModule?: typeof VectorStoreModule;
188
+ };
189
+ llamaIndex?: typeof LlamaIndex;
190
+ chromadb?: typeof ChromaDB;
191
+ qdrant?: typeof Qdrant;
192
+ };
193
+ }
194
+ ```
195
+
196
+ #### Methods
197
+
198
+ - `initialize()` - Manually initialize tracing (returns Promise)
199
+ - `isInitialized()` - Check if tracing has been initialized
200
+ - `enableInstrumentation(name: string)` - Enable a specific instrumentation (dynamic method)
201
+ - `enableInstrumentations(names: string[])` - Enable multiple instrumentations (dynamic method)
202
+ - `addProcessor(config: ProcessorConfig)` - Add a processor for routing spans
203
+ - `getClient()` - Get the client API for span management
204
+ - `getSpanBufferManager()` - Get the span buffer manager
205
+ - `shutdown()` - Flush and shutdown tracing
206
+
207
+ ### Decorators
208
+
209
+ #### withWorkflow
210
+ Trace high-level workflows:
211
+ ```typescript
212
+ await keywordsAi.withWorkflow(
213
+ { name: 'my_workflow', version: 1 },
214
+ async () => {
215
+ // Your workflow logic
216
+ }
217
+ );
218
+ ```
219
+
220
+ #### withTask
221
+ Trace individual tasks:
222
+ ```typescript
223
+ await keywordsAi.withTask(
224
+ { name: 'my_task' },
225
+ async () => {
226
+ // Your task logic
227
+ }
228
+ );
229
+ ```
230
+
231
+ #### withAgent
232
+ Trace agent operations:
233
+ ```typescript
234
+ await keywordsAi.withAgent(
235
+ { name: 'my_agent', associationProperties: { type: 'assistant' } },
236
+ async () => {
237
+ // Your agent logic
238
+ }
239
+ );
240
+ ```
241
+
242
+ #### withTool
243
+ Trace tool usage:
244
+ ```typescript
245
+ await keywordsAi.withTool(
246
+ { name: 'my_tool' },
247
+ async () => {
248
+ // Your tool logic
249
+ }
250
+ );
251
+ ```
252
+
253
+ ### Decorator Configuration
254
+
255
+ ```typescript
256
+ interface DecoratorConfig {
257
+ name: string; // Required: Name of the operation
258
+ version?: number; // Optional: Version number
259
+ associationProperties?: Record<string, string>; // Optional: Additional metadata
260
+ traceContent?: boolean; // Optional: Override trace content setting
261
+ inputParameters?: unknown[]; // Optional: Custom input parameters
262
+ suppressTracing?: boolean; // Optional: Suppress tracing for this operation
263
+ processors?: string | string[]; // Optional: Route to specific processor(s)
264
+ }
265
+ ```
266
+
267
+ ## Advanced Features
268
+
269
+ ### Span Management with getClient()
270
+
271
+ Get full control over your spans with the client API:
272
+
273
+ ```typescript
274
+ import { KeywordsAITelemetry, getClient } from '@keywordsai/tracing';
275
+
276
+ const kai = new KeywordsAITelemetry({ apiKey: 'your-key' });
277
+ await kai.initialize();
278
+
279
+ await kai.withTask({ name: 'process_data' }, async () => {
280
+ const client = getClient();
281
+
282
+ // Get current trace and span IDs
283
+ const traceId = client.getCurrentTraceId();
284
+ const spanId = client.getCurrentSpanId();
285
+ console.log(`Trace: ${traceId}, Span: ${spanId}`);
286
+
287
+ // Update span with KeywordsAI parameters
288
+ client.updateCurrentSpan({
289
+ keywordsaiParams: {
290
+ customerIdentifier: 'user-123',
291
+ traceGroupIdentifier: 'data-pipeline',
292
+ metadata: {
293
+ version: '1.0',
294
+ environment: 'production'
295
+ }
296
+ }
297
+ });
298
+
299
+ // Add events to track progress
300
+ client.addEvent('validation_started', { records: 1000 });
301
+
302
+ // Your processing logic here
303
+
304
+ client.addEvent('validation_completed', { status: 'success' });
305
+
306
+ // Record exceptions
307
+ try {
308
+ // risky operation
309
+ } catch (error) {
310
+ client.recordException(error as Error);
311
+ throw error;
312
+ }
313
+ });
314
+ ```
315
+
316
+ **Available Client Methods:**
317
+ - `getCurrentTraceId()` - Get the current trace ID
318
+ - `getCurrentSpanId()` - Get the current span ID
319
+ - `updateCurrentSpan(options)` - Update span attributes, name, status, or KeywordsAI params
320
+ - `addEvent(name, attributes?)` - Add an event to the current span
321
+ - `recordException(exception)` - Record an exception on the current span
322
+ - `isRecording()` - Check if the span is recording
323
+ - `getTracer()` - Get the tracer for manual span creation
324
+ - `flush()` - Force flush all pending spans
325
+
326
+ ### Multi-Processor Routing
327
+
328
+ Route spans to different destinations based on processor names:
329
+
330
+ ```typescript
331
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
332
+
333
+ const kai = new KeywordsAITelemetry({ apiKey: 'your-key' });
334
+
335
+ // Add a debug processor (in addition to default KeywordsAI processor)
336
+ kai.addProcessor({
337
+ exporter: new YourCustomExporter(),
338
+ name: 'debug',
339
+ filter: (span) => span.attributes['environment'] === 'development'
340
+ });
341
+
342
+ // Route specific spans to debug processor
343
+ await kai.withTask(
344
+ { name: 'debug_task', processors: 'debug' },
345
+ async () => {
346
+ // This span goes to the debug processor
347
+ }
348
+ );
349
+
350
+ // Route to multiple processors
351
+ await kai.withTask(
352
+ { name: 'important_task', processors: ['debug', 'analytics'] },
353
+ async () => {
354
+ // This span goes to both processors
355
+ }
356
+ );
357
+
358
+ // Default behavior - no processors attribute
359
+ await kai.withTask(
360
+ { name: 'normal_task' },
361
+ async () => {
362
+ // This span goes to the default KeywordsAI processor
363
+ }
364
+ );
365
+ ```
366
+
367
+ **Processor Configuration:**
368
+ ```typescript
369
+ interface ProcessorConfig {
370
+ exporter: SpanExporter; // The span exporter to use
371
+ name: string; // Processor identifier for routing
372
+ filter?: (span: ReadableSpan) => boolean; // Optional custom filter
373
+ priority?: number; // Optional priority (higher = processed first)
374
+ }
375
+ ```
376
+
377
+ ### Span Buffering for Manual Control
378
+
379
+ Buffer spans and control when they're exported:
380
+
381
+ ```typescript
382
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
383
+
384
+ const kai = new KeywordsAITelemetry({ apiKey: 'your-key' });
385
+ const manager = kai.getSpanBufferManager();
386
+
387
+ // Create a buffer (spans won't be auto-exported)
388
+ const buffer = manager.createBuffer('workflow-123');
389
+
390
+ // Add spans to the buffer
391
+ buffer.createSpan('validation', {
392
+ status: 'success',
393
+ duration_ms: 10
394
+ });
395
+
396
+ buffer.createSpan('processing', {
397
+ status: 'success',
398
+ duration_ms: 100
399
+ });
400
+
401
+ // Get buffered spans (they're transportable!)
402
+ const spans = buffer.getAllSpans();
403
+ console.log(`Collected ${spans.length} spans`);
404
+
405
+ // Conditionally process based on business logic
406
+ const isSuccessful = true; // Your business logic
407
+ const isPremiumUser = true; // Your business logic
408
+
409
+ if (isSuccessful && isPremiumUser) {
410
+ // Export to KeywordsAI
411
+ await manager.processSpans(spans);
412
+ } else {
413
+ // Discard spans
414
+ buffer.clearSpans();
415
+ }
416
+ ```
417
+
418
+ **Use Cases for Span Buffering:**
419
+ - Backend systems that need delayed span export
420
+ - Conditional export based on business logic
421
+ - Batch processing of spans
422
+ - Async span creation (create spans after execution)
423
+ - Experiment tracking with selective export
424
+
425
+ **SpanBuffer Methods:**
426
+ - `createSpan(name, attributes?, kind?)` - Create a span in the buffer
427
+ - `getAllSpans()` - Get all buffered spans as a transportable array
428
+ - `getSpanCount()` - Get the number of buffered spans
429
+ - `clearSpans()` - Discard all buffered spans without exporting
430
+
431
+ ### KeywordsAI-Specific Parameters
432
+
433
+ Add customer and trace group identifiers to your spans:
434
+
435
+ ```typescript
436
+ import { getClient } from '@keywordsai/tracing';
437
+
438
+ await kai.withWorkflow({ name: 'user_workflow' }, async () => {
439
+ const client = getClient();
440
+
441
+ client.updateCurrentSpan({
442
+ keywordsaiParams: {
443
+ // Group traces by customer
444
+ customerIdentifier: 'user-123',
445
+
446
+ // Organize traces into groups
447
+ traceGroupIdentifier: 'onboarding-flow',
448
+
449
+ // Add custom metadata
450
+ metadata: {
451
+ plan: 'premium',
452
+ region: 'us-east-1',
453
+ version: '2.1.0'
454
+ }
455
+ }
456
+ });
457
+ });
458
+ ```
459
+
460
+ These parameters help you:
461
+ - Group traces by customer for user-level analytics
462
+ - Organize traces into logical groups (experiments, features, etc.)
463
+ - Add custom metadata for filtering and analysis
464
+
465
+ ## Available Instrumentations
466
+
467
+ The following instrumentations can be enabled dynamically:
468
+
469
+ - `openai` - OpenAI API calls
470
+ - `anthropic` - Anthropic API calls
471
+ - `azure` - Azure OpenAI API calls
472
+ - `bedrock` - AWS Bedrock API calls
473
+ - `cohere` - Cohere API calls
474
+ - `langchain` - LangChain operations
475
+ - `llamaindex` - LlamaIndex operations
476
+ - `pinecone` - Pinecone vector database
477
+ - `chromadb` - ChromaDB vector database
478
+ - `qdrant` - Qdrant vector database
479
+ - `together` - Together AI API calls
480
+ - `vertexai` - Google Vertex AI API calls
481
+
482
+ ## Troubleshooting Instrumentation
483
+
484
+ ### Error: "instrumentation failed to initialize"
485
+
486
+ This usually means the instrumentation package is missing. Install it:
487
+
488
+ ```bash
489
+ # For Anthropic
490
+ npm install @traceloop/instrumentation-anthropic
491
+
492
+ # For OpenAI
493
+ npm install @traceloop/instrumentation-openai
494
+ ```
495
+
496
+ ### Error: "Cannot read properties of undefined (reading 'prototype')"
497
+
498
+ This means you're using manual instrumentation but passed the wrong module. Make sure to:
499
+
500
+ ```typescript
501
+ // ✅ CORRECT - Pass the class/module itself
502
+ instrumentModules: {
503
+ anthropic: Anthropic // The imported class
504
+ }
505
+
506
+ // ❌ WRONG - Don't pass an instance
507
+ instrumentModules: {
508
+ anthropic: new Anthropic() // This won't work
509
+ }
510
+ ```
511
+
512
+ ### Dynamic instrumentation not working in Next.js/Webpack?
513
+
514
+ Use manual instrumentation instead:
515
+
516
+ ```typescript
517
+ // Instead of this:
518
+ await kai.enableInstrumentation('anthropic');
519
+
520
+ // Use this:
521
+ const kai = new KeywordsAITelemetry({
522
+ instrumentModules: {
523
+ anthropic: Anthropic
524
+ }
525
+ });
526
+ ```
527
+
528
+ ### Anthropic spans not appearing?
529
+
530
+ **Known Issue**: `@traceloop/instrumentation-anthropic@0.22.2` doesn't work with `@anthropic-ai/sdk@0.71+`
531
+
532
+ **Tested Working Versions:**
533
+ - ✅ `@anthropic-ai/sdk@^0.20.0` to `@^0.41.0` - Full tracing support with all metrics
534
+ - ❌ `@anthropic-ai/sdk@0.50.0+` - No spans created (breaking changes)
535
+
536
+ **Official Support**: The instrumentation package officially supports SDK `0.9.1 and later`, but breaking changes in SDK `v0.50.0` prevent spans from being created. Last confirmed working version: `0.41.0`.
537
+
538
+ **Solutions:**
539
+
540
+ 1. **Use a compatible version** (recommended - use latest working version):
541
+ ```bash
542
+ npm install @anthropic-ai/sdk@^0.41.0
543
+ # or
544
+ yarn add @anthropic-ai/sdk@^0.41.0
545
+ ```
546
+ Or use any version between `0.20.0` and `0.41.0`
547
+
548
+ 2. **Use OpenAI instead** - Fully supported with latest SDK versions
549
+
550
+ **What Works with 0.20.x:**
551
+ - ✅ All token metrics (`gen_ai.usage.prompt_tokens`, `gen_ai.usage.completion_tokens`)
552
+ - ✅ Full request/response content tracing
553
+ - ✅ Model information and metadata
554
+ - ✅ Proper span hierarchy within workflows
555
+
556
+ **Status**: SDK v0.50.0 introduced breaking changes that broke instrumentation compatibility. Waiting for `@traceloop` to update their package.
557
+
558
+ ### Spans not showing up?
559
+
560
+ 1. Check that you're using decorators (`withTask`, `withWorkflow`, etc.)
561
+ 2. Verify API key is set: `process.env.KEYWORDSAI_API_KEY`
562
+ 3. Enable debug logging: `logLevel: 'debug'`
563
+ 4. Check network requests to KeywordsAI endpoint
564
+
565
+ ## Environment Variables
566
+
567
+ - `KEYWORDSAI_API_KEY`: Your KeywordsAI API key
568
+ - `KEYWORDSAI_BASE_URL`: KeywordsAI base URL (default: https://api.keywordsai.co)
569
+ - `KEYWORDSAI_APP_NAME`: Default app name
570
+ - `KEYWORDSAI_TRACE_CONTENT`: Enable/disable content tracing (default: true)
571
+
572
+ ## Provider-Specific Examples
573
+
574
+ ### OpenAI
575
+
576
+ **Method 1: Dynamic Instrumentation (Simple)**
577
+ ```typescript
578
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
579
+ import OpenAI from 'openai';
580
+
581
+ const kai = new KeywordsAITelemetry({
582
+ apiKey: process.env.KEYWORDSAI_API_KEY,
583
+ appName: 'openai-app'
584
+ });
585
+
586
+ // Enable OpenAI instrumentation dynamically
587
+ await kai.enableInstrumentation('openai');
588
+
589
+ const openai = new OpenAI();
590
+
591
+ await kai.withTask({ name: 'chat' }, async () => {
592
+ const response = await openai.chat.completions.create({
593
+ model: 'gpt-4',
594
+ messages: [{ role: 'user', content: 'Hello!' }]
595
+ });
596
+ console.log(response.choices[0].message.content);
597
+ });
598
+ ```
599
+
600
+ **Method 2: Manual Instrumentation (Next.js/Webpack)**
601
+ ```typescript
602
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
603
+ import OpenAI from 'openai';
604
+
605
+ const kai = new KeywordsAITelemetry({
606
+ apiKey: process.env.KEYWORDSAI_API_KEY,
607
+ appName: 'openai-app',
608
+ instrumentModules: {
609
+ openAI: OpenAI // Pass the OpenAI class
610
+ }
611
+ });
612
+
613
+ await kai.initialize();
614
+
615
+ const openai = new OpenAI();
616
+
617
+ await kai.withTask({ name: 'chat' }, async () => {
618
+ const response = await openai.chat.completions.create({
619
+ model: 'gpt-4',
620
+ messages: [{ role: 'user', content: 'Hello!' }]
621
+ });
622
+ console.log(response.choices[0].message.content);
623
+ });
624
+ ```
625
+
626
+ ### Anthropic (Claude)
627
+
628
+ ```typescript
629
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
630
+ import Anthropic from '@anthropic-ai/sdk';
631
+
632
+ // Initialize with Anthropic instrumentation
633
+ const kai = new KeywordsAITelemetry({
634
+ apiKey: process.env.KEYWORDSAI_API_KEY,
635
+ appName: 'anthropic-app',
636
+ instrumentModules: {
637
+ anthropic: Anthropic // Pass the Anthropic class
638
+ }
639
+ });
640
+
641
+ await kai.initialize();
642
+
643
+ // Create Anthropic client (will be auto-instrumented)
644
+ const anthropic = new Anthropic({
645
+ apiKey: process.env.ANTHROPIC_API_KEY
646
+ });
647
+
648
+ // Use with decorators
649
+ await kai.withTask({ name: 'generate_text' }, async () => {
650
+ const response = await anthropic.messages.create({
651
+ model: 'claude-3-haiku-20240307',
652
+ max_tokens: 1024,
653
+ messages: [{ role: 'user', content: 'Hello, Claude!' }]
654
+ });
655
+
656
+ console.log(response.content[0].text);
657
+ });
658
+
659
+ // Streaming example
660
+ await kai.withTask({ name: 'stream_text' }, async () => {
661
+ const stream = await anthropic.messages.create({
662
+ model: 'claude-3-haiku-20240307',
663
+ max_tokens: 1024,
664
+ messages: [{ role: 'user', content: 'Count to 10' }],
665
+ stream: true
666
+ });
667
+
668
+ for await (const event of stream) {
669
+ if (event.type === 'content_block_delta' &&
670
+ event.delta.type === 'text_delta') {
671
+ process.stdout.write(event.delta.text);
672
+ }
673
+ }
674
+ });
675
+ ```
676
+
677
+ **Note**: Make sure to install the Anthropic instrumentation:
678
+ ```bash
679
+ npm install @anthropic-ai/sdk @traceloop/instrumentation-anthropic
680
+ ```
681
+
682
+ ## Complete Examples
683
+
684
+ ### Example 1: Full Workflow with Span Management
685
+
686
+ ```typescript
687
+ import { KeywordsAITelemetry, getClient } from '@keywordsai/tracing';
688
+ import OpenAI from 'openai';
689
+
690
+ const kai = new KeywordsAITelemetry({
691
+ apiKey: process.env.KEYWORDSAI_API_KEY,
692
+ appName: 'my-app',
693
+ resourceAttributes: {
694
+ environment: 'production',
695
+ version: '1.0.0'
696
+ }
697
+ });
698
+
699
+ await kai.initialize();
700
+ const openai = new OpenAI();
701
+
702
+ await kai.withWorkflow({ name: 'process_user_request', version: 1 }, async () => {
703
+ const client = getClient();
704
+
705
+ // Set customer context
706
+ client.updateCurrentSpan({
707
+ keywordsaiParams: {
708
+ customerIdentifier: 'user-123',
709
+ traceGroupIdentifier: 'onboarding'
710
+ }
711
+ });
712
+
713
+ // Track progress with events
714
+ client.addEvent('validation_started');
715
+
716
+ // Nested task
717
+ await kai.withTask({ name: 'validate_input' }, async () => {
718
+ // Validation logic
719
+ });
720
+
721
+ client.addEvent('ai_processing_started');
722
+
723
+ // LLM call (auto-instrumented)
724
+ const response = await openai.chat.completions.create({
725
+ model: 'gpt-3.5-turbo',
726
+ messages: [{ role: 'user', content: 'Process this' }]
727
+ });
728
+
729
+ client.addEvent('ai_processing_completed', {
730
+ tokens: response.usage?.total_tokens
731
+ });
732
+
733
+ return response.choices[0].message.content;
734
+ });
735
+ ```
736
+
737
+ ### Example 2: Backend Workflow with Span Buffering
738
+
739
+ ```typescript
740
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
741
+
742
+ const kai = new KeywordsAITelemetry({ apiKey: 'your-key' });
743
+ const manager = kai.getSpanBufferManager();
744
+
745
+ // Ingest workflow results from backend
746
+ async function ingestWorkflow(workflowResult: any, orgId: string) {
747
+ const buffer = manager.createBuffer(`workflow-${workflowResult.id}`);
748
+
749
+ // Create spans from workflow results
750
+ buffer.createSpan('workflow_execution', {
751
+ organization_id: orgId,
752
+ input: workflowResult.input,
753
+ output: workflowResult.output,
754
+ duration_ms: workflowResult.duration
755
+ });
756
+
757
+ for (const step of workflowResult.steps) {
758
+ buffer.createSpan(`step_${step.name}`, {
759
+ input: step.input,
760
+ output: step.output,
761
+ duration_ms: step.duration
762
+ });
763
+ }
764
+
765
+ // Get transportable spans
766
+ const spans = buffer.getAllSpans();
767
+
768
+ // Conditionally export based on business logic
769
+ const isPremium = orgId.includes('premium');
770
+
771
+ if (isPremium) {
772
+ await manager.processSpans(spans);
773
+ console.log('Exported spans for premium org');
774
+ } else {
775
+ buffer.clearSpans();
776
+ console.log('Skipped spans for free org');
777
+ }
778
+ }
779
+ ```
780
+
781
+ ### Example 3: Multi-Destination Routing
782
+
783
+ ```typescript
784
+ import { KeywordsAITelemetry } from '@keywordsai/tracing';
785
+ import { FileExporter, AnalyticsExporter } from './exporters';
786
+
787
+ const kai = new KeywordsAITelemetry({ apiKey: 'your-key' });
788
+
789
+ // Add debug file exporter
790
+ kai.addProcessor({
791
+ exporter: new FileExporter('./debug-spans.jsonl'),
792
+ name: 'debug'
793
+ });
794
+
795
+ // Add analytics exporter with filter
796
+ kai.addProcessor({
797
+ exporter: new AnalyticsExporter(),
798
+ name: 'analytics',
799
+ filter: (span) => !span.name.includes('test')
800
+ });
801
+
802
+ // Route to default KeywordsAI processor
803
+ await kai.withTask(
804
+ { name: 'production_task' },
805
+ async () => { /* goes to KeywordsAI */ }
806
+ );
807
+
808
+ // Route to debug processor
809
+ await kai.withTask(
810
+ { name: 'debug_task', processors: 'debug' },
811
+ async () => { /* goes to file */ }
812
+ );
813
+
814
+ // Route to multiple processors
815
+ await kai.withTask(
816
+ { name: 'important_task', processors: ['debug', 'analytics'] },
817
+ async () => { /* goes to file + analytics */ }
818
+ );
819
+ ```
820
+
821
+ ## Browser Compatibility
822
+
823
+ The core package is designed to work in both Node.js and browser environments. However, some instrumentations may be Node.js only.
824
+
825
+ ## Testing Builds
826
+
827
+ Before publishing, test the built package:
828
+
829
+ ```bash
830
+ npm run test:build
831
+ ```
832
+
833
+ This builds, packs, installs, and tests the package exactly as users will receive it.
834
+
835
+ ## Examples Directory
836
+
837
+ Check out the `examples/` directory for more comprehensive examples:
838
+ - `span-management-example.ts` - Full span management with getClient()
839
+ - `multi-processor-example.ts` - Multi-processor routing examples
840
+ - `span-buffer-example.ts` - Span buffering patterns
841
+ - `basic-usage.ts` - Basic usage patterns
842
+ - `advanced-tracing-example.ts` - Advanced tracing patterns
843
+
844
+ ## Migration from v1.0.x
845
+
846
+ All new features are **backward compatible**. Existing code will continue to work without modifications:
847
+
848
+ - Default processor is automatically configured
849
+ - New methods are additive (optional)
850
+ - No breaking changes to existing APIs
851
+
852
+ To use new features, simply import and use them:
853
+ ```typescript
854
+ import { getClient } from '@keywordsai/tracing'; // New in v1.1.0
855
+ ```
856
+
857
+ ## License
858
+
859
+ Apache-2.0
860
+