@observyze/sdk 0.1.3 → 0.1.5

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.
@@ -0,0 +1,646 @@
1
+ /**
2
+ * SDK Configuration and Types
3
+ */
4
+ /**
5
+ * Type of span operation
6
+ */
7
+ declare enum SpanType {
8
+ LLM = "llm",
9
+ TOOL = "tool",
10
+ AGENT = "agent",
11
+ CHAIN = "chain",
12
+ RETRIEVAL = "retrieval",
13
+ EMBEDDING = "embedding",
14
+ CUSTOM = "custom"
15
+ }
16
+ /**
17
+ * Status of trace execution
18
+ */
19
+ declare enum TraceStatus {
20
+ SUCCESS = "success",
21
+ ERROR = "error",
22
+ TIMEOUT = "timeout",
23
+ RUNNING = "running"
24
+ }
25
+ /**
26
+ * Error information for a span
27
+ */
28
+ interface SpanError {
29
+ message: string;
30
+ stack?: string;
31
+ code?: string;
32
+ }
33
+ /**
34
+ * Token usage information
35
+ */
36
+ interface TokenUsage {
37
+ input: number;
38
+ output: number;
39
+ total: number;
40
+ computed_pixels?: number;
41
+ audio_seconds?: number;
42
+ }
43
+ /**
44
+ * Individual operation within a trace
45
+ */
46
+ interface Span$1 {
47
+ span_id: string;
48
+ parent_span_id?: string;
49
+ name: string;
50
+ type: SpanType;
51
+ start_time: Date;
52
+ end_time: Date;
53
+ duration_ms: number;
54
+ input: any;
55
+ output: any;
56
+ error?: SpanError;
57
+ metadata: {
58
+ model?: string;
59
+ provider?: string;
60
+ temperature?: number;
61
+ max_tokens?: number;
62
+ [key: string]: any;
63
+ };
64
+ tokens?: TokenUsage;
65
+ }
66
+ /**
67
+ * Complete record of an AI workflow execution
68
+ */
69
+ interface Trace$1 {
70
+ trace_id: string;
71
+ organization_id: string;
72
+ project_id?: string;
73
+ name: string;
74
+ provider?: string;
75
+ model?: string;
76
+ status: TraceStatus;
77
+ start_time: Date;
78
+ end_time: Date;
79
+ duration_ms: number;
80
+ metadata: Record<string, any>;
81
+ spans: Span$1[];
82
+ tags: string[];
83
+ user_id?: string;
84
+ session_id?: string;
85
+ cost?: number;
86
+ cost_breakdown?: {
87
+ tokens: number;
88
+ pixels: number;
89
+ audio: number;
90
+ };
91
+ created_at: Date;
92
+ updated_at: Date;
93
+ }
94
+ /**
95
+ * Configuration for Observyze SDK client
96
+ */
97
+ interface ClientConfig {
98
+ /**
99
+ * API key for authentication with Observyze
100
+ */
101
+ apiKey: string;
102
+ /**
103
+ * Endpoint URL for the Observyze API
104
+ * @default 'https://api.observyze.com'
105
+ */
106
+ endpoint?: string;
107
+ /**
108
+ * Maximum number of traces to buffer before flushing
109
+ * @default 100
110
+ */
111
+ batchSize?: number;
112
+ /**
113
+ * Time in milliseconds to wait before auto-flushing buffered traces
114
+ * @default 5000
115
+ */
116
+ flushInterval?: number;
117
+ /**
118
+ * Timeout for each ingestion HTTP attempt in milliseconds.
119
+ * @default 10000
120
+ */
121
+ requestTimeoutMs?: number;
122
+ /**
123
+ * Organization ID (optional, can be extracted from API key)
124
+ */
125
+ organizationId?: string;
126
+ /**
127
+ * Project ID for trace attribution
128
+ */
129
+ projectId?: string;
130
+ /**
131
+ * Enable debug logging
132
+ * @default false
133
+ */
134
+ debug?: boolean;
135
+ /**
136
+ * Dry run mode — don't send traces to server (useful for testing)
137
+ * @default false
138
+ */
139
+ dryRun?: boolean;
140
+ /**
141
+ * Automatically scrub PII from trace output before network transmission
142
+ * @default true
143
+ */
144
+ enablePiiRedaction?: boolean;
145
+ /**
146
+ * Capture prompt/completion content and arbitrary user metadata in traces.
147
+ * Set to false for metadata-only telemetry: names, inputs, outputs, errors,
148
+ * user/session identifiers, and tags are omitted locally before a trace
149
+ * can enter the dispatch buffer.
150
+ * @default true
151
+ */
152
+ captureContent?: boolean;
153
+ /**
154
+ * Hallucination threshold for checkGuardrails() calls.
155
+ * @default 0.8
156
+ */
157
+ hallucinationThreshold?: number;
158
+ /**
159
+ * Safety threshold for checkGuardrails() calls.
160
+ * @default 0.9
161
+ */
162
+ safetyThreshold?: number;
163
+ /**
164
+ * Minimum confidence threshold for guardrail blocking (0-1).
165
+ * When confidence is below this value, high scores trigger an alert-only
166
+ * response instead of halting execution.
167
+ * @default 0.4
168
+ */
169
+ confidenceThreshold?: number;
170
+ /**
171
+ * Evaluation service URL for guardrail checks.
172
+ * Defaults to the same value as `endpoint`.
173
+ */
174
+ evalEndpoint?: string;
175
+ /**
176
+ * Enable explicit guardrail methods (checkGuardrails, executeWithCircuitBreaker).
177
+ * @default true
178
+ */
179
+ enableCircuitBreaker?: boolean;
180
+ /**
181
+ * Fail closed when the evaluation service is unreachable.
182
+ * true = block execution on errors (safe default).
183
+ * false = allow execution on errors.
184
+ * @default true
185
+ */
186
+ failClosed?: boolean;
187
+ /**
188
+ * Automatically redirect OpenAI/Anthropic base URLs through the Observyze
189
+ * proxy gateway to enforce Redis-backed circuit breakers.
190
+ * @default true
191
+ */
192
+ enableProxyRedirect?: boolean;
193
+ }
194
+ /**
195
+ * Internal configuration with all defaults applied
196
+ */
197
+ type ResolvedClientConfig = Required<ClientConfig>;
198
+ /**
199
+ * Result of a connection test.
200
+ * Verifies the SDK can reach Observyze and send a real trace end-to-end.
201
+ */
202
+ interface TestConnectionResult {
203
+ /** Whether the test trace was accepted by Observyze */
204
+ ok: boolean;
205
+ /** The trace ID of the sent test trace (present when ok === true) */
206
+ traceId?: string;
207
+ /** HTTP status returned by the API (present when the request failed) */
208
+ status?: number;
209
+ /** Human-readable message with the outcome or the exact failure reason */
210
+ message: string;
211
+ }
212
+ /**
213
+ * Result of a guardrail evaluation check.
214
+ * score is null when evaluation could not be performed.
215
+ */
216
+ interface GuardrailResult {
217
+ pass: boolean;
218
+ /** The hallucination score (0-1), or null if evaluation failed */
219
+ score: number | null;
220
+ /** Confidence in the evaluation score (0-1), null if unavailable */
221
+ confidence?: number | null;
222
+ /** The safety score (0-1), or null if evaluation failed */
223
+ safetyScore?: number | null;
224
+ /** Human-readable reason for the result */
225
+ reason?: string;
226
+ /** How the evaluation was performed */
227
+ evaluationSource?: 'live' | 'consensus' | 'nli_fast_path' | 'error' | 'disabled' | 'fallback';
228
+ /** Why a live evaluation didn't happen */
229
+ fallbackReason?: string;
230
+ }
231
+
232
+ /**
233
+ * Trace and Span classes for capturing AI workflow execution
234
+ */
235
+
236
+ /**
237
+ * Represents an individual operation within a trace
238
+ */
239
+ declare class Span {
240
+ private data;
241
+ private startTime;
242
+ private readonly captureContent;
243
+ constructor(name: string, type: SpanType, parentSpanId?: string, captureContent?: boolean);
244
+ /**
245
+ * Set the input data for this span
246
+ */
247
+ setInput(input: any): this;
248
+ /**
249
+ * Set the output data for this span
250
+ */
251
+ setOutput(output: any): this;
252
+ /**
253
+ * Record an error that occurred during span execution
254
+ */
255
+ setError(error: Error): this;
256
+ /**
257
+ * Set metadata for this span
258
+ */
259
+ setMetadata(key: string, value: any): this;
260
+ /**
261
+ * Set multiple metadata fields at once
262
+ */
263
+ setMetadataAll(metadata: Record<string, any>): this;
264
+ /**
265
+ * Set token usage information
266
+ */
267
+ setTokens(tokens: TokenUsage): this;
268
+ /**
269
+ * End the span and calculate duration
270
+ */
271
+ end(): void;
272
+ /**
273
+ * Get the span ID
274
+ */
275
+ get id(): string;
276
+ /**
277
+ * Get the span data for serialization
278
+ */
279
+ toJSON(): Span$1;
280
+ }
281
+ /**
282
+ * Represents a complete AI workflow execution
283
+ */
284
+ declare class Trace {
285
+ private data;
286
+ private startTime;
287
+ private spans;
288
+ private ended;
289
+ private readonly captureContent;
290
+ constructor(name: string, organizationId: string, projectId?: string, captureContent?: boolean);
291
+ /**
292
+ * Start a new span within this trace
293
+ */
294
+ startSpan(name: string, type: SpanType, parentSpanId?: string): Span;
295
+ /**
296
+ * Add metadata to the trace
297
+ */
298
+ setMetadata(key: string, value: any): this;
299
+ /**
300
+ * Set multiple metadata fields at once
301
+ */
302
+ setMetadataAll(metadata: Record<string, any>): this;
303
+ /**
304
+ * Add tags to the trace
305
+ */
306
+ addTag(tag: string): this;
307
+ /**
308
+ * Add multiple tags at once
309
+ */
310
+ addTags(tags: string[]): this;
311
+ /**
312
+ * Set the user ID associated with this trace
313
+ */
314
+ setUserId(userId: string): this;
315
+ /**
316
+ * Set the session ID associated with this trace
317
+ */
318
+ setSessionId(sessionId: string): this;
319
+ /**
320
+ * End the trace with a final status
321
+ */
322
+ end(status?: TraceStatus): void;
323
+ /**
324
+ * Get the trace ID
325
+ */
326
+ get id(): string;
327
+ /**
328
+ * Check if the trace has ended
329
+ */
330
+ get isEnded(): boolean;
331
+ /**
332
+ * Get the trace data for serialization
333
+ */
334
+ toJSON(): Omit<Trace$1, '_id' | 'created_at' | 'updated_at'>;
335
+ }
336
+
337
+ interface LangChainRunnableLike {
338
+ invoke: (input: unknown, config?: unknown) => Promise<unknown>;
339
+ stream?: (input: unknown, config?: unknown) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>;
340
+ [key: string]: unknown;
341
+ }
342
+ /**
343
+ * Instruments a LangChain-compatible Runnable boundary without taking a hard
344
+ * dependency on LangChain. It captures invoke() and stream(); provider-specific
345
+ * inner spans still require wrapping the underlying provider client.
346
+ */
347
+ declare function wrapLangChain<T extends LangChainRunnableLike>(runnable: T, nwClient: ObservyzeClient): T;
348
+
349
+ type Callable$2 = (...args: any[]) => any;
350
+ type GeminiMethodSurface = ({
351
+ generateContent: Callable$2;
352
+ generateContentStream?: Callable$2;
353
+ } | {
354
+ generateContent?: Callable$2;
355
+ generateContentStream: Callable$2;
356
+ }) & Record<string, unknown>;
357
+ type GeminiClientLike = GeminiMethodSurface | ({
358
+ models: GeminiMethodSurface;
359
+ } & Record<string, unknown>);
360
+ /** Supports both @google/generative-ai model objects and @google/genai clients. */
361
+ declare function wrapGemini<T extends GeminiClientLike>(client: T, nwClient: ObservyzeClient): T;
362
+
363
+ type Callable$1 = (...args: any[]) => any;
364
+ type VercelAILike = ({
365
+ generateText: Callable$1;
366
+ streamText?: Callable$1;
367
+ } | {
368
+ generateText?: Callable$1;
369
+ streamText: Callable$1;
370
+ }) & Record<string, unknown>;
371
+ /**
372
+ * Wraps the Vercel AI SDK function surface. A new object is returned because
373
+ * ESM namespace imports are immutable and cannot be monkey-patched safely.
374
+ */
375
+ declare function wrapVercelAI<T extends VercelAILike>(sdk: T, nwClient: ObservyzeClient): T;
376
+
377
+ type Callable = (...args: any[]) => any;
378
+ type LlamaIndexEngineLike = ({
379
+ query: Callable;
380
+ chat?: Callable;
381
+ } | {
382
+ query?: Callable;
383
+ chat: Callable;
384
+ }) & Record<string, unknown>;
385
+ /** Instruments LlamaIndex QueryEngine and ChatEngine method boundaries. */
386
+ declare function wrapLlamaIndex<T extends LlamaIndexEngineLike>(engine: T, nwClient: ObservyzeClient): T;
387
+
388
+ /**
389
+ * Auto-Instrumentation API
390
+ * Provides nw.wrap() API for instrumenting LLM clients
391
+ */
392
+
393
+ type ClientMethod = (...args: any[]) => any;
394
+ /**
395
+ * Supported client types for auto-instrumentation
396
+ */
397
+ type SupportedClient = {
398
+ chat: {
399
+ completions: {
400
+ create: ClientMethod;
401
+ };
402
+ };
403
+ } | {
404
+ messages: {
405
+ create: ClientMethod;
406
+ };
407
+ } | GeminiClientLike | VercelAILike | LlamaIndexEngineLike | LangChainRunnableLike;
408
+ /**
409
+ * Detect the type of LLM client and apply appropriate instrumentation
410
+ */
411
+ declare function wrap<T extends SupportedClient>(client: T, nwClient: ObservyzeClient): T;
412
+
413
+ interface ExecutionBudgetOptions {
414
+ /** Maximum provider/tool operations allowed in this run. */
415
+ maxCalls: number;
416
+ /** Maximum cumulative tokens. Omit when token usage is not available. */
417
+ maxTokens?: number;
418
+ /** Wall-clock deadline for the complete run in milliseconds. */
419
+ timeoutMs: number;
420
+ }
421
+ interface ExecutionBudgetStats {
422
+ callsUsed: number;
423
+ tokensUsed: number;
424
+ elapsedMs: number;
425
+ remainingCalls: number;
426
+ remainingTokens: number | null;
427
+ remainingMs: number;
428
+ aborted: boolean;
429
+ }
430
+ declare class ExecutionBudgetExceededError extends Error {
431
+ readonly dimension: 'calls' | 'tokens' | 'time';
432
+ readonly code = "OBSERVYZE_EXECUTION_BUDGET_EXCEEDED";
433
+ constructor(dimension: 'calls' | 'tokens' | 'time', message: string);
434
+ }
435
+ /**
436
+ * Active, process-local agent execution budget. Use one instance for one
437
+ * agent/session run and execute every provider/tool action through run().
438
+ * The callback receives an AbortSignal and must pass it to the underlying SDK
439
+ * for transport-level cancellation; run() still rejects at the deadline if a
440
+ * third-party SDK ignores the signal.
441
+ *
442
+ * @example
443
+ * ```typescript
444
+ * const budget = new ExecutionBudget({ maxCalls: 20, maxTokens: 50_000, timeoutMs: 30_000 })
445
+ *
446
+ * const result = await budget.run(async (signal) => {
447
+ * return openai.chat.completions.create({ ... }, { signal })
448
+ * })
449
+ * ```
450
+ */
451
+ declare class ExecutionBudget {
452
+ private readonly options;
453
+ private readonly startedAt;
454
+ private readonly controller;
455
+ private callsUsed;
456
+ private tokensUsed;
457
+ constructor(options: ExecutionBudgetOptions);
458
+ get signal(): AbortSignal;
459
+ get stats(): ExecutionBudgetStats;
460
+ abort(reason?: string): void;
461
+ consumeTokens(tokens: number): void;
462
+ run<T>(operation: (signal: AbortSignal) => Promise<T>, reservedTokens?: number): Promise<T>;
463
+ private assertTime;
464
+ }
465
+
466
+ /**
467
+ * Observyze SDK Client
468
+ * Main entry point for instrumenting AI applications
469
+ */
470
+
471
+ /**
472
+ * Main SDK client for Observyze
473
+ */
474
+ declare class ObservyzeClient {
475
+ private config;
476
+ private traceBuffer;
477
+ private flushTimer;
478
+ private isShuttingDown;
479
+ private readonly MAX_QUEUE_SIZE;
480
+ private readonly RETRY_DELAYS;
481
+ private static readBoundedResponse;
482
+ private static retryAfterMs;
483
+ private static parseApiError;
484
+ private static formatApiError;
485
+ constructor(config: ClientConfig);
486
+ /**
487
+ * Start a new trace
488
+ */
489
+ startTrace(name: string, metadata?: Record<string, any>): Trace;
490
+ /**
491
+ * Buffer a completed trace for batch sending
492
+ */
493
+ private bufferTrace;
494
+ /**
495
+ * Start the auto-flush timer
496
+ */
497
+ private startFlushTimer;
498
+ /**
499
+ * Flush all buffered traces to Observyze
500
+ */
501
+ flush(): Promise<void>;
502
+ /**
503
+ * Send traces with exponential backoff retry
504
+ */
505
+ private sendWithRetry;
506
+ /**
507
+ * Shutdown the SDK and flush remaining traces
508
+ */
509
+ shutdown(): Promise<void>;
510
+ /**
511
+ * Get current buffer size
512
+ */
513
+ get bufferSize(): number;
514
+ /**
515
+ * Get SDK configuration
516
+ */
517
+ getConfig(): Readonly<ResolvedClientConfig>;
518
+ /**
519
+ * Wrap a supported LLM client or framework boundary to enable auto-instrumentation.
520
+ *
521
+ * Supports: OpenAI, Anthropic, Google Gemini, Vercel AI SDK, LangChain, LlamaIndex.
522
+ *
523
+ * @example
524
+ * ```typescript
525
+ * import OpenAI from 'openai'
526
+ * import { ObservyzeClient } from '@observyze/sdk'
527
+ *
528
+ * const nw = new ObservyzeClient({ apiKey: 'your-api-key' })
529
+ * const openai = nw.wrap(new OpenAI({ apiKey: 'openai-key' }))
530
+ *
531
+ * // All calls are now automatically traced
532
+ * const response = await openai.chat.completions.create({
533
+ * model: 'gpt-4o',
534
+ * messages: [{ role: 'user', content: 'Hello!' }]
535
+ * })
536
+ * ```
537
+ */
538
+ wrap<T extends SupportedClient>(client: T): T;
539
+ /** Create an active call/token/time budget for one agent execution. */
540
+ createExecutionBudget(options: ExecutionBudgetOptions): ExecutionBudget;
541
+ /**
542
+ * Verify that the SDK can reach Observyze and send traces end-to-end.
543
+ *
544
+ * @example
545
+ * ```typescript
546
+ * const nw = new ObservyzeClient({ apiKey: process.env.OBSERVYZE_API_KEY })
547
+ * const result = await nw.testConnection()
548
+ * // { ok: true, traceId: '...', message: 'Connection successful...' }
549
+ * ```
550
+ */
551
+ testConnection(): Promise<TestConnectionResult>;
552
+ /**
553
+ * Sync a local agent .history file to Observyze cloud.
554
+ * Parses JSON/NDJSON agent history and sends to the ingestion endpoint.
555
+ */
556
+ syncLocalHistory(filePath: string): Promise<void>;
557
+ /**
558
+ * Industry-grade PII redaction.
559
+ * Recursively scrubs PII from trace data before transmission.
560
+ */
561
+ private sanitizePII;
562
+ /**
563
+ * Explicitly evaluate content before an application action.
564
+ * Requires an active Observyze subscription with guardrails enabled.
565
+ *
566
+ * Returns GuardrailResult with score: null when evaluation couldn't be performed.
567
+ * In failClosed mode (default), null scores block execution.
568
+ *
569
+ * @example
570
+ * ```typescript
571
+ * const result = await nw.checkGuardrails(llmOutput)
572
+ * if (!result.pass) {
573
+ * throw new Error('Guardrail blocked: ' + result.reason)
574
+ * }
575
+ * ```
576
+ */
577
+ checkGuardrails(content: string | any): Promise<GuardrailResult>;
578
+ /**
579
+ * Execute an application action only after an explicit pre-execution
580
+ * guardrail check passes.
581
+ * @throws Error when execution is blocked by circuit breaker
582
+ */
583
+ executeWithCircuitBreaker<T>(agentExecution: () => Promise<T>, traceContext?: any): Promise<T>;
584
+ }
585
+
586
+ /**
587
+ * Observyze OpenTelemetry Span Exporter
588
+ *
589
+ * Implements `@opentelemetry/sdk-trace-base`'s `SpanExporter` interface
590
+ * so users can use the standard OTel Node.js SDK and export spans to
591
+ * the Observyze platform.
592
+ *
593
+ * Usage:
594
+ * ```typescript
595
+ * import { ObservyzeClient } from '@observyze/sdk'
596
+ * import { ObservyzeSpanExporter } from '@observyze/sdk/opentelemetry'
597
+ * import { NodeSDK } from '@opentelemetry/sdk-node'
598
+ * import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
599
+ *
600
+ * const nw = new ObservyzeClient({ apiKey: 'nw_...' })
601
+ *
602
+ * const sdk = new NodeSDK({
603
+ * spanProcessor: new BatchSpanProcessor(
604
+ * new ObservyzeSpanExporter(nw, { serviceName: 'my-app' })
605
+ * )
606
+ * })
607
+ * sdk.start()
608
+ * ```
609
+ */
610
+
611
+ /**
612
+ * Exporter configuration
613
+ */
614
+ interface ObservyzeExporterConfig {
615
+ serviceName?: string;
616
+ projectId?: string;
617
+ defaultSpanType?: SpanType | 'llm' | 'tool' | 'agent' | 'chain' | 'retrieval';
618
+ headers?: Record<string, string>;
619
+ }
620
+ /**
621
+ * Observyze OpenTelemetry Span Exporter
622
+ */
623
+ declare class ObservyzeSpanExporter {
624
+ private client;
625
+ private config;
626
+ constructor(client: ObservyzeClient, config?: ObservyzeExporterConfig);
627
+ /**
628
+ * Export spans — called by OTel SDK when spans are ready.
629
+ * Converts OTel spans to Observyze traces and buffers them.
630
+ */
631
+ export(spans: any[], resultCallback: (result: {
632
+ code: number;
633
+ error?: Error;
634
+ }) => void): Promise<void>;
635
+ /**
636
+ * Called when the exporter is shut down.
637
+ * Flushes any remaining buffered traces via the SDK client.
638
+ */
639
+ shutdown(): Promise<void>;
640
+ /**
641
+ * Called by the OTel SDK to force-export buffered spans.
642
+ */
643
+ forceFlush(): Promise<void>;
644
+ }
645
+
646
+ export { type ClientConfig as C, ExecutionBudget as E, type GeminiClientLike as G, type LangChainRunnableLike as L, ObservyzeClient as O, type ResolvedClientConfig as R, Span as S, type TestConnectionResult as T, type VercelAILike as V, ExecutionBudgetExceededError as a, type ExecutionBudgetOptions as b, type ExecutionBudgetStats as c, type GuardrailResult as d, type LlamaIndexEngineLike as e, type ObservyzeExporterConfig as f, ObservyzeSpanExporter as g, type Span$1 as h, type SpanError as i, SpanType as j, type SupportedClient as k, type TokenUsage as l, Trace as m, type Trace$1 as n, TraceStatus as o, wrapGemini as p, wrapLangChain as q, wrapLlamaIndex as r, wrapVercelAI as s, wrap as w };