@observyze/sdk 0.1.0 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Observyze
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # @observyze/sdk
1
+ # Observyze Node.js SDK
2
2
 
3
- Node.js SDK for Observyze AI Observability Platform.
3
+ The official Node.js SDK for Observyze.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,191 +8,26 @@ Node.js SDK for Observyze AI Observability Platform.
8
8
  npm install @observyze/sdk
9
9
  ```
10
10
 
11
- ## Quick Start
12
-
13
- ### Auto-Instrumentation (Recommended)
14
-
15
- The easiest way to get started is with auto-instrumentation:
11
+ ## Basic Usage
16
12
 
17
13
  ```typescript
18
- import OpenAI from 'openai'
19
14
  import { ObservyzeClient } from '@observyze/sdk'
15
+ import OpenAI from 'openai'
20
16
 
21
- // Initialize Observyze
22
17
  const nw = new ObservyzeClient({
23
- apiKey: process.env.Observyze_API_KEY!,
24
- organizationId: 'your-org-id',
25
- projectId: 'your-project-id'
18
+ apiKey: process.env.OBSERVYZE_API_KEY,
19
+ projectId: 'my-project'
26
20
  })
27
21
 
28
- // Initialize your LLM client
29
- const openai = new OpenAI({
30
- apiKey: process.env.OPENAI_API_KEY!
31
- })
22
+ const openai = nw.wrap(new OpenAI())
32
23
 
33
- // Wrap the client - that's it!
34
- nw.wrap(openai)
35
-
36
- // All calls are now automatically traced
24
+ // All calls are now automatically traced!
37
25
  const response = await openai.chat.completions.create({
38
26
  model: 'gpt-4',
39
27
  messages: [{ role: 'user', content: 'Hello!' }]
40
28
  })
