@axon-ai/openai-tracer 1.0.3

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/dist/index.js ADDED
@@ -0,0 +1,425 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ OpenAITracer: () => OpenAITracer,
34
+ TracedOpenAI: () => TracedOpenAI,
35
+ createOpenAITracer: () => createOpenAITracer,
36
+ default: () => OpenAITracer
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/OpenAITracer.ts
41
+ var import_events = require("events");
42
+ var import_uuid = require("uuid");
43
+ var OpenAITracer = class extends import_events.EventEmitter {
44
+ /**
45
+ * Creates a new OpenAI tracer instance
46
+ *
47
+ * @param config - Configuration object for the tracer
48
+ * @param config.projectName - Project name for organizing traces (default: 'openai-agent')
49
+ * @param config.endpoint - Backend server endpoint (default: 'http://localhost:3000')
50
+ * @param config.metadata - Additional metadata to include with all events
51
+ * @param config.autoConnect - Whether to automatically connect to the server (default: true)
52
+ */
53
+ constructor(config = {}) {
54
+ super();
55
+ this.eventQueue = [];
56
+ this.isConnected = false;
57
+ this.client = null;
58
+ this.flushInterval = null;
59
+ this.traceId = (0, import_uuid.v4)();
60
+ this.projectName = config.projectName || "openai-agent";
61
+ this.endpoint = config.endpoint || "http://localhost:3000";
62
+ this.metadata = config.metadata || {};
63
+ if (config.autoConnect !== false) {
64
+ this.connect();
65
+ }
66
+ }
67
+ /**
68
+ * Establishes WebSocket connection to the trace server
69
+ *
70
+ * This method creates a Socket.IO client connection to the backend server,
71
+ * sets up event listeners for connection status, and starts the periodic
72
+ * event flushing mechanism.
73
+ *
74
+ * @throws Error if connection fails
75
+ */
76
+ async connect() {
77
+ try {
78
+ const { io } = await import("socket.io-client");
79
+ this.client = io(this.endpoint, {
80
+ transports: ["websocket", "polling"],
81
+ timeout: 5e3
82
+ });
83
+ this.client.on("connect", () => {
84
+ this.isConnected = true;
85
+ this.emit("connected");
86
+ console.log(`[OpenAI Tracer] Connected to ${this.endpoint}`);
87
+ });
88
+ this.client.on("disconnect", () => {
89
+ this.isConnected = false;
90
+ this.emit("disconnected");
91
+ console.log("[OpenAI Tracer] Disconnected from server");
92
+ });
93
+ this.client.on("connect_error", (error) => {
94
+ console.error("[OpenAI Tracer] Connection error:", error);
95
+ this.emit("error", error);
96
+ });
97
+ this.flushInterval = setInterval(() => {
98
+ this.flushQueue();
99
+ }, 1e3);
100
+ } catch (error) {
101
+ console.error("[OpenAI Tracer] Connection failed:", error);
102
+ this.emit("error", error);
103
+ }
104
+ }
105
+ /**
106
+ * Disconnect from the trace server
107
+ */
108
+ disconnect() {
109
+ this.isConnected = false;
110
+ if (this.flushInterval) {
111
+ clearInterval(this.flushInterval);
112
+ this.flushInterval = null;
113
+ }
114
+ if (this.client) {
115
+ this.client.disconnect();
116
+ this.client = null;
117
+ }
118
+ this.emit("disconnected");
119
+ }
120
+ /**
121
+ * Records the start of a function call execution
122
+ *
123
+ * This method creates and queues a function call start event, capturing
124
+ * the function name, arguments, model, messages, and available tools.
125
+ * It returns an event ID that can be used to correlate with the end event.
126
+ *
127
+ * @param functionName - Name of the function being called
128
+ * @param functionArguments - Arguments passed to the function
129
+ * @param model - OpenAI model being used (e.g., 'gpt-4', 'gpt-3.5-turbo')
130
+ * @param messages - Array of conversation messages
131
+ * @param tools - Optional array of available tools
132
+ * @returns Event ID for correlating with the corresponding end event
133
+ */
134
+ traceFunctionCallStart(functionName, functionArguments, model, messages, tools) {
135
+ const eventId = (0, import_uuid.v4)();
136
+ const event = {
137
+ eventId,
138
+ traceId: this.traceId,
139
+ timestamp: Date.now(),
140
+ type: "function_call_start",
141
+ data: {
142
+ functionName,
143
+ arguments: JSON.stringify(functionArguments),
144
+ model,
145
+ messageCount: messages.length,
146
+ availableTools: tools?.map((t) => t.function.name) || [],
147
+ conversationContext: this.extractConversationContext(messages)
148
+ },
149
+ metadata: {
150
+ ...this.metadata,
151
+ projectName: this.projectName
152
+ }
153
+ };
154
+ this.addEvent(event);
155
+ return eventId;
156
+ }
157
+ /**
158
+ * Track function call end
159
+ */
160
+ traceFunctionCallEnd(eventId, result, cost, latency, tokens) {
161
+ const event = {
162
+ eventId: (0, import_uuid.v4)(),
163
+ traceId: this.traceId,
164
+ timestamp: Date.now(),
165
+ type: "function_call_end",
166
+ data: {
167
+ originalEventId: eventId,
168
+ result: typeof result === "string" ? result : JSON.stringify(result),
169
+ cost,
170
+ latency,
171
+ tokens: tokens || { prompt: 0, completion: 0, total: 0 },
172
+ success: true
173
+ },
174
+ metadata: {
175
+ ...this.metadata,
176
+ projectName: this.projectName
177
+ }
178
+ };
179
+ this.addEvent(event);
180
+ }
181
+ /**
182
+ * Track tool selection
183
+ */
184
+ traceToolSelection(availableTools, selectedTool, reasoning, confidence) {
185
+ const event = {
186
+ eventId: (0, import_uuid.v4)(),
187
+ traceId: this.traceId,
188
+ timestamp: Date.now(),
189
+ type: "tool_selection",
190
+ data: {
191
+ availableTools: availableTools.map((t) => ({
192
+ name: t.function.name,
193
+ description: t.function.description
194
+ })),
195
+ selectedTool: {
196
+ name: selectedTool.function.name,
197
+ description: selectedTool.function.description
198
+ },
199
+ reasoning,
200
+ confidence,
201
+ selectionTime: Date.now()
202
+ },
203
+ metadata: {
204
+ ...this.metadata,
205
+ projectName: this.projectName
206
+ }
207
+ };
208
+ this.addEvent(event);
209
+ }
210
+ /**
211
+ * Track conversation turn
212
+ */
213
+ traceConversationTurn(userMessage, assistantResponse, model, tokens, cost) {
214
+ const event = {
215
+ eventId: (0, import_uuid.v4)(),
216
+ traceId: this.traceId,
217
+ timestamp: Date.now(),
218
+ type: "conversation_turn",
219
+ data: {
220
+ userMessage,
221
+ assistantResponse,
222
+ model,
223
+ tokens: tokens || { prompt: 0, completion: 0, total: 0 },
224
+ cost: cost || 0,
225
+ turnNumber: this.getTurnNumber()
226
+ },
227
+ metadata: {
228
+ ...this.metadata,
229
+ projectName: this.projectName
230
+ }
231
+ };
232
+ this.addEvent(event);
233
+ }
234
+ /**
235
+ * Track error
236
+ */
237
+ traceError(error, context, functionName, functionArguments) {
238
+ const event = {
239
+ eventId: (0, import_uuid.v4)(),
240
+ traceId: this.traceId,
241
+ timestamp: Date.now(),
242
+ type: "error",
243
+ data: {
244
+ error: {
245
+ message: error.message,
246
+ stack: error.stack,
247
+ name: error.name
248
+ },
249
+ context,
250
+ functionName,
251
+ arguments: functionArguments ? JSON.stringify(functionArguments) : void 0
252
+ },
253
+ metadata: {
254
+ ...this.metadata,
255
+ projectName: this.projectName
256
+ }
257
+ };
258
+ this.addEvent(event);
259
+ }
260
+ /**
261
+ * Add event to queue
262
+ */
263
+ addEvent(event) {
264
+ this.eventQueue.push(event);
265
+ this.emit("event", event);
266
+ }
267
+ /**
268
+ * Flush events to server
269
+ */
270
+ async flushQueue() {
271
+ if (this.eventQueue.length === 0 || !this.isConnected || !this.client) {
272
+ return;
273
+ }
274
+ const events = [...this.eventQueue];
275
+ this.eventQueue = [];
276
+ try {
277
+ console.log(`[OpenAI Tracer] Flushing ${events.length} events`);
278
+ this.client.emit("openai_events", {
279
+ traceId: this.traceId,
280
+ projectName: this.projectName,
281
+ events,
282
+ metadata: this.metadata
283
+ });
284
+ this.emit("events_sent", events);
285
+ } catch (error) {
286
+ console.error("[OpenAI Tracer] Failed to flush events:", error);
287
+ this.eventQueue.unshift(...events);
288
+ }
289
+ }
290
+ /**
291
+ * Extract conversation context from messages
292
+ */
293
+ extractConversationContext(messages) {
294
+ return {
295
+ messageCount: messages.length,
296
+ lastUserMessage: messages.filter((m) => m.role === "user").pop()?.content,
297
+ hasSystemMessage: messages.some((m) => m.role === "system"),
298
+ hasToolCalls: messages.some((m) => m.tool_calls && m.tool_calls.length > 0)
299
+ };
300
+ }
301
+ /**
302
+ * Get current turn number
303
+ */
304
+ getTurnNumber() {
305
+ return this.eventQueue.filter((e) => e.type === "conversation_turn").length + 1;
306
+ }
307
+ /**
308
+ * Calculate cost based on tokens and model
309
+ */
310
+ calculateCost(tokens, model) {
311
+ const pricing = {
312
+ "gpt-4": { prompt: 0.03, completion: 0.06 },
313
+ "gpt-4-turbo": { prompt: 0.01, completion: 0.03 },
314
+ "gpt-3.5-turbo": { prompt: 1e-3, completion: 2e-3 },
315
+ "gpt-3.5-turbo-16k": { prompt: 3e-3, completion: 4e-3 }
316
+ };
317
+ const prices = pricing[model] || pricing["gpt-3.5-turbo"];
318
+ return (tokens.prompt * prices.prompt + tokens.completion * prices.completion) / 1e3;
319
+ }
320
+ /**
321
+ * Get trace ID
322
+ */
323
+ getTraceId() {
324
+ return this.traceId;
325
+ }
326
+ /**
327
+ * Check if connected
328
+ */
329
+ isConnectedToServer() {
330
+ return this.isConnected;
331
+ }
332
+ /**
333
+ * Get current event queue size
334
+ */
335
+ getQueueSize() {
336
+ return this.eventQueue.length;
337
+ }
338
+ /**
339
+ * Clear event queue
340
+ */
341
+ clearQueue() {
342
+ this.eventQueue = [];
343
+ }
344
+ /**
345
+ * Shutdown tracer
346
+ */
347
+ async shutdown() {
348
+ await this.flushQueue();
349
+ this.disconnect();
350
+ }
351
+ };
352
+ function createOpenAITracer(config) {
353
+ return new OpenAITracer(config);
354
+ }
355
+ var TracedOpenAI = class {
356
+ constructor(openaiClient, tracer) {
357
+ this.openai = openaiClient;
358
+ this.tracer = tracer;
359
+ }
360
+ /**
361
+ * Traced chat completion with function calling
362
+ */
363
+ async createChatCompletion(params) {
364
+ const startTime = Date.now();
365
+ const eventId = this.tracer.traceFunctionCallStart(
366
+ "createChatCompletion",
367
+ params,
368
+ params.model,
369
+ params.messages,
370
+ params.tools
371
+ );
372
+ try {
373
+ const response = await this.openai.chat.completions.create(params);
374
+ const endTime = Date.now();
375
+ const latency = endTime - startTime;
376
+ const tokens = response.usage ? {
377
+ prompt: response.usage.prompt_tokens,
378
+ completion: response.usage.completion_tokens,
379
+ total: response.usage.total_tokens
380
+ } : { prompt: 0, completion: 0, total: 0 };
381
+ const cost = this.tracer.calculateCost(tokens, params.model);
382
+ this.tracer.traceFunctionCallEnd(eventId, response, cost, latency, tokens);
383
+ if (response.choices[0]?.message?.tool_calls) {
384
+ const selectedTool = params.tools?.find(
385
+ (t) => t.function.name === response.choices[0].message.tool_calls[0].function.name
386
+ );
387
+ if (selectedTool) {
388
+ this.tracer.traceToolSelection(
389
+ params.tools || [],
390
+ selectedTool,
391
+ "Model selected tool based on user request",
392
+ 0.9
393
+ // High confidence for model selection
394
+ );
395
+ }
396
+ }
397
+ const userMessage = params.messages.filter((m) => m.role === "user").pop()?.content || "";
398
+ const assistantResponse = response.choices[0]?.message?.content || "";
399
+ if (userMessage && assistantResponse) {
400
+ this.tracer.traceConversationTurn(
401
+ userMessage,
402
+ assistantResponse,
403
+ params.model,
404
+ tokens,
405
+ cost
406
+ );
407
+ }
408
+ return response;
409
+ } catch (error) {
410
+ this.tracer.traceError(
411
+ error,
412
+ "createChatCompletion",
413
+ "createChatCompletion",
414
+ params
415
+ );
416
+ throw error;
417
+ }
418
+ }
419
+ };
420
+ // Annotate the CommonJS export names for ESM import in node:
421
+ 0 && (module.exports = {
422
+ OpenAITracer,
423
+ TracedOpenAI,
424
+ createOpenAITracer
425
+ });