@struktur/telemetry 2.1.1 → 2.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/{src/adapters/langfuse/LangfuseAdapter.ts → dist/adapters/langfuse.js} +60 -149
- package/dist/adapters/langfuse.js.map +1 -0
- package/{src/adapters/phoenix/PhoenixAdapter.ts → dist/adapters/phoenix.js} +61 -159
- package/dist/adapters/phoenix.js.map +1 -0
- package/dist/factory.js +592 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.js +601 -0
- package/dist/index.js.map +1 -0
- package/dist/types.js +37 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -11
- package/src/adapters/langfuse/index.ts +0 -6
- package/src/adapters/phoenix/index.ts +0 -6
- package/src/factory.ts +0 -133
- package/src/index.ts +0 -55
- package/src/types.ts +0 -453
- package/tests/adapters/langfuse.test.ts +0 -118
- package/tests/adapters/phoenix.test.ts +0 -132
- package/tests/factory.test.ts +0 -93
- package/tests/types.test.ts +0 -248
|
@@ -1,146 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Span,
|
|
12
|
-
SpanResult,
|
|
13
|
-
TelemetryEvent,
|
|
14
|
-
TelemetryContext,
|
|
15
|
-
PhoenixConfig,
|
|
16
|
-
LLMCallEvent,
|
|
17
|
-
ValidationEvent,
|
|
18
|
-
ChunkEvent,
|
|
19
|
-
ToolCallEvent,
|
|
20
|
-
MergeEvent,
|
|
21
|
-
ParseEvent,
|
|
22
|
-
TokenUsage,
|
|
23
|
-
} from "../../types.js";
|
|
24
|
-
|
|
25
|
-
type OtelSpan = {
|
|
26
|
-
spanContext: () => { spanId: string; traceId: string };
|
|
27
|
-
setStatus: (status: { code: number; message?: string }) => void;
|
|
28
|
-
setAttribute: (key: string, value: string | number | boolean | undefined) => void;
|
|
29
|
-
setAttributes: (attrs: Record<string, string | number | boolean>) => void;
|
|
30
|
-
recordException: (error: Error) => void;
|
|
31
|
-
end: () => void;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Phoenix telemetry adapter using OpenTelemetry
|
|
36
|
-
*/
|
|
37
|
-
export class PhoenixAdapter implements TelemetryAdapter {
|
|
38
|
-
readonly name = "phoenix";
|
|
39
|
-
readonly version = "1.0.0";
|
|
40
|
-
|
|
41
|
-
private config: PhoenixConfig;
|
|
42
|
-
private tracerProvider: { forceFlush?: () => Promise<void> } | null = null;
|
|
43
|
-
private activeSpans = new Map<string, OtelSpan>();
|
|
44
|
-
private otelApi: typeof import("@opentelemetry/api") | null = null;
|
|
45
|
-
private phoenixOtel: typeof import("@arizeai/phoenix-otel") | null = null;
|
|
46
|
-
|
|
47
|
-
constructor(config: PhoenixConfig) {
|
|
1
|
+
// src/adapters/phoenix/PhoenixAdapter.ts
|
|
2
|
+
var PhoenixAdapter = class {
|
|
3
|
+
name = "phoenix";
|
|
4
|
+
version = "1.0.0";
|
|
5
|
+
config;
|
|
6
|
+
tracerProvider = null;
|
|
7
|
+
activeSpans = /* @__PURE__ */ new Map();
|
|
8
|
+
otelApi = null;
|
|
9
|
+
phoenixOtel = null;
|
|
10
|
+
constructor(config) {
|
|
48
11
|
this.config = {
|
|
49
12
|
url: "http://localhost:6006",
|
|
50
13
|
batch: true,
|
|
51
|
-
...config
|
|
14
|
+
...config
|
|
52
15
|
};
|
|
53
16
|
}
|
|
54
|
-
|
|
55
|
-
async initialize(): Promise<void> {
|
|
56
|
-
// Dynamically import OTel dependencies
|
|
17
|
+
async initialize() {
|
|
57
18
|
const [{ register }, otelApi] = await Promise.all([
|
|
58
19
|
import("@arizeai/phoenix-otel"),
|
|
59
|
-
import("@opentelemetry/api")
|
|
20
|
+
import("@opentelemetry/api")
|
|
60
21
|
]);
|
|
61
|
-
|
|
62
22
|
this.otelApi = otelApi;
|
|
63
|
-
this.phoenixOtel = { register }
|
|
64
|
-
|
|
23
|
+
this.phoenixOtel = { register };
|
|
65
24
|
this.tracerProvider = register({
|
|
66
25
|
projectName: this.config.projectName,
|
|
67
26
|
url: this.config.url,
|
|
68
27
|
apiKey: this.config.apiKey,
|
|
69
28
|
batch: this.config.batch,
|
|
70
|
-
headers: this.config.headers
|
|
29
|
+
headers: this.config.headers
|
|
71
30
|
});
|
|
72
31
|
}
|
|
73
|
-
|
|
74
|
-
async shutdown(): Promise<void> {
|
|
32
|
+
async shutdown() {
|
|
75
33
|
if (this.tracerProvider?.forceFlush) {
|
|
76
34
|
await this.tracerProvider.forceFlush();
|
|
77
35
|
}
|
|
78
36
|
}
|
|
79
|
-
|
|
80
|
-
startSpan(context: SpanContext): Span {
|
|
37
|
+
startSpan(context) {
|
|
81
38
|
if (!this.otelApi) {
|
|
82
39
|
throw new Error("PhoenixAdapter not initialized");
|
|
83
40
|
}
|
|
84
|
-
|
|
85
41
|
const tracer = this.otelApi.trace.getTracer("struktur");
|
|
86
|
-
|
|
87
42
|
const spanKind = context.kind;
|
|
88
43
|
const otelSpan = tracer.startSpan(context.name, {
|
|
89
44
|
attributes: {
|
|
90
45
|
"openinference.span.kind": spanKind,
|
|
91
|
-
...context.attributes
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
46
|
+
...context.attributes
|
|
47
|
+
}
|
|
48
|
+
});
|
|
95
49
|
const spanContext = otelSpan.spanContext();
|
|
96
|
-
const span
|
|
50
|
+
const span = {
|
|
97
51
|
id: spanContext.spanId,
|
|
98
52
|
traceId: spanContext.traceId,
|
|
99
53
|
name: context.name,
|
|
100
54
|
kind: context.kind,
|
|
101
55
|
startTime: context.startTime ?? Date.now(),
|
|
102
|
-
parentId: context.parentSpan?.id
|
|
56
|
+
parentId: context.parentSpan?.id
|
|
103
57
|
};
|
|
104
|
-
|
|
105
58
|
this.activeSpans.set(span.id, otelSpan);
|
|
106
59
|
return span;
|
|
107
60
|
}
|
|
108
|
-
|
|
109
|
-
endSpan(span: Span, result?: SpanResult): void {
|
|
61
|
+
endSpan(span, result) {
|
|
110
62
|
const otelSpan = this.activeSpans.get(span.id);
|
|
111
63
|
if (!otelSpan) return;
|
|
112
|
-
|
|
113
64
|
if (result) {
|
|
114
|
-
// OTel status codes: 1 = OK, 2 = ERROR
|
|
115
65
|
otelSpan.setStatus({
|
|
116
66
|
code: result.status === "ok" ? 1 : 2,
|
|
117
|
-
message: result.error?.message
|
|
67
|
+
message: result.error?.message
|
|
118
68
|
});
|
|
119
|
-
|
|
120
|
-
if (result.output !== undefined) {
|
|
69
|
+
if (result.output !== void 0) {
|
|
121
70
|
try {
|
|
122
|
-
const outputStr = typeof result.output === "string"
|
|
123
|
-
? result.output
|
|
124
|
-
: JSON.stringify(result.output);
|
|
71
|
+
const outputStr = typeof result.output === "string" ? result.output : JSON.stringify(result.output);
|
|
125
72
|
otelSpan.setAttribute("output.value", outputStr);
|
|
126
73
|
} catch {
|
|
127
74
|
otelSpan.setAttribute("output.value", "[object]");
|
|
128
75
|
}
|
|
129
76
|
}
|
|
130
|
-
|
|
131
|
-
if (result.latencyMs !== undefined) {
|
|
77
|
+
if (result.latencyMs !== void 0) {
|
|
132
78
|
otelSpan.setAttribute("latency_ms", result.latencyMs);
|
|
133
79
|
}
|
|
134
80
|
}
|
|
135
|
-
|
|
136
81
|
otelSpan.end();
|
|
137
82
|
this.activeSpans.delete(span.id);
|
|
138
83
|
}
|
|
139
|
-
|
|
140
|
-
recordEvent(span: Span, event: TelemetryEvent): void {
|
|
84
|
+
recordEvent(span, event) {
|
|
141
85
|
const otelSpan = this.activeSpans.get(span.id);
|
|
142
86
|
if (!otelSpan) return;
|
|
143
|
-
|
|
144
87
|
switch (event.type) {
|
|
145
88
|
case "llm_call":
|
|
146
89
|
this.recordLLMCall(otelSpan, event);
|
|
@@ -162,14 +105,12 @@ export class PhoenixAdapter implements TelemetryAdapter {
|
|
|
162
105
|
break;
|
|
163
106
|
}
|
|
164
107
|
}
|
|
165
|
-
|
|
166
|
-
setAttributes(span: Span, attributes: Record<string, unknown>): void {
|
|
108
|
+
setAttributes(span, attributes) {
|
|
167
109
|
const otelSpan = this.activeSpans.get(span.id);
|
|
168
110
|
if (!otelSpan) return;
|
|
169
|
-
|
|
170
|
-
const stringAttrs: Record<string, string | number | boolean> = {};
|
|
111
|
+
const stringAttrs = {};
|
|
171
112
|
for (const [key, value] of Object.entries(attributes)) {
|
|
172
|
-
if (value !==
|
|
113
|
+
if (value !== void 0 && value !== null) {
|
|
173
114
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
174
115
|
stringAttrs[key] = value;
|
|
175
116
|
} else {
|
|
@@ -181,158 +122,119 @@ export class PhoenixAdapter implements TelemetryAdapter {
|
|
|
181
122
|
}
|
|
182
123
|
}
|
|
183
124
|
}
|
|
184
|
-
|
|
185
125
|
otelSpan.setAttributes(stringAttrs);
|
|
186
126
|
}
|
|
187
|
-
|
|
188
|
-
setContext(_context: TelemetryContext): void {
|
|
189
|
-
// Phoenix/OpenInference supports context via OTel context propagation
|
|
190
|
-
// This would require setting up context managers
|
|
191
|
-
// For now, attributes can be set on spans directly
|
|
127
|
+
setContext(_context) {
|
|
192
128
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const attrs: Record<string, string | number | boolean> = {
|
|
129
|
+
recordLLMCall(span, event) {
|
|
130
|
+
const attrs = {
|
|
196
131
|
"llm.model_name": event.model,
|
|
197
132
|
"llm.provider": event.provider,
|
|
198
133
|
"llm.temperature": event.input.temperature ?? "",
|
|
199
|
-
"llm.max_tokens": event.input.maxTokens ?? ""
|
|
134
|
+
"llm.max_tokens": event.input.maxTokens ?? ""
|
|
200
135
|
};
|
|
201
|
-
|
|
202
|
-
// Record input messages
|
|
203
136
|
if (event.input.messages.length > 0) {
|
|
204
137
|
attrs["llm.input_messages"] = JSON.stringify(event.input.messages);
|
|
205
138
|
}
|
|
206
|
-
|
|
207
|
-
// Record schema if present
|
|
208
139
|
if (event.input.schema) {
|
|
209
140
|
try {
|
|
210
141
|
attrs["llm.schema"] = JSON.stringify(event.input.schema);
|
|
211
142
|
} catch {
|
|
212
|
-
// Ignore schema serialization errors
|
|
213
143
|
}
|
|
214
144
|
}
|
|
215
|
-
|
|
216
|
-
// Record output
|
|
217
145
|
if (event.output) {
|
|
218
146
|
attrs["output.value"] = event.output.content;
|
|
219
147
|
attrs["llm.structured_output"] = event.output.structured ?? false;
|
|
220
|
-
|
|
221
|
-
// Record token usage
|
|
222
148
|
if (event.output.usage) {
|
|
223
149
|
this.setTokenUsageAttrs(attrs, event.output.usage);
|
|
224
150
|
}
|
|
225
151
|
}
|
|
226
|
-
|
|
227
152
|
attrs["latency_ms"] = event.latencyMs;
|
|
228
|
-
|
|
229
153
|
if (event.error) {
|
|
230
154
|
span.recordException(event.error);
|
|
231
155
|
}
|
|
232
|
-
|
|
233
156
|
span.setAttributes(attrs);
|
|
234
157
|
}
|
|
235
|
-
|
|
236
|
-
private setTokenUsageAttrs(attrs: Record<string, string | number | boolean>, usage: TokenUsage): void {
|
|
158
|
+
setTokenUsageAttrs(attrs, usage) {
|
|
237
159
|
attrs["llm.token_count.prompt"] = usage.input;
|
|
238
160
|
attrs["llm.token_count.completion"] = usage.output;
|
|
239
161
|
attrs["llm.token_count.total"] = usage.total;
|
|
240
162
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const attrs: Record<string, string | number | boolean> = {
|
|
163
|
+
recordValidation(span, event) {
|
|
164
|
+
const attrs = {
|
|
244
165
|
"validation.attempt": event.attempt,
|
|
245
166
|
"validation.max_attempts": event.maxAttempts,
|
|
246
|
-
"validation.success": event.success
|
|
167
|
+
"validation.success": event.success
|
|
247
168
|
};
|
|
248
|
-
|
|
249
169
|
if (event.errors && event.errors.length > 0) {
|
|
250
170
|
attrs["validation.errors"] = JSON.stringify(event.errors);
|
|
251
171
|
}
|
|
252
|
-
|
|
253
|
-
if (event.latencyMs !== undefined) {
|
|
172
|
+
if (event.latencyMs !== void 0) {
|
|
254
173
|
attrs["latency_ms"] = event.latencyMs;
|
|
255
174
|
}
|
|
256
|
-
|
|
257
175
|
span.setAttributes(attrs);
|
|
258
176
|
}
|
|
259
|
-
|
|
260
|
-
private recordChunk(span: OtelSpan, event: ChunkEvent): void {
|
|
177
|
+
recordChunk(span, event) {
|
|
261
178
|
span.setAttributes({
|
|
262
179
|
"chunk.index": event.chunkIndex,
|
|
263
180
|
"chunk.total": event.totalChunks,
|
|
264
181
|
"chunk.tokens": event.tokens,
|
|
265
|
-
"chunk.images": event.images
|
|
182
|
+
"chunk.images": event.images
|
|
266
183
|
});
|
|
267
|
-
|
|
268
184
|
if (event.content) {
|
|
269
|
-
span.setAttribute("chunk.content_preview", event.content.slice(0,
|
|
185
|
+
span.setAttribute("chunk.content_preview", event.content.slice(0, 1e3));
|
|
270
186
|
}
|
|
271
187
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const attrs: Record<string, string | number | boolean> = {
|
|
188
|
+
recordToolCall(span, event) {
|
|
189
|
+
const attrs = {
|
|
275
190
|
"tool.name": event.toolName,
|
|
276
|
-
"tool.args": JSON.stringify(event.args)
|
|
191
|
+
"tool.args": JSON.stringify(event.args)
|
|
277
192
|
};
|
|
278
|
-
|
|
279
|
-
if (event.result !== undefined) {
|
|
193
|
+
if (event.result !== void 0) {
|
|
280
194
|
try {
|
|
281
195
|
attrs["tool.result"] = JSON.stringify(event.result);
|
|
282
196
|
} catch {
|
|
283
197
|
attrs["tool.result"] = "[object]";
|
|
284
198
|
}
|
|
285
199
|
}
|
|
286
|
-
|
|
287
200
|
if (event.error) {
|
|
288
201
|
attrs["tool.error"] = event.error.message;
|
|
289
202
|
}
|
|
290
|
-
|
|
291
|
-
if (event.latencyMs !== undefined) {
|
|
203
|
+
if (event.latencyMs !== void 0) {
|
|
292
204
|
attrs["latency_ms"] = event.latencyMs;
|
|
293
205
|
}
|
|
294
|
-
|
|
295
206
|
span.setAttributes(attrs);
|
|
296
|
-
|
|
297
207
|
if (event.error) {
|
|
298
208
|
span.recordException(event.error);
|
|
299
209
|
}
|
|
300
210
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const attrs: Record<string, string | number | boolean> = {
|
|
211
|
+
recordMerge(span, event) {
|
|
212
|
+
const attrs = {
|
|
304
213
|
"merge.strategy": event.strategy,
|
|
305
214
|
"merge.input_count": event.inputCount,
|
|
306
|
-
"merge.output_count": event.outputCount
|
|
215
|
+
"merge.output_count": event.outputCount
|
|
307
216
|
};
|
|
308
|
-
|
|
309
|
-
if (event.deduped !== undefined) {
|
|
217
|
+
if (event.deduped !== void 0) {
|
|
310
218
|
attrs["merge.deduped"] = event.deduped;
|
|
311
219
|
}
|
|
312
|
-
|
|
313
220
|
span.setAttributes(attrs);
|
|
314
221
|
}
|
|
315
|
-
|
|
316
|
-
private recordParse(span: OtelSpan, event: ParseEvent): void {
|
|
222
|
+
recordParse(span, event) {
|
|
317
223
|
span.setAttributes({
|
|
318
224
|
"parse.mime_type": event.mimeType,
|
|
319
225
|
"parse.parser": event.parser,
|
|
320
226
|
"parse.input_size": event.inputSize,
|
|
321
227
|
"parse.output_tokens": event.outputTokens,
|
|
322
228
|
"parse.output_images": event.outputImages,
|
|
323
|
-
"latency_ms": event.latencyMs
|
|
229
|
+
"latency_ms": event.latencyMs
|
|
324
230
|
});
|
|
325
231
|
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Create a Phoenix telemetry adapter
|
|
330
|
-
*
|
|
331
|
-
* @param config - Phoenix configuration
|
|
332
|
-
* @returns Phoenix telemetry adapter
|
|
333
|
-
*/
|
|
334
|
-
export function createPhoenixAdapter(config: PhoenixConfig): PhoenixAdapter {
|
|
232
|
+
};
|
|
233
|
+
function createPhoenixAdapter(config) {
|
|
335
234
|
return new PhoenixAdapter(config);
|
|
336
235
|
}
|
|
337
|
-
|
|
338
|
-
|
|
236
|
+
export {
|
|
237
|
+
PhoenixAdapter,
|
|
238
|
+
createPhoenixAdapter
|
|
239
|
+
};
|
|
240
|
+
//# sourceMappingURL=phoenix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/phoenix/PhoenixAdapter.ts"],"sourcesContent":["/**\n * Phoenix (Arize) telemetry adapter\n * \n * Implements TelemetryAdapter for Phoenix using OpenTelemetry and\n * OpenInference semantic conventions.\n */\n\nimport type {\n TelemetryAdapter,\n SpanContext,\n Span,\n SpanResult,\n TelemetryEvent,\n TelemetryContext,\n PhoenixConfig,\n LLMCallEvent,\n ValidationEvent,\n ChunkEvent,\n ToolCallEvent,\n MergeEvent,\n ParseEvent,\n TokenUsage,\n} from \"../../types.js\";\n\ntype OtelSpan = {\n spanContext: () => { spanId: string; traceId: string };\n setStatus: (status: { code: number; message?: string }) => void;\n setAttribute: (key: string, value: string | number | boolean | undefined) => void;\n setAttributes: (attrs: Record<string, string | number | boolean>) => void;\n recordException: (error: Error) => void;\n end: () => void;\n};\n\n/**\n * Phoenix telemetry adapter using OpenTelemetry\n */\nexport class PhoenixAdapter implements TelemetryAdapter {\n readonly name = \"phoenix\";\n readonly version = \"1.0.0\";\n\n private config: PhoenixConfig;\n private tracerProvider: { forceFlush?: () => Promise<void> } | null = null;\n private activeSpans = new Map<string, OtelSpan>();\n private otelApi: typeof import(\"@opentelemetry/api\") | null = null;\n private phoenixOtel: typeof import(\"@arizeai/phoenix-otel\") | null = null;\n\n constructor(config: PhoenixConfig) {\n this.config = {\n url: \"http://localhost:6006\",\n batch: true,\n ...config,\n };\n }\n\n async initialize(): Promise<void> {\n // Dynamically import OTel dependencies\n const [{ register }, otelApi] = await Promise.all([\n import(\"@arizeai/phoenix-otel\"),\n import(\"@opentelemetry/api\"),\n ]);\n\n this.otelApi = otelApi;\n this.phoenixOtel = { register } as typeof import(\"@arizeai/phoenix-otel\");\n\n this.tracerProvider = register({\n projectName: this.config.projectName,\n url: this.config.url,\n apiKey: this.config.apiKey,\n batch: this.config.batch,\n headers: this.config.headers,\n });\n }\n\n async shutdown(): Promise<void> {\n if (this.tracerProvider?.forceFlush) {\n await this.tracerProvider.forceFlush();\n }\n }\n\n startSpan(context: SpanContext): Span {\n if (!this.otelApi) {\n throw new Error(\"PhoenixAdapter not initialized\");\n }\n\n const tracer = this.otelApi.trace.getTracer(\"struktur\");\n \n const spanKind = context.kind;\n const otelSpan = tracer.startSpan(context.name, {\n attributes: {\n \"openinference.span.kind\": spanKind,\n ...context.attributes,\n },\n }) as OtelSpan;\n\n const spanContext = otelSpan.spanContext();\n const span: Span = {\n id: spanContext.spanId,\n traceId: spanContext.traceId,\n name: context.name,\n kind: context.kind,\n startTime: context.startTime ?? Date.now(),\n parentId: context.parentSpan?.id,\n };\n\n this.activeSpans.set(span.id, otelSpan);\n return span;\n }\n\n endSpan(span: Span, result?: SpanResult): void {\n const otelSpan = this.activeSpans.get(span.id);\n if (!otelSpan) return;\n\n if (result) {\n // OTel status codes: 1 = OK, 2 = ERROR\n otelSpan.setStatus({\n code: result.status === \"ok\" ? 1 : 2,\n message: result.error?.message,\n });\n\n if (result.output !== undefined) {\n try {\n const outputStr = typeof result.output === \"string\" \n ? result.output \n : JSON.stringify(result.output);\n otelSpan.setAttribute(\"output.value\", outputStr);\n } catch {\n otelSpan.setAttribute(\"output.value\", \"[object]\");\n }\n }\n\n if (result.latencyMs !== undefined) {\n otelSpan.setAttribute(\"latency_ms\", result.latencyMs);\n }\n }\n\n otelSpan.end();\n this.activeSpans.delete(span.id);\n }\n\n recordEvent(span: Span, event: TelemetryEvent): void {\n const otelSpan = this.activeSpans.get(span.id);\n if (!otelSpan) return;\n\n switch (event.type) {\n case \"llm_call\":\n this.recordLLMCall(otelSpan, event);\n break;\n case \"validation\":\n this.recordValidation(otelSpan, event);\n break;\n case \"chunk\":\n this.recordChunk(otelSpan, event);\n break;\n case \"tool_call\":\n this.recordToolCall(otelSpan, event);\n break;\n case \"merge\":\n this.recordMerge(otelSpan, event);\n break;\n case \"parse\":\n this.recordParse(otelSpan, event);\n break;\n }\n }\n\n setAttributes(span: Span, attributes: Record<string, unknown>): void {\n const otelSpan = this.activeSpans.get(span.id);\n if (!otelSpan) return;\n\n const stringAttrs: Record<string, string | number | boolean> = {};\n for (const [key, value] of Object.entries(attributes)) {\n if (value !== undefined && value !== null) {\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n stringAttrs[key] = value;\n } else {\n try {\n stringAttrs[key] = JSON.stringify(value);\n } catch {\n stringAttrs[key] = String(value);\n }\n }\n }\n }\n\n otelSpan.setAttributes(stringAttrs);\n }\n\n setContext(_context: TelemetryContext): void {\n // Phoenix/OpenInference supports context via OTel context propagation\n // This would require setting up context managers\n // For now, attributes can be set on spans directly\n }\n\n private recordLLMCall(span: OtelSpan, event: LLMCallEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n \"llm.model_name\": event.model,\n \"llm.provider\": event.provider,\n \"llm.temperature\": event.input.temperature ?? \"\",\n \"llm.max_tokens\": event.input.maxTokens ?? \"\",\n };\n\n // Record input messages\n if (event.input.messages.length > 0) {\n attrs[\"llm.input_messages\"] = JSON.stringify(event.input.messages);\n }\n\n // Record schema if present\n if (event.input.schema) {\n try {\n attrs[\"llm.schema\"] = JSON.stringify(event.input.schema);\n } catch {\n // Ignore schema serialization errors\n }\n }\n\n // Record output\n if (event.output) {\n attrs[\"output.value\"] = event.output.content;\n attrs[\"llm.structured_output\"] = event.output.structured ?? false;\n\n // Record token usage\n if (event.output.usage) {\n this.setTokenUsageAttrs(attrs, event.output.usage);\n }\n }\n\n attrs[\"latency_ms\"] = event.latencyMs;\n\n if (event.error) {\n span.recordException(event.error);\n }\n\n span.setAttributes(attrs);\n }\n\n private setTokenUsageAttrs(attrs: Record<string, string | number | boolean>, usage: TokenUsage): void {\n attrs[\"llm.token_count.prompt\"] = usage.input;\n attrs[\"llm.token_count.completion\"] = usage.output;\n attrs[\"llm.token_count.total\"] = usage.total;\n }\n\n private recordValidation(span: OtelSpan, event: ValidationEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n \"validation.attempt\": event.attempt,\n \"validation.max_attempts\": event.maxAttempts,\n \"validation.success\": event.success,\n };\n\n if (event.errors && event.errors.length > 0) {\n attrs[\"validation.errors\"] = JSON.stringify(event.errors);\n }\n\n if (event.latencyMs !== undefined) {\n attrs[\"latency_ms\"] = event.latencyMs;\n }\n\n span.setAttributes(attrs);\n }\n\n private recordChunk(span: OtelSpan, event: ChunkEvent): void {\n span.setAttributes({\n \"chunk.index\": event.chunkIndex,\n \"chunk.total\": event.totalChunks,\n \"chunk.tokens\": event.tokens,\n \"chunk.images\": event.images,\n });\n\n if (event.content) {\n span.setAttribute(\"chunk.content_preview\", event.content.slice(0, 1000));\n }\n }\n\n private recordToolCall(span: OtelSpan, event: ToolCallEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n \"tool.name\": event.toolName,\n \"tool.args\": JSON.stringify(event.args),\n };\n\n if (event.result !== undefined) {\n try {\n attrs[\"tool.result\"] = JSON.stringify(event.result);\n } catch {\n attrs[\"tool.result\"] = \"[object]\";\n }\n }\n\n if (event.error) {\n attrs[\"tool.error\"] = event.error.message;\n }\n\n if (event.latencyMs !== undefined) {\n attrs[\"latency_ms\"] = event.latencyMs;\n }\n\n span.setAttributes(attrs);\n\n if (event.error) {\n span.recordException(event.error);\n }\n }\n\n private recordMerge(span: OtelSpan, event: MergeEvent): void {\n const attrs: Record<string, string | number | boolean> = {\n \"merge.strategy\": event.strategy,\n \"merge.input_count\": event.inputCount,\n \"merge.output_count\": event.outputCount,\n };\n\n if (event.deduped !== undefined) {\n attrs[\"merge.deduped\"] = event.deduped;\n }\n\n span.setAttributes(attrs);\n }\n\n private recordParse(span: OtelSpan, event: ParseEvent): void {\n span.setAttributes({\n \"parse.mime_type\": event.mimeType,\n \"parse.parser\": event.parser,\n \"parse.input_size\": event.inputSize,\n \"parse.output_tokens\": event.outputTokens,\n \"parse.output_images\": event.outputImages,\n \"latency_ms\": event.latencyMs,\n });\n }\n}\n\n/**\n * Create a Phoenix telemetry adapter\n * \n * @param config - Phoenix configuration\n * @returns Phoenix telemetry adapter\n */\nexport function createPhoenixAdapter(config: PhoenixConfig): PhoenixAdapter {\n return new PhoenixAdapter(config);\n}\n\nexport type { PhoenixConfig };\n"],"mappings":"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","names":[]}
|