41
29
  ```
42
30
 
43
- **Supported Providers:**
44
- - OpenAI (`chat.completions.create`)
45
- - Anthropic (`messages.create`)
46
- - Streaming responses fully supported
47
-
48
- See [Auto-Instrumentation Guide](./src/instrumentation/README.md) for more details.
49
-
50
- ### Manual Instrumentation
51
-
52
- For more control, you can manually create traces and spans:
53
-
54
- ```typescript
55
- import { ObservyzeClient, SpanType, TraceStatus } from '@observyze/sdk'
56
-
57
- // Initialize the client
58
- const nw = new ObservyzeClient({
59
- apiKey: 'your-api-key',
60
- endpoint: 'https://api.observyze.com',
61
- projectId: 'your-project-id'
62
- })
63
-
64
- // Create a trace
65
- const trace = nw.startTrace('my-ai-workflow')
66
-
67
- // Add a span for an LLM call
68
- const span = trace.startSpan('openai-completion', SpanType.LLM)
69
- span.setInput({ prompt: 'Hello, world!' })
70
- span.setMetadata('model', 'gpt-4')
71
- span.setMetadata('temperature', 0.7)
72
-
73
- // ... perform your LLM call ...
74
-
75
- span.setOutput({ completion: 'Hello! How can I help you?' })
76
- span.setTokens({ input: 10, output: 15, total: 25 })
77
- span.end()
78
-
79
- // End the trace
80
- trace.end(TraceStatus.SUCCESS)
81
-
82
- // Flush traces (or wait for auto-flush)
83
- await nw.flush()
84
-
85
- // Shutdown when done
86
- await nw.shutdown()
87
- ```
88
-
89
- ## Configuration
90
-
91
- ```typescript
92
- interface ClientConfig {
93
- apiKey: string // Required: Your Observyze API key
94
- endpoint?: string // Optional: Ingestion endpoint (default: http://localhost:3001)
95
- batchSize?: number // Optional: Max traces per batch (default: 100)
96
- flushInterval?: number // Optional: Auto-flush interval in ms (default: 5000)
97
- organizationId?: string // Optional: Organization ID
98
- projectId?: string // Optional: Project ID
99
- debug?: boolean // Optional: Enable debug logging (default: false)
100
- dryRun?: boolean // Optional: Don't send traces (default: false)
101
- }
102
- ```
103
-
104
- ## API Reference
105
-
106
- ### ObservyzeClient
107
-
108
- #### `startTrace(name: string, metadata?: Record<string, any>): Trace`
109
-
110
- Start a new trace for an AI workflow.
111
-
112
- #### `flush(): Promise<void>`
113
-
114
- Manually flush buffered traces to the Ingestion Service.
115
-
116
- #### `shutdown(): Promise<void>`
117
-
118
- Shutdown the SDK and flush remaining traces.
119
-
120
- #### `wrap<T>(client: T): T`
121
-
122
- Wrap an LLM client (OpenAI, Anthropic) to enable auto-instrumentation. Returns the wrapped client.
123
-
124
- ```typescript
125
- const openai = new OpenAI({ apiKey: 'key' })
126
- nw.wrap(openai)
127
- ```
128
-
129
- ### Trace
130
-
131
- #### `startSpan(name: string, type: SpanType, parentSpanId?: string): Span`
132
-
133
- Start a new span within the trace.
134
-
135
- #### `setMetadata(key: string, value: any): this`
136
-
137
- Add metadata to the trace.
138
-
139
- #### `addTag(tag: string): this`
140
-
141
- Add a tag to the trace.
142
-
143
- #### `setUserId(userId: string): this`
144
-
145
- Set the user ID associated with this trace.
146
-
147
- #### `setSessionId(sessionId: string): this`
148
-
149
- Set the session ID associated with this trace.
150
-
151
- #### `end(status?: TraceStatus): void`
152
-
153
- End the trace with a final status.
154
-
155
- ### Span
156
-
157
- #### `setInput(input: any): this`
158
-
159
- Set the input data for the span.
160
-
161
- #### `setOutput(output: any): this`
162
-
163
- Set the output data for the span.
164
-
165
- #### `setError(error: Error): this`
166
-
167
- Record an error that occurred during span execution.
168
-
169
- #### `setMetadata(key: string, value: any): this`
170
-
171
- Add metadata to the span.
172
-
173
- #### `setTokens(tokens: TokenUsage): this`
174
-
175
- Set token usage information.
176
-
177
- #### `end(): void`
178
-
179
- End the span and calculate duration.
180
-
181
- ## Span Types
182
-
183
- - `SpanType.LLM` - LLM API calls (OpenAI, Anthropic, etc.)
184
- - `SpanType.TOOL` - Tool invocations
185
- - `SpanType.AGENT` - Agent executions
186
- - `SpanType.CHAIN` - Chain operations
187
- - `SpanType.RETRIEVAL` - Retrieval operations (RAG)
188
-
189
- ## Trace Status
190
-
191
- - `TraceStatus.SUCCESS` - Workflow completed successfully
192
- - `TraceStatus.ERROR` - Workflow failed with an error
193
- - `TraceStatus.TIMEOUT` - Workflow timed out
194
- - `TraceStatus.RUNNING` - Workflow is still running
195
-
196
- ## License
31
+ ## Documentation
197
32
 
198
- MIT
33
+ Full documentation is available at [observyze.com/docs](https://observyze.com/docs).
@@ -0,0 +1,372 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+
28
+ // src/types.ts
29
+ var SpanType, TraceStatus;
30
+ var init_types = __esm({
31
+ "src/types.ts"() {
32
+ "use strict";
33
+ SpanType = /* @__PURE__ */ ((SpanType3) => {
34
+ SpanType3["LLM"] = "llm";
35
+ SpanType3["TOOL"] = "tool";
36
+ SpanType3["AGENT"] = "agent";
37
+ SpanType3["CHAIN"] = "chain";
38
+ SpanType3["RETRIEVAL"] = "retrieval";
39
+ return SpanType3;
40
+ })(SpanType || {});
41
+ TraceStatus = /* @__PURE__ */ ((TraceStatus2) => {
42
+ TraceStatus2["SUCCESS"] = "success";
43
+ TraceStatus2["ERROR"] = "error";
44
+ TraceStatus2["TIMEOUT"] = "timeout";
45
+ TraceStatus2["RUNNING"] = "running";
46
+ return TraceStatus2;
47
+ })(TraceStatus || {});
48
+ }
49
+ });
50
+
51
+ // src/opentelemetry/exporter.ts
52
+ init_types();
53
+
54
+ // src/trace.ts
55
+ init_types();
56
+ import { randomUUID } from "crypto";
57
+ function generateId() {
58
+ return `${Date.now()}-${randomUUID().substring(0, 8)}`;
59
+ }
60
+ var Span = class {
61
+ data;
62
+ startTime;
63
+ constructor(name, type, parentSpanId) {
64
+ this.startTime = Date.now();
65
+ this.data = {
66
+ span_id: generateId(),
67
+ parent_span_id: parentSpanId,
68
+ name,
69
+ type,
70
+ start_time: new Date(this.startTime),
71
+ end_time: new Date(this.startTime),
72
+ // Will be updated on end()
73
+ duration_ms: 0,
74
+ input: null,
75
+ output: null,
76
+ metadata: {}
77
+ };
78
+ }
79
+ /**
80
+ * Set the input data for this span
81
+ */
82
+ setInput(input) {
83
+ this.data.input = input;
84
+ return this;
85
+ }
86
+ /**
87
+ * Set the output data for this span
88
+ */
89
+ setOutput(output) {
90
+ this.data.output = output;
91
+ return this;
92
+ }
93
+ /**
94
+ * Record an error that occurred during span execution
95
+ */
96
+ setError(error) {
97
+ this.data.error = {
98
+ message: error.message,
99
+ stack: error.stack,
100
+ code: error.code
101
+ };
102
+ return this;
103
+ }
104
+ /**
105
+ * Set metadata for this span
106
+ */
107
+ setMetadata(key, value) {
108
+ this.data.metadata[key] = value;
109
+ return this;
110
+ }
111
+ /**
112
+ * Set multiple metadata fields at once
113
+ */
114
+ setMetadataAll(metadata) {
115
+ this.data.metadata = { ...this.data.metadata, ...metadata };
116
+ return this;
117
+ }
118
+ /**
119
+ * Set token usage information
120
+ */
121
+ setTokens(tokens) {
122
+ this.data.tokens = tokens;
123
+ return this;
124
+ }
125
+ /**
126
+ * End the span and calculate duration
127
+ */
128
+ end() {
129
+ const endTime = Date.now();
130
+ this.data.end_time = new Date(endTime);
131
+ this.data.duration_ms = endTime - this.startTime;
132
+ }
133
+ /**
134
+ * Get the span ID
135
+ */
136
+ get id() {
137
+ return this.data.span_id;
138
+ }
139
+ /**
140
+ * Get the span data for serialization
141
+ */
142
+ toJSON() {
143
+ return { ...this.data };
144
+ }
145
+ };
146
+ var Trace = class {
147
+ data;
148
+ startTime;
149
+ spans = [];
150
+ ended = false;
151
+ constructor(name, organizationId, projectId) {
152
+ this.startTime = Date.now();
153
+ this.data = {
154
+ trace_id: generateId(),
155
+ organization_id: organizationId,
156
+ project_id: projectId,
157
+ name,
158
+ status: "running" /* RUNNING */,
159
+ start_time: new Date(this.startTime),
160
+ end_time: new Date(this.startTime),
161
+ // Will be updated on end()
162
+ duration_ms: 0,
163
+ metadata: {},
164
+ spans: [],
165
+ tags: []
166
+ };
167
+ }
168
+ /**
169
+ * Start a new span within this trace
170
+ */
171
+ startSpan(name, type, parentSpanId) {
172
+ if (this.ended) {
173
+ throw new Error("Cannot start span on an ended trace");
174
+ }
175
+ const span = new Span(name, type, parentSpanId);
176
+ this.spans.push(span);
177
+ return span;
178
+ }
179
+ /**
180
+ * Add metadata to the trace
181
+ */
182
+ setMetadata(key, value) {
183
+ this.data.metadata[key] = value;
184
+ return this;
185
+ }
186
+ /**
187
+ * Set multiple metadata fields at once
188
+ */
189
+ setMetadataAll(metadata) {
190
+ this.data.metadata = { ...this.data.metadata, ...metadata };
191
+ return this;
192
+ }
193
+ /**
194
+ * Add tags to the trace
195
+ */
196
+ addTag(tag) {
197
+ if (!this.data.tags.includes(tag)) {
198
+ this.data.tags.push(tag);
199
+ }
200
+ return this;
201
+ }
202
+ /**
203
+ * Add multiple tags at once
204
+ */
205
+ addTags(tags) {
206
+ tags.forEach((tag) => this.addTag(tag));
207
+ return this;
208
+ }
209
+ /**
210
+ * Set the user ID associated with this trace
211
+ */
212
+ setUserId(userId) {
213
+ this.data.user_id = userId;
214
+ return this;
215
+ }
216
+ /**
217
+ * Set the session ID associated with this trace
218
+ */
219
+ setSessionId(sessionId) {
220
+ this.data.session_id = sessionId;
221
+ return this;
222
+ }
223
+ /**
224
+ * End the trace with a final status
225
+ */
226
+ end(status = "success" /* SUCCESS */) {
227
+ if (this.ended) {
228
+ return;
229
+ }
230
+ const endTime = Date.now();
231
+ this.data.end_time = new Date(endTime);
232
+ this.data.duration_ms = endTime - this.startTime;
233
+ this.data.status = status;
234
+ this.data.spans = this.spans.map((span) => span.toJSON());
235
+ this.ended = true;
236
+ }
237
+ /**
238
+ * Get the trace ID
239
+ */
240
+ get id() {
241
+ return this.data.trace_id;
242
+ }
243
+ /**
244
+ * Check if the trace has ended
245
+ */
246
+ get isEnded() {
247
+ return this.ended;
248
+ }
249
+ /**
250
+ * Get the trace data for serialization
251
+ */
252
+ toJSON() {
253
+ return { ...this.data };
254
+ }
255
+ };
256
+
257
+ // src/opentelemetry/exporter.ts
258
+ var ObservyzeSpanExporter = class {
259
+ client;
260
+ config;
261
+ constructor(client, config) {
262
+ this.client = client;
263
+ this.config = {
264
+ serviceName: config?.serviceName || "unknown-service",
265
+ projectId: config?.projectId || "",
266
+ defaultSpanType: config?.defaultSpanType || "llm",
267
+ headers: config?.headers || {}
268
+ };
269
+ }
270
+ /**
271
+ * Export spans — called by OTel SDK when spans are ready.
272
+ * Converts OTel spans to Observyze traces and buffers them.
273
+ */
274
+ async export(spans, resultCallback) {
275
+ if (!spans || spans.length === 0) {
276
+ resultCallback({ code: 0 });
277
+ return;
278
+ }
279
+ try {
280
+ const organizationId = this.client.config?.organizationId || "";
281
+ const projectId = this.config.projectId || this.client.config?.projectId || "";
282
+ for (const span of spans) {
283
+ const spanContext = span.spanContext();
284
+ const traceId = spanContext?.traceId || span.spanId();
285
+ const trace = new Trace(
286
+ span.name || "otel-span",
287
+ organizationId,
288
+ projectId
289
+ );
290
+ trace.setMetadata("source", "opentelemetry");
291
+ trace.setMetadata("otel.trace_id", traceId);
292
+ trace.setMetadata("otel.span_id", spanContext?.spanId || "");
293
+ trace.setMetadata("service.name", this.config.serviceName);
294
+ trace.setMetadataAll(span.attributes || {});
295
+ if (span.resource?.attributes) {
296
+ trace.setMetadataAll(span.resource.attributes);
297
+ }
298
+ const input = span.attributes?.["gen_ai.prompt.0.content"] || span.attributes?.["gen_ai.completion.0.content"] || span.attributes?.["llm.input"] || void 0;
299
+ const output = span.attributes?.["gen_ai.completion.0.content"] || span.attributes?.["llm.output"] || void 0;
300
+ const model = span.attributes?.["gen_ai.request.model"] || span.attributes?.["llm.model"] || void 0;
301
+ const provider = span.attributes?.["gen_ai.request.provider"] || span.attributes?.["llm.provider"] || void 0;
302
+ const inputTokens = typeof span.attributes?.["gen_ai.usage.input_tokens"] === "number" ? span.attributes["gen_ai.usage.input_tokens"] : void 0;
303
+ const outputTokens = typeof span.attributes?.["gen_ai.usage.output_tokens"] === "number" ? span.attributes["gen_ai.usage.output_tokens"] : void 0;
304
+ trace.setMetadata("provider", provider || "unknown");
305
+ trace.setMetadata("model", model || "unknown");
306
+ const nsDuration = span.duration;
307
+ const durationMs = nsDuration ? Math.round(nsDuration / 1e6) : 0;
308
+ trace.setMetadata("latency_ms", durationMs);
309
+ trace.setMetadata("otel.duration_ns", nsDuration);
310
+ const oSpan = trace.startSpan(span.name || "otel-operation", this.config.defaultSpanType);
311
+ if (input) oSpan.setInput(input);
312
+ if (output) oSpan.setOutput(output);
313
+ if (inputTokens || outputTokens) {
314
+ oSpan.setTokens({
315
+ input: inputTokens || 0,
316
+ output: outputTokens || 0,
317
+ total: (inputTokens || 0) + (outputTokens || 0)
318
+ });
319
+ }
320
+ if (model) oSpan.setMetadata("model", model);
321
+ if (provider) oSpan.setMetadata("provider", provider);
322
+ if (span.attributes) oSpan.setMetadataAll(span.attributes);
323
+ const status = span.status;
324
+ const statusCode = status?.code;
325
+ if (statusCode === 2) {
326
+ oSpan.setError(new Error(status?.message || "OTel span error"));
327
+ trace.end("error" /* ERROR */);
328
+ } else {
329
+ trace.end("success" /* SUCCESS */);
330
+ }
331
+ }
332
+ resultCallback({ code: 0 });
333
+ } catch (error) {
334
+ resultCallback({
335
+ code: 1,
336
+ error: error instanceof Error ? error : new Error(String(error))
337
+ });
338
+ }
339
+ }
340
+ /**
341
+ * Called when the exporter is shut down.
342
+ * Flushes any remaining buffered traces via the SDK client.
343
+ */
344
+ async shutdown() {
345
+ try {
346
+ await this.client.flush();
347
+ } catch {
348
+ }
349
+ }
350
+ /**
351
+ * Called by the OTel SDK to force-export buffered spans.
352
+ */
353
+ async forceFlush() {
354
+ try {
355
+ await this.client.flush();
356
+ } catch {
357
+ }
358
+ }
359
+ };
360
+
361
+ export {
362
+ __require,
363
+ __esm,
364
+ __export,
365
+ __toCommonJS,
366
+ SpanType,
367
+ TraceStatus,
368
+ init_types,
369
+ Span,
370
+ Trace,
371
+ ObservyzeSpanExporter
372
+ };