@fallom/trace 0.1.11 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -2,10 +2,10 @@ import { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';
2
2
  import { ExportResult } from '@opentelemetry/core';
3
3
 
4
4
  /**
5
- * Fallom tracing module.
6
- *
7
- * Auto-instruments all LLM calls via OTEL and groups them by session.
8
- * Also supports custom spans for business metrics.
5
+ * Type definitions for Fallom tracing module.
6
+ */
7
+ /**
8
+ * Session context for grouping traces.
9
9
  */
10
10
  interface SessionContext {
11
11
  configKey: string;
@@ -13,27 +13,92 @@ interface SessionContext {
13
13
  customerId?: string;
14
14
  }
15
15
  /**
16
- * Initialize Fallom tracing. Auto-instruments all LLM calls.
16
+ * Trace context for linking spans together.
17
+ */
18
+ interface TraceContext {
19
+ traceId: string;
20
+ parentSpanId?: string;
21
+ }
22
+ /**
23
+ * Data structure for a trace sent to the Fallom API.
24
+ */
25
+ interface TraceData {
26
+ config_key: string;
27
+ session_id: string;
28
+ customer_id?: string;
29
+ trace_id: string;
30
+ span_id: string;
31
+ parent_span_id?: string;
32
+ name: string;
33
+ kind?: string;
34
+ model?: string;
35
+ start_time: string;
36
+ end_time: string;
37
+ duration_ms: number;
38
+ status: "OK" | "ERROR";
39
+ error_message?: string;
40
+ prompt_tokens?: number;
41
+ completion_tokens?: number;
42
+ total_tokens?: number;
43
+ time_to_first_token_ms?: number;
44
+ is_streaming?: boolean;
45
+ attributes?: Record<string, unknown>;
46
+ prompt_key?: string;
47
+ prompt_version?: number;
48
+ prompt_ab_test_key?: string;
49
+ prompt_variant_index?: number;
50
+ }
51
+ /**
52
+ * Options for creating a Fallom session.
53
+ */
54
+ interface SessionOptions {
55
+ /** Your config name (e.g., "linkedin-agent") */
56
+ configKey: string;
57
+ /** Your session/conversation ID */
58
+ sessionId: string;
59
+ /** Optional customer/user identifier for analytics */
60
+ customerId?: string;
61
+ }
62
+ /**
63
+ * Options for wrapAISDK.
64
+ */
65
+ interface WrapAISDKOptions {
66
+ /**
67
+ * Enable debug logging to see the raw Vercel AI SDK response structure.
68
+ * Useful for debugging token extraction issues with different providers.
69
+ */
70
+ debug?: boolean;
71
+ }
72
+
73
+ /**
74
+ * Core Fallom tracing functionality.
75
+ *
76
+ * Handles initialization and trace sending.
77
+ * Session management is now handled by FallomSession.
78
+ */
79
+
80
+ /**
81
+ * Initialize Fallom tracing.
17
82
  *
18
83
  * @param options - Configuration options
19
84
  * @param options.apiKey - Your Fallom API key. Defaults to FALLOM_API_KEY env var.
20
- * @param options.baseUrl - API base URL. Defaults to FALLOM_BASE_URL env var, or https://spans.fallom.com
21
- * @param options.captureContent - Whether to capture prompt/completion content in traces.
22
- * Set to false for privacy/compliance. Defaults to true.
23
- * Also respects FALLOM_CAPTURE_CONTENT env var ("true"/"false").
85
+ * @param options.baseUrl - API base URL. Defaults to https://traces.fallom.com
86
+ * @param options.captureContent - Whether to capture prompt/completion content.
87
+ * @param options.debug - Enable debug logging.
24
88
  *
25
89
  * @example
26
90
  * ```typescript
27
- * import fallom from 'fallom';
91
+ * import fallom from '@fallom/trace';
28
92
  *
29
- * // Normal usage (captures everything)
30
- * fallom.trace.init();
93
+ * await fallom.init({ apiKey: process.env.FALLOM_API_KEY });
31
94
  *
32
- * // Privacy mode (no prompts/completions stored)
33
- * fallom.trace.init({ captureContent: false });
95
+ * const session = fallom.session({
96
+ * configKey: "my-agent",
97
+ * sessionId: "session-123",
98
+ * });
34
99
  *
35
- * fallom.trace.setSession("my-agent", sessionId);
36
- * await agent.run(message); // Automatically traced
100
+ * const { generateText } = session.wrapAISDK(ai);
101
+ * await generateText({ model: openai("gpt-4o"), prompt: "Hello!" });
37
102
  * ```
38
103
  */
39
104
  declare function init$3(options?: {
@@ -42,228 +107,122 @@ declare function init$3(options?: {
42
107
  captureContent?: boolean;
43
108
  debug?: boolean;
44
109
  }): Promise<void>;
45
- /**
46
- * Set the current session context.
47
- *
48
- * All subsequent LLM calls in this async context will be
49
- * automatically tagged with this configKey, sessionId, and customerId.
50
- *
51
- * @param configKey - Your config name (e.g., "linkedin-agent")
52
- * @param sessionId - Your session/conversation ID
53
- * @param customerId - Optional customer/user identifier for analytics
54
- *
55
- * @example
56
- * ```typescript
57
- * trace.setSession("linkedin-agent", sessionId, "user_123");
58
- * await agent.run(message); // Automatically traced with session + customer
59
- * ```
60
- */
61
- declare function setSession(configKey: string, sessionId: string, customerId?: string): void;
62
- /**
63
- * Run a function with session context.
64
- * Use this to ensure session context propagates across async boundaries.
65
- *
66
- * @param configKey - Your config name
67
- * @param sessionId - Your session ID
68
- * @param customerId - Optional customer/user identifier
69
- * @param fn - Function to run with session context
70
- *
71
- * @example
72
- * ```typescript
73
- * await trace.runWithSession("my-agent", sessionId, "user_123", async () => {
74
- * await agent.run(message); // Has session context
75
- * });
76
- * ```
77
- */
78
- declare function runWithSession<T>(configKey: string, sessionId: string, customerIdOrFn: string | (() => T), fn?: () => T): T;
79
- /**
80
- * Get current session context, if any.
81
- */
82
- declare function getSession(): SessionContext | undefined;
83
- /**
84
- * Clear session context.
85
- */
86
- declare function clearSession(): void;
87
- /**
88
- * Record custom business metrics. Latest value per field wins.
89
- *
90
- * Use this for metrics that OTEL can't capture automatically:
91
- * - Outlier scores
92
- * - Engagement metrics
93
- * - Conversion rates
94
- * - Any business-specific outcome
95
- *
96
- * @param data - Dict of metrics to record
97
- * @param options - Optional session identifiers
98
- * @param options.configKey - Config name (optional if setSession was called)
99
- * @param options.sessionId - Session ID (optional if setSession was called)
100
- *
101
- * @example
102
- * ```typescript
103
- * // If session context is set:
104
- * trace.span({ outlier_score: 0.8, engagement: 42 });
105
- *
106
- * // Or explicitly:
107
- * trace.span(
108
- * { outlier_score: 0.8 },
109
- * { configKey: "linkedin-agent", sessionId: "user123-convo456" }
110
- * );
111
- * ```
112
- */
113
- declare function span(data: Record<string, unknown>, options?: {
114
- configKey?: string;
115
- sessionId?: string;
116
- }): void;
117
110
  /**
118
111
  * Shutdown the tracing SDK gracefully.
119
112
  */
120
113
  declare function shutdown(): Promise<void>;
114
+
121
115
  /**
122
- * Wrap an OpenAI client to automatically trace all chat completions.
123
- * Works with OpenAI, OpenRouter, Azure OpenAI, LiteLLM, and any OpenAI-compatible API.
124
- *
125
- * @param client - The OpenAI client instance
126
- * @returns The same client with tracing enabled
127
- *
128
- * @example
129
- * ```typescript
130
- * import OpenAI from "openai";
131
- * import { trace } from "@fallom/trace";
132
- *
133
- * const openai = trace.wrapOpenAI(new OpenAI());
134
- *
135
- * trace.setSession("my-config", sessionId);
136
- * const response = await openai.chat.completions.create({...}); // Automatically traced!
137
- * ```
116
+ * FallomSession - Session-scoped tracing for concurrent-safe operations.
138
117
  */
139
- declare function wrapOpenAI<T extends {
140
- chat: {
141
- completions: {
142
- create: (...args: any[]) => Promise<any>;
143
- };
144
- };
145
- }>(client: T): T;
118
+
146
119
  /**
147
- * Wrap an Anthropic client to automatically trace all message creations.
120
+ * A session-scoped Fallom instance.
148
121
  *
149
- * @param client - The Anthropic client instance
150
- * @returns The same client with tracing enabled
122
+ * All wrappers created from this session automatically use the session context,
123
+ * making them safe for concurrent operations without global state issues.
151
124
  *
152
125
  * @example
153
126
  * ```typescript
154
- * import Anthropic from "@anthropic-ai/sdk";
155
- * import { trace } from "@fallom/trace";
127
+ * const session = fallom.session({
128
+ * configKey: "my-app",
129
+ * sessionId: "session-123",
130
+ * customerId: "user-456"
131
+ * });
156
132
  *
157
- * const anthropic = trace.wrapAnthropic(new Anthropic());
133
+ * // All calls use the session context
134
+ * const { generateText } = session.wrapAISDK(ai);
135
+ * await generateText({ model: openai("gpt-4o"), prompt: "..." });
158
136
  *
159
- * trace.setSession("my-config", sessionId);
160
- * const response = await anthropic.messages.create({...}); // Automatically traced!
137
+ * // Or wrap the model directly
138
+ * const model = session.traceModel(openai("gpt-4o"));
139
+ * await generateText({ model, prompt: "..." });
161
140
  * ```
162
141
  */
163
- declare function wrapAnthropic<T extends {
164
- messages: {
165
- create: (...args: any[]) => Promise<any>;
142
+ declare class FallomSession {
143
+ private ctx;
144
+ constructor(options: SessionOptions);
145
+ /** Get the session context. */
146
+ getContext(): SessionContext;
147
+ /**
148
+ * Get model assignment for this session (A/B testing).
149
+ */
150
+ getModel(configKeyOrOptions?: string | {
151
+ fallback?: string;
152
+ version?: number;
153
+ }, options?: {
154
+ fallback?: string;
155
+ version?: number;
156
+ }): Promise<string>;
157
+ /**
158
+ * Wrap a Vercel AI SDK model to trace all calls.
159
+ */
160
+ traceModel<T extends {
161
+ doGenerate?: (...args: any[]) => Promise<any>;
162
+ doStream?: (...args: any[]) => Promise<any>;
163
+ }>(model: T): T;
164
+ /** Wrap OpenAI client. Delegates to shared wrapper. */
165
+ wrapOpenAI<T extends {
166
+ chat: {
167
+ completions: {
168
+ create: (...args: any[]) => Promise<any>;
169
+ };
170
+ };
171
+ }>(client: T): T;
172
+ /** Wrap Anthropic client. Delegates to shared wrapper. */
173
+ wrapAnthropic<T extends {
174
+ messages: {
175
+ create: (...args: any[]) => Promise<any>;
176
+ };
177
+ }>(client: T): T;
178
+ /** Wrap Google AI model. Delegates to shared wrapper. */
179
+ wrapGoogleAI<T extends {
180
+ generateContent: (...args: any[]) => Promise<any>;
181
+ }>(model: T): T;
182
+ /** Wrap Vercel AI SDK. Delegates to shared wrapper. */
183
+ wrapAISDK<T extends {
184
+ generateText: (...args: any[]) => Promise<any>;
185
+ streamText: (...args: any[]) => any;
186
+ generateObject?: (...args: any[]) => Promise<any>;
187
+ streamObject?: (...args: any[]) => any;
188
+ }>(ai: T, options?: WrapAISDKOptions): {
189
+ generateText: T["generateText"];
190
+ streamText: T["streamText"];
191
+ generateObject: T["generateObject"];
192
+ streamObject: T["streamObject"];
166
193
  };
167
- }>(client: T): T;
194
+ /** Wrap Mastra agent. Delegates to shared wrapper. */
195
+ wrapMastraAgent<T extends {
196
+ generate: (...args: any[]) => Promise<any>;
197
+ name?: string;
198
+ }>(agent: T): T;
199
+ }
168
200
  /**
169
- * Wrap a Google Generative AI client to automatically trace all content generations.
170
- *
171
- * @param client - The GoogleGenerativeAI client instance
172
- * @returns The same client with tracing enabled
173
- *
174
- * @example
175
- * ```typescript
176
- * import { GoogleGenerativeAI } from "@google/generative-ai";
177
- * import { trace } from "@fallom/trace";
178
- *
179
- * const genAI = new GoogleGenerativeAI(apiKey);
180
- * const model = trace.wrapGoogleAI(genAI.getGenerativeModel({ model: "gemini-pro" }));
181
- *
182
- * trace.setSession("my-config", sessionId);
183
- * const response = await model.generateContent("Hello!"); // Automatically traced!
184
- * ```
201
+ * Create a session-scoped Fallom instance.
185
202
  */
186
- declare function wrapGoogleAI<T extends {
187
- generateContent: (...args: any[]) => Promise<any>;
188
- }>(model: T): T;
189
- /**
190
- * Wrap the Vercel AI SDK to automatically trace all LLM calls.
191
- * Works with generateText, streamText, generateObject, streamObject.
192
- *
193
- * @param ai - The ai module (import * as ai from "ai")
194
- * @returns Object with wrapped generateText, streamText, generateObject, streamObject
195
- *
196
- * @example
197
- * ```typescript
198
- * import * as ai from "ai";
199
- * import { createOpenAI } from "@ai-sdk/openai";
200
- * import { trace } from "@fallom/trace";
201
- *
202
- * await trace.init({ apiKey: process.env.FALLOM_API_KEY });
203
- * const { generateText, streamText } = trace.wrapAISDK(ai);
204
- *
205
- * const openrouter = createOpenAI({
206
- * apiKey: process.env.OPENROUTER_API_KEY,
207
- * baseURL: "https://openrouter.ai/api/v1",
208
- * });
209
- *
210
- * trace.setSession("my-config", sessionId);
211
- * const { text } = await generateText({
212
- * model: openrouter("openai/gpt-4o-mini"),
213
- * prompt: "Hello!",
214
- * }); // Automatically traced!
215
- * ```
216
- */
217
- declare function wrapAISDK<T extends {
218
- generateText: (...args: any[]) => Promise<any>;
219
- streamText: (...args: any[]) => any;
220
- generateObject?: (...args: any[]) => Promise<any>;
221
- streamObject?: (...args: any[]) => any;
222
- }>(ai: T): {
223
- generateText: T["generateText"];
224
- streamText: T["streamText"];
225
- generateObject: T["generateObject"];
226
- streamObject: T["streamObject"];
227
- };
203
+ declare function session(options: SessionOptions): FallomSession;
204
+
228
205
  /**
229
- * Wrap a Mastra agent to automatically trace all generate() calls.
230
- *
231
- * @param agent - The Mastra Agent instance
232
- * @returns The same agent with tracing enabled
233
- *
234
- * @example
235
- * ```typescript
236
- * import { trace } from "@fallom/trace";
237
- * import { Agent } from "@mastra/core";
238
- *
239
- * await trace.init({ apiKey: "your-key" });
206
+ * Fallom tracing module.
240
207
  *
241
- * const agent = new Agent({ ... });
242
- * const tracedAgent = trace.wrapMastraAgent(agent);
208
+ * Auto-instruments all LLM calls via OTEL and groups them by session.
209
+ * Also supports custom spans for business metrics.
243
210
  *
244
- * trace.setSession("my-app", "session-123", "user-456");
245
- * const result = await tracedAgent.generate([{ role: "user", content: "Hello" }]);
246
- * // ^ Automatically traced!
247
- * ```
211
+ * This file re-exports from the modular trace/ directory.
212
+ * Each wrapper is in its own file for better maintainability.
248
213
  */
249
- declare function wrapMastraAgent<T extends {
250
- generate: (...args: any[]) => Promise<any>;
251
- name?: string;
252
- }>(agent: T): T;
253
214
 
254
- declare const trace_clearSession: typeof clearSession;
255
- declare const trace_getSession: typeof getSession;
256
- declare const trace_runWithSession: typeof runWithSession;
257
- declare const trace_setSession: typeof setSession;
215
+ type trace_FallomSession = FallomSession;
216
+ declare const trace_FallomSession: typeof FallomSession;
217
+ type trace_SessionContext = SessionContext;
218
+ type trace_SessionOptions = SessionOptions;
219
+ type trace_TraceContext = TraceContext;
220
+ type trace_TraceData = TraceData;
221
+ type trace_WrapAISDKOptions = WrapAISDKOptions;
222
+ declare const trace_session: typeof session;
258
223
  declare const trace_shutdown: typeof shutdown;
259
- declare const trace_span: typeof span;
260
- declare const trace_wrapAISDK: typeof wrapAISDK;
261
- declare const trace_wrapAnthropic: typeof wrapAnthropic;
262
- declare const trace_wrapGoogleAI: typeof wrapGoogleAI;
263
- declare const trace_wrapMastraAgent: typeof wrapMastraAgent;
264
- declare const trace_wrapOpenAI: typeof wrapOpenAI;
265
224
  declare namespace trace {
266
- export { trace_clearSession as clearSession, trace_getSession as getSession, init$3 as init, trace_runWithSession as runWithSession, trace_setSession as setSession, trace_shutdown as shutdown, trace_span as span, trace_wrapAISDK as wrapAISDK, trace_wrapAnthropic as wrapAnthropic, trace_wrapGoogleAI as wrapGoogleAI, trace_wrapMastraAgent as wrapMastraAgent, trace_wrapOpenAI as wrapOpenAI };
225
+ export { trace_FallomSession as FallomSession, type trace_SessionContext as SessionContext, type trace_SessionOptions as SessionOptions, type trace_TraceContext as TraceContext, type trace_TraceData as TraceData, type trace_WrapAISDKOptions as WrapAISDKOptions, init$3 as init, trace_session as session, trace_shutdown as shutdown };
267
226
  }
268
227
 
269
228
  /**
@@ -296,9 +255,6 @@ declare function init$2(options?: {
296
255
  *
297
256
  * Same session_id always returns same model (sticky assignment).
298
257
  *
299
- * Also automatically sets trace context, so all subsequent LLM calls
300
- * are tagged with this session.
301
- *
302
258
  * @param configKey - Your config name (e.g., "linkedin-agent")
303
259
  * @param sessionId - Your session/conversation ID (must be consistent)
304
260
  * @param options - Optional settings
@@ -477,7 +433,7 @@ declare function init(options?: InitOptions): Promise<void>;
477
433
  * Fallom Exporter for Mastra
478
434
  *
479
435
  * Custom OpenTelemetry exporter that sends traces from Mastra agents to Fallom.
480
- * Reads session context from the shared trace module (set via trace.setSession()).
436
+ * Session context should be passed to the exporter constructor.
481
437
  *
482
438
  * Usage with Mastra:
483
439
  * ```typescript
@@ -487,7 +443,14 @@ declare function init(options?: InitOptions): Promise<void>;
487
443
  * // Initialize trace module
488
444
  * await trace.init({ apiKey: process.env.FALLOM_API_KEY });
489
445
  *
490
- * // Create Mastra with Fallom exporter
446
+ * // Create session for this request
447
+ * const session = trace.session({
448
+ * configKey: "my-app",
449
+ * sessionId: "session-123",
450
+ * customerId: "user-456"
451
+ * });
452
+ *
453
+ * // Create Mastra with Fallom exporter (pass session context)
491
454
  * const mastra = new Mastra({
492
455
  * agents: { myAgent },
493
456
  * telemetry: {
@@ -495,13 +458,13 @@ declare function init(options?: InitOptions): Promise<void>;
495
458
  * enabled: true,
496
459
  * export: {
497
460
  * type: "custom",
498
- * exporter: new FallomExporter(),
461
+ * exporter: new FallomExporter({
462
+ * session: session.getContext()
463
+ * }),
499
464
  * },
500
465
  * },
501
466
  * });
502
467
  *
503
- * // In your request handler:
504
- * trace.setSession("my-app", "session-123", "user-456");
505
468
  * const result = await mastra.getAgent("myAgent").generate("Hello!");
506
469
  * ```
507
470
  */
@@ -513,6 +476,8 @@ interface FallomExporterOptions {
513
476
  baseUrl?: string;
514
477
  /** Enable debug logging */
515
478
  debug?: boolean;
479
+ /** Session context for tracing */
480
+ session?: SessionContext;
516
481
  }
517
482
  /**
518
483
  * Set prompt tracking info.
@@ -531,13 +496,14 @@ declare function clearMastraPrompt(): void;
531
496
  /**
532
497
  * OpenTelemetry SpanExporter that sends traces to Fallom.
533
498
  *
534
- * Reads session context from trace.setSession() automatically.
499
+ * Pass session context via constructor options.
535
500
  * Compatible with Mastra's custom exporter interface.
536
501
  */
537
502
  declare class FallomExporter implements SpanExporter {
538
503
  private apiKey;
539
504
  private baseUrl;
540
505
  private debug;
506
+ private session?;
541
507
  private pendingExports;
542
508
  constructor(options?: FallomExporterOptions);
543
509
  private log;
@@ -584,35 +550,30 @@ declare class FallomExporter implements SpanExporter {
584
550
  *
585
551
  * @example
586
552
  * ```typescript
587
- * import fallom from 'fallom';
588
- *
589
- * // Initialize (call this early, before LLM imports if possible)
590
- * fallom.init({ apiKey: "your-api-key" });
591
- *
592
- * // Set session context for tracing
593
- * fallom.trace.setSession("my-agent", sessionId);
594
- *
595
- * // Get A/B tested model
596
- * const model = await fallom.models.get("my-config", sessionId, {
597
- * fallback: "gpt-4o-mini"
553
+ * import fallom from '@fallom/trace';
554
+ * import * as ai from 'ai';
555
+ * import { createOpenAI } from '@ai-sdk/openai';
556
+ *
557
+ * // Initialize once
558
+ * await fallom.init({ apiKey: "your-api-key" });
559
+ *
560
+ * // Create a session for this conversation/request
561
+ * const session = fallom.session({
562
+ * configKey: "my-agent",
563
+ * sessionId: "session-123",
564
+ * customerId: "user-456",
598
565
  * });
599
566
  *
600
- * // Get managed prompts (with optional A/B testing)
601
- * const prompt = await fallom.prompts.get("onboarding", {
602
- * variables: { userName: "John" }
603
- * });
567
+ * // Option 1: Wrap the AI SDK (our style)
568
+ * const { generateText } = session.wrapAISDK(ai);
569
+ * await generateText({ model: openai("gpt-4o"), prompt: "Hello!" });
604
570
  *
605
- * // Use with OpenAI
606
- * const response = await openai.chat.completions.create({
607
- * model,
608
- * messages: [
609
- * { role: "system", content: prompt.system },
610
- * { role: "user", content: prompt.user }
611
- * ]
612
- * });
571
+ * // Option 2: Wrap the model directly (PostHog style)
572
+ * const model = session.traceModel(openai("gpt-4o"));
573
+ * await ai.generateText({ model, prompt: "Hello!" });
613
574
  *
614
- * // Record custom metrics
615
- * fallom.trace.span({ user_satisfaction: 5 });
575
+ * // Get A/B tested model within session
576
+ * const modelName = await session.getModel({ fallback: "gpt-4o-mini" });
616
577
  * ```
617
578
  */
618
579
 
@@ -621,6 +582,7 @@ declare const _default: {
621
582
  trace: typeof trace;
622
583
  models: typeof models;
623
584
  prompts: typeof prompts;
585
+ session: typeof session;
624
586
  };
625
587
 
626
- export { FallomExporter, type FallomExporterOptions, type InitOptions, type PromptResult, clearMastraPrompt, _default as default, init, models, prompts, setMastraPrompt, setMastraPromptAB, trace };
588
+ export { FallomExporter, type FallomExporterOptions, FallomSession, type InitOptions, type PromptResult, type SessionContext, type SessionOptions, clearMastraPrompt, _default as default, init, models, prompts, session, setMastraPrompt, setMastraPromptAB, trace };