UNPKG

@maximai/maxim-js

Version:

Maxim AI JS SDK. Visit https://getmaxim.ai for more info.

370 lines 17.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MaximOpenAIAgentsProcessor = void 0; const uuid_1 = require("uuid"); const utils_1 = require("./utils"); /** * A tracing processor for the OpenAI Agents SDK that forwards trace and span * lifecycle events into Maxim. * * @example * ```ts * import { addTraceProcessor, setTraceProcessors } from "@openai/agents"; * import { Maxim } from "@maximai/maxim-js"; * import { MaximOpenAIAgentsProcessor } from "@maximai/maxim-js/openai-agents"; * * const maxim = new Maxim({ apiKey: process.env.MAXIM_API_KEY! }); * const logger = await maxim.logger({ id: "my-app" }); * * // Add alongside the default OpenAI exporter * addTraceProcessor(new MaximOpenAIAgentsProcessor(logger)); * * // Or replace all processors (disables default OpenAI exporter) * // setTraceProcessors([new MaximOpenAIAgentsProcessor(logger)]); * ``` * * Trace metadata recognized on the OpenAI Agents trace * (only `traceTags`, `traceMetadata`, and `traceMetrics` are read at both onTraceStart and onTraceEnd, the rest is only read at onTraceStart): * - `traceId`: string — override the Maxim trace id (default: Agent's `traceId`) * - `traceName`: string — set the Maxim trace name * - `traceSessionId`: string — associate the trace to a session * - `traceTags`: Record<string,string> — tags to add on the trace * - `traceMetadata`: Record<string,unknown> — metadata to add on the trace * - `traceMetrics`: Record<string,number> — numeric metrics to add on the trace * - `traceSpanId`: string — id for the single top-level span * - `traceSpanName`: string — name for the top-level span * - `traceSpanTags`: Record<string,string> — tags for the top-level span * * Unsupported span types: * - "speech" | "transcription" | "speech_group" (currently ignored) * * Notes: * - No automatic flush is performed. Manage shutdown/flush in your app * lifecycle (e.g., `await maxim.cleanup()`). */ class MaximOpenAIAgentsProcessor { constructor(logger) { this.traceStates = new Map(); this.spanStates = new Map(); this.generationStates = new Map(); this.toolCallStates = new Map(); this.logger = logger; } async shutdown() { await this.logger.cleanup(); } async forceFlush() { await this.logger.flush(); } async onTraceStart(trace) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p; const traceId = (_b = (_a = trace.metadata) === null || _a === void 0 ? void 0 : _a["traceId"]) !== null && _b !== void 0 ? _b : trace.traceId; const traceName = (_e = (_d = (_c = trace.metadata) === null || _c === void 0 ? void 0 : _c["traceName"]) !== null && _d !== void 0 ? _d : ("name" in trace ? trace.name : undefined)) !== null && _e !== void 0 ? _e : "agents-trace"; const maximTrace = this.logger.trace({ id: traceId, name: traceName, tags: (_f = trace.metadata) === null || _f === void 0 ? void 0 : _f["traceTags"], sessionId: (_g = trace.metadata) === null || _g === void 0 ? void 0 : _g["traceSessionId"], }); if ((_h = trace.metadata) === null || _h === void 0 ? void 0 : _h["traceMetadata"]) { this.logger.traceMetadata(traceId, trace.metadata["traceMetadata"]); } if ((_j = trace.metadata) === null || _j === void 0 ? void 0 : _j["traceMetrics"]) { for (const [k, v] of Object.entries(trace.metadata["traceMetrics"])) { if (typeof v === "number") { this.logger.traceAddMetric(traceId, k, v); } else { console.warn(`Skipping trace metric ${k} with value ${v} because it is not a number`); } } } // Create a single top-level span per trace const spanId = (_l = (_k = trace.metadata) === null || _k === void 0 ? void 0 : _k["traceSpanId"]) !== null && _l !== void 0 ? _l : (0, uuid_1.v4)(); const spanName = (_o = (_m = trace.metadata) === null || _m === void 0 ? void 0 : _m["traceSpanName"]) !== null && _o !== void 0 ? _o : "agent-run"; const topSpan = maximTrace.span({ id: spanId, name: spanName, tags: (_p = trace.metadata) === null || _p === void 0 ? void 0 : _p["traceSpanTags"] }); // store state this.traceStates.set(trace.traceId, { trace: maximTrace, topSpan, createdByUs: true }); } async onTraceEnd(trace) { var _a, _b, _c, _d; const state = this.traceStates.get(trace.traceId); if (!state) return; // Use the Maxim trace id from traceState if provided, otherwise fall back to the Agents traceId const maximId = (_a = state.trace.id) !== null && _a !== void 0 ? _a : trace.traceId; if ((_b = trace.metadata) === null || _b === void 0 ? void 0 : _b["traceTags"]) { for (const [k, v] of Object.entries(trace.metadata["traceTags"])) { if (typeof v === "string") { this.logger.traceTag(maximId, k, v); } else { console.warn(`Skipping trace tag ${k} with value ${v} because it is not a string`); } } } if ((_c = trace.metadata) === null || _c === void 0 ? void 0 : _c["traceMetadata"]) { this.logger.traceMetadata(maximId, trace.metadata["traceMetadata"]); } if ((_d = trace.metadata) === null || _d === void 0 ? void 0 : _d["traceMetrics"]) { for (const [k, v] of Object.entries(trace.metadata["traceMetrics"])) { if (typeof v === "number") { this.logger.traceAddMetric(maximId, k, v); } else { console.warn(`Skipping trace metric ${k} with value ${v} because it is not a number`); } } } // End the top-level span if still open this.logger.spanEnd(state.topSpan.id); // End trace this.logger.traceEnd(state.trace.id); // cleanup state this.traceStates.delete(trace.traceId); } async onSpanStart(span) { var _a, _b, _c, _d, _e, _f, _g, _h; const traceState = this.traceStates.get(span.traceId); if (!traceState) { console.warn(`onSpanStart called for span ${span.spanId} but no trace state found`); return; } const data = span.spanData; const topSpanId = traceState.topSpan.id; if (span.error) { this.logger.spanError(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, { id: (0, uuid_1.v4)(), message: span.error.message, metadata: span.error.data, }); } if (data.type === "agent") { const child = this.logger.spanSpan(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, { id: span.spanId, name: data.name, }); if (data.handoffs) { child.addMetadata({ handoffs: data.handoffs }); } if (data.output_type) { child.addMetadata({ output_type: data.output_type }); } if (data.tools) { child.addMetadata({ tools: data.tools }); } this.spanStates.set(span.spanId, child); return; } if (data.type === "generation") { const model = (_a = data.model) !== null && _a !== void 0 ? _a : "openai"; const generation = this.logger.spanGeneration(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, { id: span.spanId, provider: "openai", model, messages: data.input, modelParameters: (_b = data.model_config) !== null && _b !== void 0 ? _b : {}, }); this.generationStates.set(span.spanId, generation); return; } if (data.type === "response") { const messages = typeof data._input === "string" ? [{ role: "user", content: data._input }] : Array.isArray(data._input) ? (0, utils_1.convertOpenAIResponsesMessagesToCompletionMessages)(data._input) : []; const generation = this.logger.spanGeneration(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, { id: span.spanId, provider: "openai", model: "unknown", messages, modelParameters: {}, }); this.generationStates.set(span.spanId, generation); return; } if (data.type === "function") { const tool = this.logger.spanToolCall(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, { id: span.spanId, name: data.name, description: data.name, args: data.input, }); if (data.mcp_data) { tool.addMetadata({ mcp_data: data.mcp_data }); } if (data.output) { tool.result(data.output); tool.end(); } else { this.toolCallStates.set(span.spanId, tool); } return; } if (data.type === "mcp_tools") { const tool = this.logger.spanToolCall(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, { id: span.spanId, name: (_c = data.server) !== null && _c !== void 0 ? _c : "unknown_mcp_tool_server", description: (_d = data.server) !== null && _d !== void 0 ? _d : "unknown_mcp_tool_server", args: "", }); if (data.result) { tool.result(JSON.stringify(data.result, null, 2)); tool.end(); } else { this.toolCallStates.set(span.spanId, tool); } return; } if (data.type === "guardrail") { this.logger.spanEvent(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, span.spanId, data.name, { triggered: data.triggered ? "true" : "false", type: data.type, }, undefined); return; } if (data.type === "handoff") { this.logger.spanEvent(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, span.spanId, `Handoff: ${(_e = data.from_agent) !== null && _e !== void 0 ? _e : "unknown_agent"}<>${(_f = data.to_agent) !== null && _f !== void 0 ? _f : "unknown_agent"}`, { type: data.type, from_agent: (_g = data.from_agent) !== null && _g !== void 0 ? _g : "unknown_agent", to_agent: (_h = data.to_agent) !== null && _h !== void 0 ? _h : "unknown_agent" }, undefined); return; } if (data.type === "custom") { this.logger.spanEvent(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, span.spanId, data.name, {}, { data: data.data }); return; } // For other kinds, do nothing // Not Handled by Maxim: "speech" | "transcription" | "speech_group" } async onSpanEnd(span) { var _a, _b, _c, _d; const traceState = this.traceStates.get(span.traceId); if (!traceState) { console.warn(`onSpanEnd called for span ${span.spanId} but no trace state found`); return; } const data = span.spanData; const topSpanId = traceState.topSpan.id; if (span.error) { this.logger.spanError(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, { id: (0, uuid_1.v4)(), message: span.error.message, metadata: span.error.data, }); } if (data.type === "agent") { const childToEnd = this.spanStates.get(span.spanId); if (!childToEnd) { console.warn(`onSpanEnd called for span ${span.spanId} but no child span found`); return; } if (data.handoffs) { childToEnd.addMetadata({ handoffs: data.handoffs }); } if (data.output_type) { childToEnd.addMetadata({ output_type: data.output_type }); } if (data.tools) { childToEnd.addMetadata({ tools: data.tools }); } childToEnd.end(); this.spanStates.delete(span.spanId); return; } if (data.type === "generation") { const generationToEnd = this.generationStates.get(span.spanId); if (!generationToEnd) { console.warn(`onSpanEnd called for span ${span.spanId} but no generation found`); return; } if (data.output && data.output.length > 0) { generationToEnd.result(data.output.at(-1)); } generationToEnd.end(); this.generationStates.delete(span.spanId); return; } if (data.type === "response") { const generationToEnd = this.generationStates.get(span.spanId); if (!generationToEnd) { console.warn(`onSpanEnd called for span ${span.spanId} but no generation found`); return; } if (data._input) { const messages = typeof data._input === "string" ? [{ role: "user", content: data._input }] : Array.isArray(data._input) ? (0, utils_1.convertOpenAIResponsesMessagesToCompletionMessages)(data._input) : []; if (messages.length) { generationToEnd.addMessages(messages); } } if (data._response) { const { completionResult, modelParameters } = (0, utils_1.convertOpenAIResponsesToCompletionResult)(data._response); if (completionResult.model) { generationToEnd.setModel(completionResult.model); } if (modelParameters && Object.keys(modelParameters).length > 0) { generationToEnd.setModelParameters(modelParameters); } generationToEnd.result(completionResult); } if (data.response_id) { generationToEnd.addTag("response_id", data.response_id); } generationToEnd.end(); this.generationStates.delete(span.spanId); return; } if (data.type === "function") { const toolToEnd = this.toolCallStates.get(span.spanId); if (!toolToEnd) { console.warn(`onSpanEnd called for span ${span.spanId} but no tool call found`); return; } if (data.mcp_data) { toolToEnd.addMetadata({ mcp_data: data.mcp_data }); } if (data.output) { toolToEnd.result(data.output); } toolToEnd.end(); this.toolCallStates.delete(span.spanId); return; } if (data.type === "mcp_tools") { const toolToEnd = this.toolCallStates.get(span.spanId); if (!toolToEnd) { console.warn(`onSpanEnd called for span ${span.spanId} but no tool call found`); return; } if (data.result) { toolToEnd.result(JSON.stringify(data.result, null, 2)); } toolToEnd.end(); this.toolCallStates.delete(span.spanId); return; } if (data.type === "guardrail") { this.logger.spanEvent(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, span.spanId, data.name, { triggered: data.triggered ? "true" : "false", type: data.type, }, undefined); return; } if (data.type === "handoff") { this.logger.spanEvent(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, span.spanId, `Handoff: ${(_a = data.from_agent) !== null && _a !== void 0 ? _a : "unknown_agent"}<>${(_b = data.to_agent) !== null && _b !== void 0 ? _b : "unknown_agent"}`, { type: data.type, from_agent: (_c = data.from_agent) !== null && _c !== void 0 ? _c : "unknown_agent", to_agent: (_d = data.to_agent) !== null && _d !== void 0 ? _d : "unknown_agent" }, undefined); return; } if (data.type === "custom") { this.logger.spanEvent(span.traceId !== span.parentId && span.parentId ? span.parentId : topSpanId, span.spanId, data.name, {}, { data: data.data }); return; } // For other kinds, do nothing // Not Handled by Maxim: "speech" | "transcription" | "speech_group" } } exports.MaximOpenAIAgentsProcessor = MaximOpenAIAgentsProcessor; //# sourceMappingURL=index.js.map