@saguiitay/eventhouse-ai-observability 0.1.1 → 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/CHANGELOG.md +48 -0
- package/README.md +159 -43
- package/dist/adapters/openai.d.ts +25 -0
- package/dist/adapters/vercel.d.ts +25 -0
- package/dist/{chunk-XFSF25OP.js → chunk-XGCHW72B.js} +11 -55
- package/dist/{chunk-XFSF25OP.js.map → chunk-XGCHW72B.js.map} +1 -1
- package/dist/copilot.js +5 -3
- package/dist/copilot.js.map +1 -1
- package/dist/index.d.ts +1 -6
- package/dist/index.js +3 -11
- package/dist/openai.d.ts +12 -0
- package/dist/openai.js +408 -0
- package/dist/openai.js.map +1 -0
- package/dist/validate.d.ts +2 -0
- package/dist/validate.js +237 -0
- package/dist/validate.js.map +1 -0
- package/dist/validation/fabric.d.ts +29 -0
- package/dist/vercel.d.ts +15 -0
- package/dist/vercel.js +369 -0
- package/dist/vercel.js.map +1 -0
- package/docs/architecture.md +19 -3
- package/docs/copilot-adapter.md +1 -1
- package/docs/extending.md +45 -1
- package/docs/fabric-setup.md +24 -0
- package/docs/implementation-report.md +62 -18
- package/docs/openai-adapter.md +137 -0
- package/docs/quickstart.md +75 -6
- package/docs/research-eventstream.md +8 -6
- package/docs/research.md +1 -1
- package/docs/security.md +1 -1
- package/docs/telemetry-schema.md +18 -0
- package/docs/vercel-adapter.md +201 -0
- package/examples/README.md +85 -5
- package/examples/copilot-basic/package.json +1 -1
- package/examples/openai-basic/env.example +8 -0
- package/examples/openai-basic/index.ts +46 -0
- package/examples/shared.ts +3 -62
- package/examples/telemetry.ts +124 -0
- package/examples/vercel-basic/env.example +8 -0
- package/examples/vercel-basic/index.ts +60 -0
- package/fabric-setup.zip +0 -0
- package/package.json +39 -5
package/dist/openai.js
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createObservability
|
|
3
|
+
} from "./chunk-XGCHW72B.js";
|
|
4
|
+
|
|
5
|
+
// src/adapters/openai.ts
|
|
6
|
+
import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
7
|
+
var record = (value) => typeof value === "object" && value !== null ? value : {};
|
|
8
|
+
function limit(value, name, maximum) {
|
|
9
|
+
if (!Number.isInteger(value) || value < 1 || value > maximum)
|
|
10
|
+
throw new Error(`${name} must be an integer between 1 and ${maximum}`);
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function validateOpenAIAttachOptions(options) {
|
|
14
|
+
limit(options.maxTrackedRequests ?? 1024, "maxTrackedRequests", 65536);
|
|
15
|
+
limit(options.maxContentChars ?? 4096, "maxContentChars", 4096);
|
|
16
|
+
}
|
|
17
|
+
function text(value, maximum, depth = 0) {
|
|
18
|
+
if (typeof value === "string") return value.slice(0, maximum);
|
|
19
|
+
if (!Array.isArray(value) || depth >= 3) return "";
|
|
20
|
+
let result = "";
|
|
21
|
+
for (const item of value.slice(0, 32)) {
|
|
22
|
+
if (result.length >= maximum) break;
|
|
23
|
+
const entry = record(item);
|
|
24
|
+
const part = typeof entry.text === "string" ? entry.text : typeof entry.content === "string" ? entry.content : Array.isArray(entry.content) ? text(entry.content, maximum - result.length, depth + 1) : "";
|
|
25
|
+
result += part.slice(0, maximum - result.length);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
function tokens(span, usage) {
|
|
30
|
+
const data = record(usage);
|
|
31
|
+
const input = record(data.prompt_tokens_details ?? data.input_tokens_details);
|
|
32
|
+
const output = record(
|
|
33
|
+
data.completion_tokens_details ?? data.output_tokens_details
|
|
34
|
+
);
|
|
35
|
+
for (const [attribute, value] of [
|
|
36
|
+
["gen_ai.usage.input_tokens", data.prompt_tokens ?? data.input_tokens],
|
|
37
|
+
[
|
|
38
|
+
"gen_ai.usage.output_tokens",
|
|
39
|
+
data.completion_tokens ?? data.output_tokens
|
|
40
|
+
],
|
|
41
|
+
["gen_ai.usage.cache_read.input_tokens", input.cached_tokens],
|
|
42
|
+
["gen_ai.usage.cache_creation.input_tokens", input.cache_write_tokens],
|
|
43
|
+
["gen_ai.usage.reasoning.output_tokens", output.reasoning_tokens]
|
|
44
|
+
]) {
|
|
45
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0)
|
|
46
|
+
span.setAttribute(attribute, value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function attachOpenAI(client, engine, options = {}) {
|
|
50
|
+
const maximum = Math.min(
|
|
51
|
+
engine.maxActiveSpans,
|
|
52
|
+
limit(options.maxTrackedRequests ?? 1024, "maxTrackedRequests", 65536)
|
|
53
|
+
);
|
|
54
|
+
const contentLimit = limit(
|
|
55
|
+
options.maxContentChars ?? 4096,
|
|
56
|
+
"maxContentChars",
|
|
57
|
+
4096
|
|
58
|
+
);
|
|
59
|
+
const active = /* @__PURE__ */ new Set();
|
|
60
|
+
let closed = false;
|
|
61
|
+
let skipped = 0;
|
|
62
|
+
let shutdownPromise;
|
|
63
|
+
const safe = (callback) => {
|
|
64
|
+
try {
|
|
65
|
+
callback();
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const fail = (state, error) => {
|
|
70
|
+
let reason = "request_error";
|
|
71
|
+
safe(() => {
|
|
72
|
+
const data = record(error);
|
|
73
|
+
const status = data.status;
|
|
74
|
+
if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599)
|
|
75
|
+
state.span.setAttribute("http.response.status_code", status);
|
|
76
|
+
if (data.name === "AbortError" || data.name === "APIUserAbortError")
|
|
77
|
+
reason = "aborted";
|
|
78
|
+
});
|
|
79
|
+
safe(() => state.finish(reason));
|
|
80
|
+
};
|
|
81
|
+
function inspect(state, value, api) {
|
|
82
|
+
if (state.ended) return;
|
|
83
|
+
const data = record(value);
|
|
84
|
+
const response = api === "responses" && data.response ? record(data.response) : data;
|
|
85
|
+
if (typeof response.model === "string")
|
|
86
|
+
state.span.setAttribute(
|
|
87
|
+
"gen_ai.response.model",
|
|
88
|
+
response.model.slice(0, 256)
|
|
89
|
+
);
|
|
90
|
+
if (typeof response.id === "string")
|
|
91
|
+
state.span.setAttribute("gen_ai.response.id", response.id.slice(0, 256));
|
|
92
|
+
tokens(state.span, response.usage);
|
|
93
|
+
if (api === "responses") {
|
|
94
|
+
if (data.type === "response.completed") state.sawTerminal = true;
|
|
95
|
+
if (data.type === "response.failed" || data.type === "error" || response.status === "failed" || response.error) {
|
|
96
|
+
state.finish("response_failed");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (data.type === "response.incomplete" || response.status === "incomplete") {
|
|
100
|
+
state.finish("response_incomplete");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (engine.captureContent) {
|
|
104
|
+
const piece = data.type === "response.output_text.delta" ? text(data.delta, contentLimit) : state.output.length === 0 ? text(response.output, contentLimit) : "";
|
|
105
|
+
if (piece)
|
|
106
|
+
state.output += piece.slice(0, contentLimit - state.output.length);
|
|
107
|
+
}
|
|
108
|
+
} else if (Array.isArray(data.choices)) {
|
|
109
|
+
const reasons = [];
|
|
110
|
+
for (const choice of data.choices.slice(0, 32)) {
|
|
111
|
+
const item = record(choice);
|
|
112
|
+
if (typeof item.finish_reason === "string" && [
|
|
113
|
+
"stop",
|
|
114
|
+
"length",
|
|
115
|
+
"tool_calls",
|
|
116
|
+
"content_filter",
|
|
117
|
+
"function_call"
|
|
118
|
+
].includes(item.finish_reason)) {
|
|
119
|
+
reasons.push(item.finish_reason);
|
|
120
|
+
if (state.expectedChoices !== void 0 && typeof item.index === "number" && Number.isInteger(item.index) && item.index >= 0 && item.index < state.expectedChoices) {
|
|
121
|
+
state.completedChoices.add(item.index);
|
|
122
|
+
state.sawTerminal = state.completedChoices.size === state.expectedChoices;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (reasons.length)
|
|
127
|
+
state.span.setAttribute("gen_ai.response.finish_reasons", reasons);
|
|
128
|
+
if (engine.captureContent) {
|
|
129
|
+
const first = record(
|
|
130
|
+
data.choices.slice(0, 32).find((choice) => record(choice).index === 0)
|
|
131
|
+
);
|
|
132
|
+
const piece = text(
|
|
133
|
+
record(first.delta ?? first.message).content,
|
|
134
|
+
contentLimit - state.output.length
|
|
135
|
+
);
|
|
136
|
+
state.output += piece;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function wrapStream(state, stream, api) {
|
|
141
|
+
let reading = false;
|
|
142
|
+
const onAbort = () => {
|
|
143
|
+
if (!reading) safe(() => state.finish("aborted"));
|
|
144
|
+
};
|
|
145
|
+
stream.controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
146
|
+
state.cleanup.push(
|
|
147
|
+
() => stream.controller.signal.removeEventListener("abort", onAbort)
|
|
148
|
+
);
|
|
149
|
+
if (stream.controller.signal.aborted) onAbort();
|
|
150
|
+
const Constructor = stream.constructor;
|
|
151
|
+
const wrapped = new Constructor(async function* () {
|
|
152
|
+
let exhausted = false;
|
|
153
|
+
try {
|
|
154
|
+
const source = stream[Symbol.asyncIterator]();
|
|
155
|
+
const iterator = {
|
|
156
|
+
async next() {
|
|
157
|
+
reading = true;
|
|
158
|
+
try {
|
|
159
|
+
return await source.next();
|
|
160
|
+
} finally {
|
|
161
|
+
reading = false;
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
...source.return ? {
|
|
165
|
+
async return(value) {
|
|
166
|
+
reading = true;
|
|
167
|
+
try {
|
|
168
|
+
return await source.return(value);
|
|
169
|
+
} finally {
|
|
170
|
+
reading = false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} : {}
|
|
174
|
+
};
|
|
175
|
+
for await (const item of {
|
|
176
|
+
[Symbol.asyncIterator]: () => iterator
|
|
177
|
+
}) {
|
|
178
|
+
safe(() => inspect(state, item, api));
|
|
179
|
+
yield item;
|
|
180
|
+
}
|
|
181
|
+
exhausted = true;
|
|
182
|
+
safe(
|
|
183
|
+
() => state.finish(
|
|
184
|
+
stream.controller.signal.aborted ? "aborted" : state.sawTerminal ? void 0 : "stream_incomplete"
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
fail(state, error);
|
|
189
|
+
throw error;
|
|
190
|
+
} finally {
|
|
191
|
+
if (!exhausted) safe(() => state.finish("cancelled"));
|
|
192
|
+
}
|
|
193
|
+
}, stream.controller);
|
|
194
|
+
safe(() => {
|
|
195
|
+
const requestId = Object.getOwnPropertyDescriptor(stream, "_request_id");
|
|
196
|
+
if (requestId) Object.defineProperty(wrapped, "_request_id", requestId);
|
|
197
|
+
});
|
|
198
|
+
return wrapped;
|
|
199
|
+
}
|
|
200
|
+
function instrument(target, method, args, api) {
|
|
201
|
+
if (closed || active.size >= maximum) {
|
|
202
|
+
if (!closed) skipped++;
|
|
203
|
+
return Reflect.apply(method, target, args);
|
|
204
|
+
}
|
|
205
|
+
let state;
|
|
206
|
+
safe(() => {
|
|
207
|
+
const request = record(args[0]);
|
|
208
|
+
const span = engine.startSpan("chat", {
|
|
209
|
+
kind: SpanKind.CLIENT,
|
|
210
|
+
attributes: {
|
|
211
|
+
"gen_ai.operation.name": "chat",
|
|
212
|
+
"gen_ai.provider.name": "openai",
|
|
213
|
+
"gen_ai.system": "openai",
|
|
214
|
+
"openai.api": api,
|
|
215
|
+
...typeof request.model === "string" ? { "gen_ai.request.model": request.model.slice(0, 256) } : {}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
state = {
|
|
219
|
+
span,
|
|
220
|
+
ended: false,
|
|
221
|
+
output: "",
|
|
222
|
+
sawTerminal: false,
|
|
223
|
+
expectedChoices: request.n === void 0 || request.n === null ? 1 : typeof request.n === "number" && Number.isInteger(request.n) && request.n >= 1 && request.n <= 32 ? request.n : void 0,
|
|
224
|
+
completedChoices: /* @__PURE__ */ new Set(),
|
|
225
|
+
cleanup: [],
|
|
226
|
+
finish(reason) {
|
|
227
|
+
if (this.ended) return;
|
|
228
|
+
this.ended = true;
|
|
229
|
+
active.delete(this);
|
|
230
|
+
for (const cleanup of this.cleanup) safe(cleanup);
|
|
231
|
+
this.cleanup.length = 0;
|
|
232
|
+
this.completedChoices.clear();
|
|
233
|
+
safe(() => {
|
|
234
|
+
if (reason) {
|
|
235
|
+
span.setAttribute("openai.lifecycle", reason);
|
|
236
|
+
if ([
|
|
237
|
+
"observer_shutdown",
|
|
238
|
+
"stream_incomplete",
|
|
239
|
+
"response_incomplete",
|
|
240
|
+
"cancelled",
|
|
241
|
+
"aborted",
|
|
242
|
+
"unsupported_result"
|
|
243
|
+
].includes(reason))
|
|
244
|
+
span.setAttribute("observability.incomplete", true);
|
|
245
|
+
span.setAttribute("error.type", `openai.${reason}`);
|
|
246
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
247
|
+
}
|
|
248
|
+
if (engine.captureContent && this.output)
|
|
249
|
+
span.setAttribute("gen_ai.output.messages", this.output);
|
|
250
|
+
});
|
|
251
|
+
this.output = "";
|
|
252
|
+
safe(() => span.end());
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
active.add(state);
|
|
256
|
+
if (engine.captureContent) {
|
|
257
|
+
const input = text(
|
|
258
|
+
api === "responses" ? request.input : request.messages,
|
|
259
|
+
contentLimit
|
|
260
|
+
);
|
|
261
|
+
if (input) span.setAttribute("gen_ai.input.messages", input);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
const current = state;
|
|
265
|
+
let result;
|
|
266
|
+
try {
|
|
267
|
+
result = Reflect.apply(method, target, args);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (current) fail(current, error);
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
if (!current) return result;
|
|
273
|
+
if (typeof result !== "object" || result === null) {
|
|
274
|
+
safe(() => current.finish("unsupported_result"));
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
let parsed;
|
|
278
|
+
const streams = /* @__PURE__ */ new WeakMap();
|
|
279
|
+
const observeData = (data) => {
|
|
280
|
+
let observed = data;
|
|
281
|
+
safe(() => {
|
|
282
|
+
const candidate = record(data);
|
|
283
|
+
if (candidate.controller instanceof AbortController && typeof data[Symbol.asyncIterator] === "function") {
|
|
284
|
+
const existing = streams.get(data);
|
|
285
|
+
if (existing) {
|
|
286
|
+
observed = existing;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (current.ended) return;
|
|
290
|
+
const wrapped = wrapStream(current, data, api);
|
|
291
|
+
streams.set(data, wrapped);
|
|
292
|
+
observed = wrapped;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (current.ended) return;
|
|
296
|
+
safe(() => inspect(current, data, api));
|
|
297
|
+
const status = record(data).status;
|
|
298
|
+
current.finish(
|
|
299
|
+
status === "queued" || status === "in_progress" ? "response_incomplete" : void 0
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
return observed;
|
|
303
|
+
};
|
|
304
|
+
const reject = (error) => {
|
|
305
|
+
fail(current, error);
|
|
306
|
+
throw error;
|
|
307
|
+
};
|
|
308
|
+
const parse = () => parsed ??= Promise.resolve(result).then(observeData, reject);
|
|
309
|
+
return new Proxy(result, {
|
|
310
|
+
get(promise, key) {
|
|
311
|
+
if (key === "then")
|
|
312
|
+
return (...callbacks) => parse().then(...callbacks);
|
|
313
|
+
if (key === "catch")
|
|
314
|
+
return (...callbacks) => parse().catch(...callbacks);
|
|
315
|
+
if (key === "finally")
|
|
316
|
+
return (...callbacks) => parse().finally(...callbacks);
|
|
317
|
+
const value = Reflect.get(promise, key, promise);
|
|
318
|
+
if (key === "withResponse" && typeof value === "function")
|
|
319
|
+
return (...parameters) => Promise.resolve(Reflect.apply(value, promise, parameters)).then(
|
|
320
|
+
(response) => {
|
|
321
|
+
const envelope = record(response);
|
|
322
|
+
return { ...envelope, data: observeData(envelope.data) };
|
|
323
|
+
},
|
|
324
|
+
reject
|
|
325
|
+
);
|
|
326
|
+
if (key === "asResponse" && typeof value === "function")
|
|
327
|
+
return (...parameters) => Promise.resolve(Reflect.apply(value, promise, parameters)).then(
|
|
328
|
+
(response) => {
|
|
329
|
+
safe(() => {
|
|
330
|
+
current.span.setAttribute("openai.raw_response", true);
|
|
331
|
+
current.finish();
|
|
332
|
+
});
|
|
333
|
+
return response;
|
|
334
|
+
},
|
|
335
|
+
reject
|
|
336
|
+
);
|
|
337
|
+
return typeof value === "function" && key !== "constructor" ? value.bind(promise) : value;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const proxy = (target, path) => {
|
|
342
|
+
const cache = /* @__PURE__ */ new Map();
|
|
343
|
+
return new Proxy(target, {
|
|
344
|
+
get(resource, key) {
|
|
345
|
+
const value = Reflect.get(resource, key, resource);
|
|
346
|
+
const cached = cache.get(key);
|
|
347
|
+
if (cached && cached.value === value) return cached.wrapped;
|
|
348
|
+
const next = [...path, String(key)];
|
|
349
|
+
let wrapped = value;
|
|
350
|
+
if (typeof value === "function" && (next.join(".") === "chat.completions.create" || next.join(".") === "responses.create"))
|
|
351
|
+
wrapped = (...args) => instrument(
|
|
352
|
+
resource,
|
|
353
|
+
value,
|
|
354
|
+
args,
|
|
355
|
+
path[0] === "responses" ? "responses" : "chat.completions"
|
|
356
|
+
);
|
|
357
|
+
else if (typeof value === "object" && value !== null && ["chat", "chat.completions", "responses"].includes(next.join(".")))
|
|
358
|
+
wrapped = proxy(value, next);
|
|
359
|
+
else if (typeof value === "function" && key !== "constructor")
|
|
360
|
+
wrapped = value.bind(resource);
|
|
361
|
+
if (wrapped !== value) cache.set(key, { value, wrapped });
|
|
362
|
+
return wrapped;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
return {
|
|
367
|
+
client: proxy(client, []),
|
|
368
|
+
flush: () => engine.flush(),
|
|
369
|
+
shutdown() {
|
|
370
|
+
shutdownPromise ??= (async () => {
|
|
371
|
+
closed = true;
|
|
372
|
+
for (const state of [...active])
|
|
373
|
+
safe(() => state.finish("observer_shutdown"));
|
|
374
|
+
if (options.shutdownEngine) await engine.shutdown();
|
|
375
|
+
else await engine.flush();
|
|
376
|
+
})();
|
|
377
|
+
return shutdownPromise;
|
|
378
|
+
},
|
|
379
|
+
getDiagnostics: () => ({
|
|
380
|
+
...engine.getDiagnostics(),
|
|
381
|
+
trackedRequests: active.size,
|
|
382
|
+
requestsSkipped: skipped
|
|
383
|
+
})
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/openai.ts
|
|
388
|
+
function observeOpenAI(client, options = {}) {
|
|
389
|
+
const external = options.engine;
|
|
390
|
+
validateOpenAIAttachOptions(options);
|
|
391
|
+
if (!external && options.shutdownEngine === false)
|
|
392
|
+
throw new Error(
|
|
393
|
+
"shutdownEngine: false requires an external observability engine"
|
|
394
|
+
);
|
|
395
|
+
return attachOpenAI(
|
|
396
|
+
client,
|
|
397
|
+
external ?? createObservability(options),
|
|
398
|
+
{
|
|
399
|
+
...options,
|
|
400
|
+
shutdownEngine: options.shutdownEngine ?? !external
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
export {
|
|
405
|
+
attachOpenAI,
|
|
406
|
+
observeOpenAI
|
|
407
|
+
};
|
|
408
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/openai.ts","../src/openai.ts"],"sourcesContent":["import { SpanKind, SpanStatusCode, type Span } from \"@opentelemetry/api\";\nimport type OpenAI from \"openai\";\nimport type { Stream } from \"openai/core/streaming\";\nimport type { Observability } from \"../engine.js\";\n\nexport interface OpenAIAttachOptions {\n /** Shared engines are externally owned by default. */\n shutdownEngine?: boolean;\n /** Maximum requests retained until consumption or observer shutdown. */\n maxTrackedRequests?: number;\n /** Text capture limit per input/output, when the engine allows content. */\n maxContentChars?: number;\n}\n\nexport interface OpenAIObserver<T extends OpenAI = OpenAI> {\n readonly client: T;\n flush(): Promise<void>;\n shutdown(): Promise<void>;\n getDiagnostics(): ReturnType<Observability[\"getDiagnostics\"]> & {\n trackedRequests: number;\n requestsSkipped: number;\n };\n}\n\ntype RecordValue = Record<string, unknown>;\nconst record = (value: unknown): RecordValue =>\n typeof value === \"object\" && value !== null ? (value as RecordValue) : {};\n\nfunction limit(value: number, name: string, maximum: number): number {\n if (!Number.isInteger(value) || value < 1 || value > maximum)\n throw new Error(`${name} must be an integer between 1 and ${maximum}`);\n return value;\n}\n\nexport function validateOpenAIAttachOptions(\n options: OpenAIAttachOptions,\n): void {\n limit(options.maxTrackedRequests ?? 1024, \"maxTrackedRequests\", 65536);\n limit(options.maxContentChars ?? 4096, \"maxContentChars\", 4096);\n}\n\n// Only text is captured: no tools, images, audio, arbitrary object serialization,\n// or unbounded traversal of user-controlled input/response structures.\nfunction text(value: unknown, maximum: number, depth = 0): string {\n if (typeof value === \"string\") return value.slice(0, maximum);\n if (!Array.isArray(value) || depth >= 3) return \"\";\n let result = \"\";\n for (const item of value.slice(0, 32)) {\n if (result.length >= maximum) break;\n const entry = record(item);\n const part =\n typeof entry.text === \"string\"\n ? entry.text\n : typeof entry.content === \"string\"\n ? entry.content\n : Array.isArray(entry.content)\n ? text(entry.content, maximum - result.length, depth + 1)\n : \"\";\n result += part.slice(0, maximum - result.length);\n }\n return result;\n}\n\nfunction tokens(span: Span, usage: unknown): void {\n const data = record(usage);\n const input = record(data.prompt_tokens_details ?? data.input_tokens_details);\n const output = record(\n data.completion_tokens_details ?? data.output_tokens_details,\n );\n for (const [attribute, value] of [\n [\"gen_ai.usage.input_tokens\", data.prompt_tokens ?? data.input_tokens],\n [\n \"gen_ai.usage.output_tokens\",\n data.completion_tokens ?? data.output_tokens,\n ],\n [\"gen_ai.usage.cache_read.input_tokens\", input.cached_tokens],\n [\"gen_ai.usage.cache_creation.input_tokens\", input.cache_write_tokens],\n [\"gen_ai.usage.reasoning.output_tokens\", output.reasoning_tokens],\n ] as const) {\n if (typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0)\n span.setAttribute(attribute, value);\n }\n}\n\ninterface RequestState {\n span: Span;\n ended: boolean;\n output: string;\n sawTerminal: boolean;\n expectedChoices: number | undefined;\n completedChoices: Set<number>;\n cleanup: (() => void)[];\n finish(reason?: string): void;\n}\n\n/**\n * Observe only direct Chat Completions/Responses create calls. The original SDK\n * client, resources, promises, streams and application OTel globals are untouched.\n */\nexport function attachOpenAI<T extends OpenAI>(\n client: T,\n engine: Observability,\n options: OpenAIAttachOptions = {},\n): OpenAIObserver<T> {\n const maximum = Math.min(\n engine.maxActiveSpans,\n limit(options.maxTrackedRequests ?? 1024, \"maxTrackedRequests\", 65536),\n );\n const contentLimit = limit(\n options.maxContentChars ?? 4096,\n \"maxContentChars\",\n 4096,\n );\n const active = new Set<RequestState>();\n let closed = false;\n let skipped = 0;\n let shutdownPromise: Promise<void> | undefined;\n const safe = (callback: () => void): void => {\n try {\n callback();\n } catch {\n // Instrumentation must never replace an SDK result or exception.\n }\n };\n const fail = (state: RequestState, error: unknown): void => {\n let reason = \"request_error\";\n safe(() => {\n const data = record(error);\n const status = data.status;\n if (\n typeof status === \"number\" &&\n Number.isInteger(status) &&\n status >= 400 &&\n status <= 599\n )\n state.span.setAttribute(\"http.response.status_code\", status);\n if (data.name === \"AbortError\" || data.name === \"APIUserAbortError\")\n reason = \"aborted\";\n });\n safe(() => state.finish(reason));\n };\n\n function inspect(state: RequestState, value: unknown, api: string): void {\n if (state.ended) return;\n const data = record(value);\n const response =\n api === \"responses\" && data.response ? record(data.response) : data;\n if (typeof response.model === \"string\")\n state.span.setAttribute(\n \"gen_ai.response.model\",\n response.model.slice(0, 256),\n );\n if (typeof response.id === \"string\")\n state.span.setAttribute(\"gen_ai.response.id\", response.id.slice(0, 256));\n tokens(state.span, response.usage);\n if (api === \"responses\") {\n if (data.type === \"response.completed\") state.sawTerminal = true;\n if (\n data.type === \"response.failed\" ||\n data.type === \"error\" ||\n response.status === \"failed\" ||\n response.error\n ) {\n state.finish(\"response_failed\");\n return;\n }\n if (\n data.type === \"response.incomplete\" ||\n response.status === \"incomplete\"\n ) {\n state.finish(\"response_incomplete\");\n return;\n }\n if (engine.captureContent) {\n const piece =\n data.type === \"response.output_text.delta\"\n ? text(data.delta, contentLimit)\n : state.output.length === 0\n ? text(response.output, contentLimit)\n : \"\";\n if (piece)\n state.output += piece.slice(0, contentLimit - state.output.length);\n }\n } else if (Array.isArray(data.choices)) {\n const reasons: string[] = [];\n for (const choice of data.choices.slice(0, 32)) {\n const item = record(choice);\n if (\n typeof item.finish_reason === \"string\" &&\n [\n \"stop\",\n \"length\",\n \"tool_calls\",\n \"content_filter\",\n \"function_call\",\n ].includes(item.finish_reason)\n ) {\n reasons.push(item.finish_reason);\n if (\n state.expectedChoices !== undefined &&\n typeof item.index === \"number\" &&\n Number.isInteger(item.index) &&\n item.index >= 0 &&\n item.index < state.expectedChoices\n ) {\n state.completedChoices.add(item.index);\n state.sawTerminal =\n state.completedChoices.size === state.expectedChoices;\n }\n }\n }\n if (reasons.length)\n state.span.setAttribute(\"gen_ai.response.finish_reasons\", reasons);\n if (engine.captureContent) {\n const first = record(\n data.choices\n .slice(0, 32)\n .find((choice) => record(choice).index === 0),\n );\n const piece = text(\n record(first.delta ?? first.message).content,\n contentLimit - state.output.length,\n );\n state.output += piece;\n }\n }\n }\n\n function wrapStream(\n state: RequestState,\n stream: Stream<unknown>,\n api: string,\n ): Stream<unknown> {\n let reading = false;\n const onAbort = () => {\n if (!reading) safe(() => state.finish(\"aborted\"));\n };\n stream.controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n state.cleanup.push(() =>\n stream.controller.signal.removeEventListener(\"abort\", onAbort),\n );\n if (stream.controller.signal.aborted) onAbort();\n // The public constructor preserves SDK tee/toReadableStream and private-brand\n // methods without monkeypatching the source stream or eagerly consuming it.\n const Constructor = stream.constructor as new (\n iterator: () => AsyncIterator<unknown>,\n controller: AbortController,\n ) => Stream<unknown>;\n const wrapped = new Constructor(async function* () {\n let exhausted = false;\n try {\n const source = stream[Symbol.asyncIterator]();\n const iterator: AsyncIterator<unknown> = {\n async next() {\n reading = true;\n try {\n return await source.next();\n } finally {\n reading = false;\n }\n },\n ...(source.return\n ? {\n async return(value?: unknown) {\n reading = true;\n try {\n return await source.return!(value);\n } finally {\n reading = false;\n }\n },\n }\n : {}),\n };\n for await (const item of {\n [Symbol.asyncIterator]: () => iterator,\n }) {\n safe(() => inspect(state, item, api));\n yield item;\n }\n exhausted = true;\n safe(() =>\n state.finish(\n stream.controller.signal.aborted\n ? \"aborted\"\n : state.sawTerminal\n ? undefined\n : \"stream_incomplete\",\n ),\n );\n } catch (error) {\n fail(state, error);\n throw error;\n } finally {\n if (!exhausted) safe(() => state.finish(\"cancelled\"));\n }\n }, stream.controller);\n safe(() => {\n const requestId = Object.getOwnPropertyDescriptor(stream, \"_request_id\");\n if (requestId) Object.defineProperty(wrapped, \"_request_id\", requestId);\n });\n return wrapped;\n }\n\n function instrument(\n target: object,\n method: (...args: unknown[]) => unknown,\n args: unknown[],\n api: string,\n ): unknown {\n if (closed || active.size >= maximum) {\n if (!closed) skipped++;\n return Reflect.apply(method, target, args);\n }\n let state: RequestState | undefined;\n safe(() => {\n const request = record(args[0]);\n const span = engine.startSpan(\"chat\", {\n kind: SpanKind.CLIENT,\n attributes: {\n \"gen_ai.operation.name\": \"chat\",\n \"gen_ai.provider.name\": \"openai\",\n \"gen_ai.system\": \"openai\",\n \"openai.api\": api,\n ...(typeof request.model === \"string\"\n ? { \"gen_ai.request.model\": request.model.slice(0, 256) }\n : {}),\n },\n });\n state = {\n span,\n ended: false,\n output: \"\",\n sawTerminal: false,\n expectedChoices:\n request.n === undefined || request.n === null\n ? 1\n : typeof request.n === \"number\" &&\n Number.isInteger(request.n) &&\n request.n >= 1 &&\n request.n <= 32\n ? request.n\n : undefined,\n completedChoices: new Set(),\n cleanup: [],\n finish(reason) {\n if (this.ended) return;\n this.ended = true;\n active.delete(this);\n for (const cleanup of this.cleanup) safe(cleanup);\n this.cleanup.length = 0;\n this.completedChoices.clear();\n safe(() => {\n if (reason) {\n span.setAttribute(\"openai.lifecycle\", reason);\n if (\n [\n \"observer_shutdown\",\n \"stream_incomplete\",\n \"response_incomplete\",\n \"cancelled\",\n \"aborted\",\n \"unsupported_result\",\n ].includes(reason)\n )\n span.setAttribute(\"observability.incomplete\", true);\n span.setAttribute(\"error.type\", `openai.${reason}`);\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n if (engine.captureContent && this.output)\n span.setAttribute(\"gen_ai.output.messages\", this.output);\n });\n this.output = \"\";\n safe(() => span.end());\n },\n };\n active.add(state);\n if (engine.captureContent) {\n const input = text(\n api === \"responses\" ? request.input : request.messages,\n contentLimit,\n );\n if (input) span.setAttribute(\"gen_ai.input.messages\", input);\n }\n });\n const current = state;\n let result: unknown;\n try {\n result = Reflect.apply(method, target, args);\n } catch (error) {\n if (current) fail(current, error);\n throw error;\n }\n if (!current) return result;\n if (typeof result !== \"object\" || result === null) {\n safe(() => current.finish(\"unsupported_result\"));\n return result;\n }\n let parsed: Promise<unknown> | undefined;\n const streams = new WeakMap<object, Stream<unknown>>();\n const observeData = (data: unknown): unknown => {\n let observed = data;\n safe(() => {\n const candidate = record(data);\n if (\n candidate.controller instanceof AbortController &&\n typeof (data as AsyncIterable<unknown>)[Symbol.asyncIterator] ===\n \"function\"\n ) {\n const existing = streams.get(data as object);\n if (existing) {\n observed = existing;\n return;\n }\n if (current.ended) return;\n const wrapped = wrapStream(current, data as Stream<unknown>, api);\n streams.set(data as object, wrapped);\n observed = wrapped;\n return;\n }\n if (current.ended) return;\n safe(() => inspect(current, data, api));\n const status = record(data).status;\n current.finish(\n status === \"queued\" || status === \"in_progress\"\n ? \"response_incomplete\"\n : undefined,\n );\n });\n return observed;\n };\n const reject = (error: unknown): never => {\n fail(current, error);\n throw error;\n };\n // Do not attach an eager .then: that parses the SDK body and breaks asResponse.\n const parse = () =>\n (parsed ??= Promise.resolve(result).then(observeData, reject));\n return new Proxy(result, {\n get(promise, key) {\n if (key === \"then\")\n return (...callbacks: Parameters<Promise<unknown>[\"then\"]>) =>\n parse().then(...callbacks);\n if (key === \"catch\")\n return (...callbacks: Parameters<Promise<unknown>[\"catch\"]>) =>\n parse().catch(...callbacks);\n if (key === \"finally\")\n return (...callbacks: Parameters<Promise<unknown>[\"finally\"]>) =>\n parse().finally(...callbacks);\n const value: unknown = Reflect.get(promise, key, promise);\n if (key === \"withResponse\" && typeof value === \"function\")\n return (...parameters: unknown[]) =>\n Promise.resolve(Reflect.apply(value, promise, parameters)).then(\n (response) => {\n const envelope = record(response);\n return { ...envelope, data: observeData(envelope.data) };\n },\n reject,\n );\n if (key === \"asResponse\" && typeof value === \"function\")\n return (...parameters: unknown[]) =>\n Promise.resolve(Reflect.apply(value, promise, parameters)).then(\n (response) => {\n safe(() => {\n current.span.setAttribute(\"openai.raw_response\", true);\n current.finish();\n });\n return response;\n },\n reject,\n );\n return typeof value === \"function\" && key !== \"constructor\"\n ? value.bind(promise)\n : value;\n },\n });\n }\n\n const proxy = (target: object, path: string[]): object => {\n const cache = new Map<PropertyKey, { value: unknown; wrapped: unknown }>();\n return new Proxy(target, {\n get(resource, key) {\n const value: unknown = Reflect.get(resource, key, resource);\n const cached = cache.get(key);\n if (cached && cached.value === value) return cached.wrapped;\n const next = [...path, String(key)];\n let wrapped = value;\n if (\n typeof value === \"function\" &&\n (next.join(\".\") === \"chat.completions.create\" ||\n next.join(\".\") === \"responses.create\")\n )\n wrapped = (...args: unknown[]) =>\n instrument(\n resource,\n value as (...args: unknown[]) => unknown,\n args,\n path[0] === \"responses\" ? \"responses\" : \"chat.completions\",\n );\n else if (\n typeof value === \"object\" &&\n value !== null &&\n [\"chat\", \"chat.completions\", \"responses\"].includes(next.join(\".\"))\n )\n wrapped = proxy(value, next);\n else if (typeof value === \"function\" && key !== \"constructor\")\n wrapped = value.bind(resource);\n if (wrapped !== value) cache.set(key, { value, wrapped });\n return wrapped;\n },\n });\n };\n\n return {\n client: proxy(client, []) as T,\n flush: () => engine.flush(),\n shutdown() {\n shutdownPromise ??= (async () => {\n closed = true;\n for (const state of [...active])\n safe(() => state.finish(\"observer_shutdown\"));\n if (options.shutdownEngine) await engine.shutdown();\n else await engine.flush();\n })();\n return shutdownPromise;\n },\n getDiagnostics: () => ({\n ...engine.getDiagnostics(),\n trackedRequests: active.size,\n requestsSkipped: skipped,\n }),\n };\n}\n","import type OpenAI from \"openai\";\nimport {\n attachOpenAI,\n validateOpenAIAttachOptions,\n type OpenAIAttachOptions,\n type OpenAIObserver,\n} from \"./adapters/openai.js\";\nimport {\n createObservability,\n type EventhouseObservabilityOptions,\n type Observability,\n} from \"./index.js\";\n\nexport { attachOpenAI } from \"./adapters/openai.js\";\nexport type { OpenAIAttachOptions, OpenAIObserver } from \"./adapters/openai.js\";\n\nexport type OpenAIObservabilityOptions = OpenAIAttachOptions &\n (\n | (EventhouseObservabilityOptions & { engine?: never })\n | { engine: Observability }\n );\n\n/** Newly constructed engines are owned; supplied engines are external by default. */\nexport function observeOpenAI<T extends OpenAI>(\n client: T,\n options: OpenAIObservabilityOptions = {},\n): OpenAIObserver<T> {\n const external = options.engine;\n validateOpenAIAttachOptions(options);\n if (!external && options.shutdownEngine === false)\n throw new Error(\n \"shutdownEngine: false requires an external observability engine\",\n );\n return attachOpenAI(\n client,\n external ?? createObservability(options as EventhouseObservabilityOptions),\n {\n ...options,\n shutdownEngine: options.shutdownEngine ?? !external,\n },\n );\n}\n"],"mappings":"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","names":[]}
|