@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 +21 -0
- package/README.md +10 -175
- package/dist/chunk-YRMQCX2P.mjs +372 -0
- package/dist/index-DEorAmFu.d.mts +405 -0
- package/dist/index-DEorAmFu.d.ts +405 -0
- package/dist/index.d.mts +60 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +1029 -0
- package/dist/index.mjs +673 -0
- package/dist/opentelemetry/index.d.mts +1 -0
- package/dist/opentelemetry/index.d.ts +1 -0
- package/dist/opentelemetry/index.js +335 -0
- package/dist/opentelemetry/index.mjs +6 -0
- package/package.json +15 -8
- package/INSTRUMENTATION_SUMMARY.md +0 -184
- package/examples/auto-instrumentation.ts +0 -210
- package/src/client.ts +0 -578
- package/src/index.ts +0 -21
- package/src/instrumentation/README.md +0 -227
- package/src/instrumentation/anthropic.ts +0 -233
- package/src/instrumentation/index.ts +0 -43
- package/src/instrumentation/openai.ts +0 -193
- package/src/trace.ts +0 -242
- package/src/types.ts +0 -102
- package/tsconfig.json +0 -14
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
|
-
#
|
|
1
|
+
# Observyze Node.js SDK
|
|
2
2
|
|
|
3
|
-
Node.js SDK for Observyze
|
|
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
|
-
##
|
|
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.
|
|
24
|
-
|
|
25
|
-
projectId: 'your-project-id'
|
|
18
|
+
apiKey: process.env.OBSERVYZE_API_KEY,
|
|
19
|
+
projectId: 'my-project'
|
|
26
20
|
})
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
const openai = new OpenAI({
|
|
30
|
-
apiKey: process.env.OPENAI_API_KEY!
|
|
31
|
-
})
|
|
22
|
+
const openai = nw.wrap(new OpenAI())
|
|
32
23
|
|
|
33
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|