@morsehq-dev/sdk 0.4.1 → 0.5.0-rc.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/dist/anthropic/index.cjs +12 -10
- package/dist/anthropic/index.js +5 -3
- package/dist/anthropic-agent-sdk/index.cjs +52 -34
- package/dist/anthropic-agent-sdk/index.cjs.map +1 -1
- package/dist/anthropic-agent-sdk/index.js +25 -7
- package/dist/anthropic-agent-sdk/index.js.map +1 -1
- package/dist/browser/anthropic/index.cjs +12 -10
- package/dist/browser/anthropic/index.js +5 -3
- package/dist/browser/anthropic-agent-sdk/index.cjs +52 -34
- package/dist/browser/anthropic-agent-sdk/index.cjs.map +1 -1
- package/dist/browser/anthropic-agent-sdk/index.js +25 -7
- package/dist/browser/anthropic-agent-sdk/index.js.map +1 -1
- package/dist/browser/{chunk-GWJYOQNC.js → chunk-3MTJGZGX.js} +10 -4
- package/dist/browser/chunk-3MTJGZGX.js.map +1 -0
- package/dist/browser/{chunk-EFUQVASI.cjs → chunk-63ZI6PGP.cjs} +17 -15
- package/dist/browser/chunk-63ZI6PGP.cjs.map +1 -0
- package/dist/browser/{chunk-DXWHT3HK.cjs → chunk-A5EYWAOT.cjs} +428 -63
- package/dist/browser/chunk-A5EYWAOT.cjs.map +1 -0
- package/dist/browser/{chunk-AYNX24P5.js → chunk-AM6LMQCT.js} +3 -3
- package/dist/browser/{chunk-AYNX24P5.js.map → chunk-AM6LMQCT.js.map} +1 -1
- package/dist/{chunk-SBGY5NS4.js → browser/chunk-FDE32B3R.js} +6 -4
- package/dist/browser/chunk-FDE32B3R.js.map +1 -0
- package/dist/browser/{chunk-DOLWR7LD.js → chunk-L6FWDEGJ.js} +4 -4
- package/dist/browser/{chunk-DOLWR7LD.js.map → chunk-L6FWDEGJ.js.map} +1 -1
- package/dist/browser/{chunk-QWRQJO57.js → chunk-MLDVXMQD.js} +3 -3
- package/dist/browser/{chunk-QWRQJO57.js.map → chunk-MLDVXMQD.js.map} +1 -1
- package/dist/{chunk-Y7SVGUKY.cjs → browser/chunk-N3PXJCOE.cjs} +7 -7
- package/dist/browser/{chunk-YNUZGKUA.cjs.map → chunk-N3PXJCOE.cjs.map} +1 -1
- package/dist/browser/chunk-NH54DAJP.js +118 -0
- package/dist/browser/chunk-NH54DAJP.js.map +1 -0
- package/dist/browser/chunk-NRYB5GJT.js +28 -0
- package/dist/browser/chunk-NRYB5GJT.js.map +1 -0
- package/dist/browser/{chunk-F3B4UR64.js → chunk-OOGAYEJF.js} +423 -61
- package/dist/browser/chunk-OOGAYEJF.js.map +1 -0
- package/dist/browser/{chunk-3643DC7K.cjs → chunk-RBNCTHK3.cjs} +5 -2
- package/dist/browser/{chunk-3643DC7K.cjs.map → chunk-RBNCTHK3.cjs.map} +1 -1
- package/dist/browser/chunk-SOJYLXCR.cjs +126 -0
- package/dist/browser/chunk-SOJYLXCR.cjs.map +1 -0
- package/dist/browser/{chunk-HGN7J3EU.cjs → chunk-T4NHSVXH.cjs} +12 -12
- package/dist/browser/{chunk-HGN7J3EU.cjs.map → chunk-T4NHSVXH.cjs.map} +1 -1
- package/dist/browser/chunk-YMEU6XQQ.cjs +31 -0
- package/dist/browser/chunk-YMEU6XQQ.cjs.map +1 -0
- package/dist/browser/{chunk-M2GZJ3RS.cjs → chunk-YR2FCEWM.cjs} +20 -14
- package/dist/browser/chunk-YR2FCEWM.cjs.map +1 -0
- package/dist/browser/index.cjs +112 -82
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.js +40 -10
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/integrations/pino.cjs +4 -4
- package/dist/browser/integrations/pino.js +3 -3
- package/dist/browser/langchain/index.cjs +19 -19
- package/dist/browser/langchain/index.js +3 -3
- package/dist/browser/langgraph/index.cjs +34 -26
- package/dist/browser/langgraph/index.cjs.map +1 -1
- package/dist/browser/langgraph/index.js +16 -8
- package/dist/browser/langgraph/index.js.map +1 -1
- package/dist/browser/openai/index.cjs +22 -8
- package/dist/browser/openai/index.cjs.map +1 -1
- package/dist/browser/openai/index.js +17 -3
- package/dist/browser/openai/index.js.map +1 -1
- package/dist/browser/openai-agents/index.cjs +42 -34
- package/dist/browser/openai-agents/index.cjs.map +1 -1
- package/dist/browser/openai-agents/index.js +13 -5
- package/dist/browser/openai-agents/index.js.map +1 -1
- package/dist/browser/vercel-ai/index.cjs +16 -16
- package/dist/browser/vercel-ai/index.js +4 -4
- package/dist/{chunk-LTKGLXAE.js → chunk-3IGQWLWE.js} +447 -76
- package/dist/chunk-3IGQWLWE.js.map +1 -0
- package/dist/{chunk-4R4SHGOK.js → chunk-4JPZV2VZ.js} +3 -3
- package/dist/{chunk-4R4SHGOK.js.map → chunk-4JPZV2VZ.js.map} +1 -1
- package/dist/chunk-6DZNK4VX.cjs +31 -0
- package/dist/chunk-6DZNK4VX.cjs.map +1 -0
- package/dist/{browser/chunk-F7BGQ26H.js → chunk-FCAPLB7A.js} +6 -4
- package/dist/chunk-FCAPLB7A.js.map +1 -0
- package/dist/{chunk-KJEO52QS.cjs → chunk-HICEJ4LV.cjs} +5 -2
- package/dist/{chunk-KJEO52QS.cjs.map → chunk-HICEJ4LV.cjs.map} +1 -1
- package/dist/chunk-S4WUABAU.js +28 -0
- package/dist/chunk-S4WUABAU.js.map +1 -0
- package/dist/{chunk-V5J6J4ZR.js → chunk-SHK2XEPB.js} +4 -4
- package/dist/{chunk-V5J6J4ZR.js.map → chunk-SHK2XEPB.js.map} +1 -1
- package/dist/{chunk-LQI63FHF.cjs → chunk-SNGYSP7Z.cjs} +20 -14
- package/dist/chunk-SNGYSP7Z.cjs.map +1 -0
- package/dist/chunk-VHF4GWJ4.cjs +126 -0
- package/dist/chunk-VHF4GWJ4.cjs.map +1 -0
- package/dist/{chunk-HNCWTENX.cjs → chunk-XCOZIFHC.cjs} +452 -78
- package/dist/chunk-XCOZIFHC.cjs.map +1 -0
- package/dist/chunk-XJUH6PR5.js +118 -0
- package/dist/chunk-XJUH6PR5.js.map +1 -0
- package/dist/{browser/chunk-YNUZGKUA.cjs → chunk-Y2QHMV6H.cjs} +7 -7
- package/dist/{chunk-Y7SVGUKY.cjs.map → chunk-Y2QHMV6H.cjs.map} +1 -1
- package/dist/{chunk-STK4KJQ6.js → chunk-YJYF4NSA.js} +10 -4
- package/dist/chunk-YJYF4NSA.js.map +1 -0
- package/dist/{chunk-5D6SBSEI.cjs → chunk-Z2IYSTRT.cjs} +17 -15
- package/dist/chunk-Z2IYSTRT.cjs.map +1 -0
- package/dist/{chunk-QXRJUPDP.cjs → chunk-ZIER4DGK.cjs} +12 -12
- package/dist/{chunk-QXRJUPDP.cjs.map → chunk-ZIER4DGK.cjs.map} +1 -1
- package/dist/{chunk-AEFUXEKV.js → chunk-ZWOSPXS4.js} +3 -3
- package/dist/{chunk-AEFUXEKV.js.map → chunk-ZWOSPXS4.js.map} +1 -1
- package/dist/index.cjs +119 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +41 -11
- package/dist/index.js.map +1 -1
- package/dist/integrations/pino.cjs +4 -4
- package/dist/integrations/pino.js +3 -3
- package/dist/langchain/index.cjs +19 -19
- package/dist/langchain/index.d.cts +1 -1
- package/dist/langchain/index.d.ts +1 -1
- package/dist/langchain/index.js +3 -3
- package/dist/langgraph/index.cjs +34 -26
- package/dist/langgraph/index.cjs.map +1 -1
- package/dist/langgraph/index.d.cts +1 -1
- package/dist/langgraph/index.d.ts +1 -1
- package/dist/langgraph/index.js +16 -8
- package/dist/langgraph/index.js.map +1 -1
- package/dist/openai/index.cjs +22 -8
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.js +17 -3
- package/dist/openai/index.js.map +1 -1
- package/dist/openai-agents/index.cjs +42 -34
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.js +13 -5
- package/dist/openai-agents/index.js.map +1 -1
- package/dist/{tracing-BYAqjT5Q.d.cts → tracing-CNlre8TK.d.cts} +1 -0
- package/dist/{tracing-rz9cWQ8d.d.ts → tracing-J8ZgxVzU.d.ts} +1 -0
- package/dist/vercel-ai/index.cjs +16 -16
- package/dist/vercel-ai/index.js +4 -4
- package/package.json +1 -9
- package/dist/browser/chunk-3BGG7GH3.cjs +0 -183
- package/dist/browser/chunk-3BGG7GH3.cjs.map +0 -1
- package/dist/browser/chunk-DXWHT3HK.cjs.map +0 -1
- package/dist/browser/chunk-EFUQVASI.cjs.map +0 -1
- package/dist/browser/chunk-F3B4UR64.js.map +0 -1
- package/dist/browser/chunk-F7BGQ26H.js.map +0 -1
- package/dist/browser/chunk-GWJYOQNC.js.map +0 -1
- package/dist/browser/chunk-M2GZJ3RS.cjs.map +0 -1
- package/dist/browser/chunk-YHV5GL32.js +0 -178
- package/dist/browser/chunk-YHV5GL32.js.map +0 -1
- package/dist/chunk-5D6SBSEI.cjs.map +0 -1
- package/dist/chunk-HNCWTENX.cjs.map +0 -1
- package/dist/chunk-LQI63FHF.cjs.map +0 -1
- package/dist/chunk-LTKGLXAE.js.map +0 -1
- package/dist/chunk-POCQNSVD.js +0 -178
- package/dist/chunk-POCQNSVD.js.map +0 -1
- package/dist/chunk-SBGY5NS4.js.map +0 -1
- package/dist/chunk-STK4KJQ6.js.map +0 -1
- package/dist/chunk-T2KW3P23.cjs +0 -183
- package/dist/chunk-T2KW3P23.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/openai/index.ts"],"names":["init_tracing","getActiveTraceState","startSpan","calculateCost","runAsync","decomposeOpenAiMessages"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * `@morsehq-dev/sdk/openai` — plain OpenAI SDK instrumentation.\n *\n * Wraps a raw `openai` npm package client (`new OpenAI()` /\n * `new AzureOpenAI()` / any structurally-compatible client) so every\n * `chat.completions.create` call emits an `llm` span carrying model,\n * token counts, `cost_usd`, and pre-segmented `context_segments`.\n *\n * TS port of `apps/sdk/python/src/morse_ai/adapters/openai.py`'s `wrap()`.\n * This module intentionally covers only the `wrap(client)` recommended\n * path (mirrors the Python module's three-layer doc comment, layer 2) —\n * no module-level monkey-patch / `install()` layer, since plain `openai`\n * is not one of the auto-detect targets on either SDK (see\n * `_internal/auto-detect.ts` and Python's `_internal/auto_detect.py`\n * `_adapter_targets()`, which both omit bare `openai`).\n *\n * Distinct from `@morsehq-dev/sdk/openai-agents`, which instruments the\n * `@openai/agents` Agents SDK (a different package with its own Runner /\n * Agent abstractions). This module is for customers calling\n * `openai.chat.completions.create` directly.\n *\n * Context segments are extracted via the shared\n * `_agent-sdk-common/context-decomposer.ts` (`decomposeOpenAiMessages`,\n * MHQ-750) — the request's `messages`/`tools` are already in the native\n * OpenAI wire format the decomposer expects, so no translation layer is\n * needed.\n *\n * Idempotent — wrapping an already-wrapped client instance is a no-op\n * that returns the same wrapper. Recording is best-effort: any failure\n * while building/emitting the span is swallowed so it can never break the\n * customer's OpenAI call. The customer's own API error (thrown by the\n * underlying `create()` call) always propagates unchanged — the span is\n * marked `status: \"failed\"` first, then the error is re-raised.\n *\n * Mirrors `apps/sdk/typescript/src/anthropic/index.ts` in shape.\n */\n\nimport { decomposeOpenAiMessages } from \"../_agent-sdk-common/context-decomposer\";\nimport { calculateCost } from \"../_internal/pricing\";\nimport type { ContextSegment } from \"../_internal/spans\";\nimport { getActiveTraceState, runAsync as _runAsync, startSpan } from \"../_internal/tracing\";\n\n/* ------------------------------------------------------------------------ */\n/* Public types — minimal structural shapes, NOT a hard dep on `openai` */\n/* ------------------------------------------------------------------------ */\n\n/** Subset of the `openai` package's chat message shape we read from. */\nexport interface OpenAiChatMessageParam {\n role: string;\n content?: unknown;\n tool_call_id?: string;\n [key: string]: unknown;\n}\n\n/** Subset of the `openai` package's tool-definition shape we read from. */\nexport interface OpenAiToolParam {\n type?: string;\n function?: { name?: string; [key: string]: unknown };\n [key: string]: unknown;\n}\n\n/**\n * Subset of `openai`'s `ChatCompletionCreateParams` we read from. We\n * deliberately decline to import the real types so the SDK works even when\n * `openai` isn't installed (it's an optional peer dep).\n */\nexport interface OpenAiChatCompletionCreateParams {\n model: string;\n messages: OpenAiChatMessageParam[];\n tools?: OpenAiToolParam[];\n // Allow forward-compat fields without complaint.\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsageDetails {\n cached_tokens?: number;\n reasoning_tokens?: number;\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n prompt_tokens_details?: OpenAiCompletionUsageDetails;\n completion_tokens_details?: OpenAiCompletionUsageDetails;\n [key: string]: unknown;\n}\n\nexport interface OpenAiChatCompletionResponse {\n id?: string;\n model?: string;\n usage?: OpenAiCompletionUsage;\n choices?: Array<{ message?: { content?: unknown; [k: string]: unknown }; [k: string]: unknown }>;\n [key: string]: unknown;\n}\n\n/**\n * Structural shape of the plain OpenAI client we wrap. We only require\n * that `client.chat.completions.create` is a function returning a Promise.\n */\nexport interface OpenAiClientLike {\n chat: {\n completions: {\n create(\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ): Promise<OpenAiChatCompletionResponse>;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level sentinel */\n/* ------------------------------------------------------------------------ */\n\nconst WRAPPED_CLIENT = Symbol.for(\"morse.openai.wrapped\");\n\n/* ------------------------------------------------------------------------ */\n/* Public API */\n/* ------------------------------------------------------------------------ */\n\nexport interface WrapOpenAiOptions {\n /**\n * When provided, used as the parent agent span's name when\n * `chat.completions.create` is called outside an active trace. Defaults\n * to `\"openai.chat.completions.create\"`.\n */\n defaultAgentName?: string;\n}\n\n/**\n * Wrap an OpenAI client so every `chat.completions.create` call records an\n * `llm` span with `model`, token counts, `cost_usd`, and `context_segments`.\n * The wrapper preserves the rest of the client's surface (other resources,\n * helpers, etc.) via a Proxy.\n *\n * Idempotent — a previously wrapped client is returned as-is.\n *\n * @example\n * ```ts\n * import { wrapOpenAI } from \"@morsehq-dev/sdk/openai\";\n * import OpenAI from \"openai\";\n *\n * const client = wrapOpenAI(new OpenAI());\n * const response = await client.chat.completions.create({\n * model: \"gpt-4o-mini\",\n * messages: [{ role: \"user\", content: \"hello\" }],\n * });\n * ```\n */\nexport function wrapOpenAI<C extends OpenAiClientLike>(\n client: C,\n options: WrapOpenAiOptions = {},\n): C {\n if (!client || typeof client !== \"object\") return client;\n // Already wrapped? Pass through.\n if ((client as Record<string | symbol, unknown>)[WRAPPED_CLIENT]) return client;\n\n const originalCreate = client.chat?.completions?.create?.bind(client.chat.completions);\n if (typeof originalCreate !== \"function\") return client;\n\n const patchedCreate = buildPatchedCreate(originalCreate, options);\n\n // Construct a new `completions` object that proxies to the original for\n // any non-`create` access, wrapped in a `chat` proxy, wrapped in a\n // top-level client proxy — mirrors `wrapAnthropic`'s nested-Proxy shape.\n const patchedCompletions = new Proxy(client.chat.completions, {\n get(target, prop, receiver) {\n if (prop === \"create\") return patchedCreate;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const patchedChat = new Proxy(client.chat, {\n get(target, prop, receiver) {\n if (prop === \"completions\") return patchedCompletions;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const wrapper = new Proxy(client, {\n get(target, prop, receiver) {\n if (prop === WRAPPED_CLIENT) return true;\n if (prop === \"chat\") return patchedChat;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n return wrapper as C;\n}\n\n/** True when a wrapped OpenAI client is passed in. */\nexport function isOpenAiWrapped(client: unknown): boolean {\n if (!client || typeof client !== \"object\") return false;\n return (client as Record<string | symbol, unknown>)[WRAPPED_CLIENT] === true;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Internals — wrapper that records an `llm` span */\n/* ------------------------------------------------------------------------ */\n\nfunction buildPatchedCreate(\n original: (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ) => Promise<OpenAiChatCompletionResponse>,\n options: WrapOpenAiOptions,\n): (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n) => Promise<OpenAiChatCompletionResponse> {\n const agentName = options.defaultAgentName ?? \"openai.chat.completions.create\";\n\n return async function patchedCreate(\n this: unknown,\n params: OpenAiChatCompletionCreateParams,\n callOptions?: unknown,\n ): Promise<OpenAiChatCompletionResponse> {\n const inTrace = getActiveTraceState() !== null;\n\n const work = async (): Promise<OpenAiChatCompletionResponse> => {\n const handle = startSpan({\n name: \"openai.chat.completions.create\",\n type: \"llm\",\n metadata: { model: params?.model },\n });\n let response: OpenAiChatCompletionResponse | undefined;\n try {\n response = await original.call(this, params, callOptions);\n } catch (err) {\n // Customer's own API error — mark the span failed, then re-raise\n // unchanged. Never swallow the caller's real error.\n handle?.end(err);\n throw err;\n }\n\n // Recording is best-effort — never let it throw at the caller.\n try {\n if (handle) {\n const segments = safeExtractSegments(params);\n const usage = response?.usage ?? {};\n const inputTokens = usage.prompt_tokens ?? 0;\n const outputTokens = usage.completion_tokens ?? 0;\n const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const model = response?.model ?? params?.model;\n\n handle.span.model = model;\n if (inputTokens > 0) handle.span.input_tokens = inputTokens;\n if (outputTokens > 0) handle.span.output_tokens = outputTokens;\n if (cachedTokens > 0) handle.span.cached_input_tokens = cachedTokens;\n const totalTokens =\n usage.total_tokens ??\n (inputTokens > 0 || outputTokens > 0 ? inputTokens + outputTokens : 0);\n if (totalTokens > 0) handle.span.total_tokens = totalTokens;\n\n if (model && (inputTokens > 0 || outputTokens > 0)) {\n const cost = calculateCost({\n inputTokens,\n outputTokens,\n model,\n cachedInputTokens: cachedTokens,\n });\n if (cost !== undefined) handle.span.cost_usd = cost;\n }\n handle.span.context_segments = segments;\n }\n } catch {\n // swallow — telemetry must never break the caller\n }\n handle?.end();\n return response;\n };\n\n if (inTrace) {\n return work();\n }\n\n // No active trace — open a fresh agent span around the call so the\n // llm span has a parent. Mirrors `wrapAnthropic`'s pattern: we can't\n // return `work()`'s Promise from inside `_runAsync`'s callback while\n // preserving typing AND having the fresh trace own the lifecycle, so\n // we wrap explicitly.\n let result: OpenAiChatCompletionResponse | undefined;\n let thrown: unknown;\n await _runAsync({ agentName }, async (runHandle) => {\n runHandle.setOutcome(true);\n try {\n result = await work();\n } catch (err) {\n thrown = err;\n throw err;\n }\n return null;\n });\n if (thrown !== undefined) throw thrown;\n return result as OpenAiChatCompletionResponse;\n };\n}\n\nfunction safeExtractSegments(params: OpenAiChatCompletionCreateParams): ContextSegment[] {\n try {\n if (!params || typeof params !== \"object\") return [];\n return decomposeOpenAiMessages(params.messages, params.tools);\n } catch {\n return [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/openai/index.ts"],"names":["init_tracing","init_diagnostics","getActiveTraceState","setActiveFramework","startSpan","calculateCost","captureInputMessages","extractOpenAiOutputText","runAsync","decomposeOpenAiMessages","report"],"mappings":";;;;;;;;AA0CAA,8BAAA,EAAA;AACAC,kCAAA,EAAA;AA+EA,IAAM,cAAA,mBAAiB,MAAA,CAAO,GAAA,CAAI,sBAAsB,CAAA;AAmCjD,SAAS,UAAA,CACd,MAAA,EACA,OAAA,GAA6B,EAAC,EAC3B;AACH,EAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,UAAU,OAAO,MAAA;AAElD,EAAA,IAAK,MAAA,CAA4C,cAAc,CAAA,EAAG,OAAO,MAAA;AAEzE,EAAA,MAAM,cAAA,GAAiB,OAAO,IAAA,EAAM,WAAA,EAAa,QAAQ,IAAA,CAAK,MAAA,CAAO,KAAK,WAAW,CAAA;AACrF,EAAA,IAAI,OAAO,cAAA,KAAmB,UAAA,EAAY,OAAO,MAAA;AAEjD,EAAA,MAAM,aAAA,GAAgB,kBAAA,CAAmB,cAAA,EAAgB,OAAO,CAAA;AAKhE,EAAA,MAAM,kBAAA,GAAqB,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,WAAA,EAAa;AAAA,IAC5D,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU;AAC1B,MAAA,IAAI,IAAA,KAAS,UAAU,OAAO,aAAA;AAC9B,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,QAAQ,CAAA;AAChD,MAAA,IAAI,OAAO,KAAA,KAAU,UAAA,EAAY,OAAQ,KAAA,CAAuC,KAAK,MAAM,CAAA;AAC3F,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AAED,EAAA,MAAM,WAAA,GAAc,IAAI,KAAA,CAAM,MAAA,CAAO,IAAA,EAAM;AAAA,IACzC,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU;AAC1B,MAAA,IAAI,IAAA,KAAS,eAAe,OAAO,kBAAA;AACnC,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,QAAQ,CAAA;AAChD,MAAA,IAAI,OAAO,KAAA,KAAU,UAAA,EAAY,OAAQ,KAAA,CAAuC,KAAK,MAAM,CAAA;AAC3F,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AAED,EAAA,MAAM,OAAA,GAAU,IAAI,KAAA,CAAM,MAAA,EAAQ;AAAA,IAChC,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU;AAC1B,MAAA,IAAI,IAAA,KAAS,gBAAgB,OAAO,IAAA;AACpC,MAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,WAAA;AAC5B,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,QAAQ,CAAA;AAChD,MAAA,IAAI,OAAO,KAAA,KAAU,UAAA,EAAY,OAAQ,KAAA,CAAuC,KAAK,MAAM,CAAA;AAC3F,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AAED,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,gBAAgB,MAAA,EAA0B;AACxD,EAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,UAAU,OAAO,KAAA;AAClD,EAAA,OAAQ,MAAA,CAA4C,cAAc,CAAA,KAAM,IAAA;AAC1E;AAMA,SAAS,kBAAA,CACP,UAIA,OAAA,EAIyC;AACzC,EAAA,MAAM,SAAA,GAAY,QAAQ,gBAAA,IAAoB,gCAAA;AAE9C,EAAA,OAAO,eAAe,aAAA,CAEpB,MAAA,EACA,WAAA,EACuC;AACvC,IAAA,MAAM,OAAA,GAAUC,uCAAoB,KAAM,IAAA;AAE1C,IAAA,MAAM,OAAO,YAAmD;AAC9D,MAAAC,oCAAA,CAAmB,QAAQ,CAAA;AAC3B,MAAA,MAAM,SAASC,2BAAA,CAAU;AAAA,QACvB,IAAA,EAAM,gCAAA;AAAA,QACN,IAAA,EAAM,KAAA;AAAA,QACN,QAAA,EAAU,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA;AAAM,OAClC,CAAA;AACD,MAAA,IAAI,QAAA;AACJ,MAAA,IAAI;AACF,QAAA,QAAA,GAAW,MAAM,QAAA,CAAS,IAAA,CAAK,IAAA,EAAM,QAAQ,WAAW,CAAA;AAAA,MAC1D,SAAS,GAAA,EAAK;AAGZ,QAAA,MAAA,EAAQ,IAAI,GAAG,CAAA;AACf,QAAA,MAAM,GAAA;AAAA,MACR;AAGA,MAAA,IAAI;AACF,QAAA,IAAI,MAAA,EAAQ;AACV,UAAA,MAAM,QAAA,GAAW,oBAAoB,MAAM,CAAA;AAC3C,UAAA,MAAM,KAAA,GAAQ,QAAA,EAAU,KAAA,IAAS,EAAC;AAClC,UAAA,MAAM,WAAA,GAAc,MAAM,aAAA,IAAiB,CAAA;AAC3C,UAAA,MAAM,YAAA,GAAe,MAAM,iBAAA,IAAqB,CAAA;AAChD,UAAA,MAAM,YAAA,GAAe,KAAA,CAAM,qBAAA,EAAuB,aAAA,IAAiB,CAAA;AACnE,UAAA,MAAM,KAAA,GAAQ,QAAA,EAAU,KAAA,IAAS,MAAA,EAAQ,KAAA;AAEzC,UAAA,MAAA,CAAO,KAAK,KAAA,GAAQ,KAAA;AACpB,UAAA,IAAI,WAAA,GAAc,CAAA,EAAG,MAAA,CAAO,IAAA,CAAK,YAAA,GAAe,WAAA;AAChD,UAAA,IAAI,YAAA,GAAe,CAAA,EAAG,MAAA,CAAO,IAAA,CAAK,aAAA,GAAgB,YAAA;AAClD,UAAA,IAAI,YAAA,GAAe,CAAA,EAAG,MAAA,CAAO,IAAA,CAAK,mBAAA,GAAsB,YAAA;AACxD,UAAA,MAAM,WAAA,GACJ,MAAM,YAAA,KACL,WAAA,GAAc,KAAK,YAAA,GAAe,CAAA,GAAI,cAAc,YAAA,GAAe,CAAA,CAAA;AACtE,UAAA,IAAI,WAAA,GAAc,CAAA,EAAG,MAAA,CAAO,IAAA,CAAK,YAAA,GAAe,WAAA;AAEhD,UAAA,IAAI,KAAA,KAAU,WAAA,GAAc,CAAA,IAAK,YAAA,GAAe,CAAA,CAAA,EAAI;AAClD,YAAA,MAAM,OAAOC,+BAAA,CAAc;AAAA,cACzB,WAAA;AAAA,cACA,YAAA;AAAA,cACA,KAAA;AAAA,cACA,iBAAA,EAAmB;AAAA,aACpB,CAAA;AACD,YAAA,IAAI,IAAA,KAAS,KAAA,CAAA,EAAW,MAAA,CAAO,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,UACjD;AACA,UAAA,MAAA,CAAO,KAAK,gBAAA,GAAmB,QAAA;AAG/B,UAAA,MAAM,SAAA,GAAYC,sCAAA,CAAqB,MAAA,EAAQ,QAAQ,CAAA;AACvD,UAAA,IAAI,SAAA,KAAc,KAAA,CAAA,EAAW,MAAA,CAAO,IAAA,CAAK,UAAA,GAAa,SAAA;AACtD,UAAA,MAAM,UAAA,GAAaC,yCAAA,CAAwB,QAAA,EAAU,OAAO,CAAA;AAC5D,UAAA,IAAI,eAAe,KAAA,CAAA,EAAW,MAAA,CAAO,KAAK,WAAA,GAAc,EAAE,SAAS,UAAA,EAAW;AAAA,QAChF;AAAA,MACF,SAAS,GAAA,EAAK;AAEZ,QAAA,eAAA,CAAgB,KAAK,0BAA0B,CAAA;AAAA,MACjD;AACA,MAAA,MAAA,EAAQ,GAAA,EAAI;AACZ,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAEA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AAOA,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,MAAA;AACJ,IAAA,MAAMC,0BAAA,CAAU,EAAE,SAAA,EAAU,EAAG,OAAO,SAAA,KAAc;AAClD,MAAA,SAAA,CAAU,WAAW,IAAI,CAAA;AACzB,MAAA,IAAI;AACF,QAAA,MAAA,GAAS,MAAM,IAAA,EAAK;AAAA,MACtB,SAAS,GAAA,EAAK;AACZ,QAAA,MAAA,GAAS,GAAA;AACT,QAAA,MAAM,GAAA;AAAA,MACR;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAA;AACD,IAAA,IAAI,MAAA,KAAW,QAAW,MAAM,MAAA;AAChC,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AACF;AAEA,SAAS,oBAAoB,MAAA,EAA4D;AACvF,EAAA,IAAI;AACF,IAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,SAAiB,EAAC;AACnD,IAAA,OAAOC,yCAAA,CAAwB,MAAA,CAAO,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AAAA,EAC9D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAQA,SAAS,eAAA,CAAgB,KAAc,WAAA,EAA2B;AAChE,EAAA,IAAI;AACF,IAAAC,wBAAA,CAAO,GAAA,EAAK,EAAE,WAAA,EAAa,CAAA;AAAA,EAC7B,CAAA,CAAA,MAAQ;AAAA,EAER;AACF","file":"index.cjs","sourcesContent":["/**\n * `@morsehq-dev/sdk/openai` — plain OpenAI SDK instrumentation.\n *\n * Wraps a raw `openai` npm package client (`new OpenAI()` /\n * `new AzureOpenAI()` / any structurally-compatible client) so every\n * `chat.completions.create` call emits an `llm` span carrying model,\n * token counts, `cost_usd`, and pre-segmented `context_segments`.\n *\n * TS port of `apps/sdk/python/src/morse_ai/adapters/openai.py`'s `wrap()`.\n * This module intentionally covers only the `wrap(client)` recommended\n * path (mirrors the Python module's three-layer doc comment, layer 2) —\n * no module-level monkey-patch / `install()` layer, since plain `openai`\n * is not one of the auto-detect targets on either SDK (see\n * `_internal/auto-detect.ts` and Python's `_internal/auto_detect.py`\n * `_adapter_targets()`, which both omit bare `openai`).\n *\n * Distinct from `@morsehq-dev/sdk/openai-agents`, which instruments the\n * `@openai/agents` Agents SDK (a different package with its own Runner /\n * Agent abstractions). This module is for customers calling\n * `openai.chat.completions.create` directly.\n *\n * Context segments are extracted via the shared\n * `_agent-sdk-common/context-decomposer.ts` (`decomposeOpenAiMessages`,\n * MHQ-750) — the request's `messages`/`tools` are already in the native\n * OpenAI wire format the decomposer expects, so no translation layer is\n * needed.\n *\n * Idempotent — wrapping an already-wrapped client instance is a no-op\n * that returns the same wrapper. Recording is best-effort: any failure\n * while building/emitting the span is swallowed so it can never break the\n * customer's OpenAI call. The customer's own API error (thrown by the\n * underlying `create()` call) always propagates unchanged — the span is\n * marked `status: \"failed\"` first, then the error is re-raised.\n *\n * Mirrors `apps/sdk/typescript/src/anthropic/index.ts` in shape.\n */\n\nimport { decomposeOpenAiMessages } from \"../_agent-sdk-common/context-decomposer\";\nimport { setActiveFramework } from \"../_internal/active-framework\";\nimport { captureInputMessages, extractOpenAiOutputText } from \"../_internal/llm-io\";\nimport { calculateCost } from \"../_internal/pricing\";\nimport type { ContextSegment } from \"../_internal/spans\";\nimport { getActiveTraceState, runAsync as _runAsync, startSpan } from \"../_internal/tracing\";\nimport { report } from \"../_internal/diagnostics\";\n\n/* ------------------------------------------------------------------------ */\n/* Public types — minimal structural shapes, NOT a hard dep on `openai` */\n/* ------------------------------------------------------------------------ */\n\n/** Subset of the `openai` package's chat message shape we read from. */\nexport interface OpenAiChatMessageParam {\n role: string;\n content?: unknown;\n tool_call_id?: string;\n [key: string]: unknown;\n}\n\n/** Subset of the `openai` package's tool-definition shape we read from. */\nexport interface OpenAiToolParam {\n type?: string;\n function?: { name?: string; [key: string]: unknown };\n [key: string]: unknown;\n}\n\n/**\n * Subset of `openai`'s `ChatCompletionCreateParams` we read from. We\n * deliberately decline to import the real types so the SDK works even when\n * `openai` isn't installed (it's an optional peer dep).\n */\nexport interface OpenAiChatCompletionCreateParams {\n model: string;\n messages: OpenAiChatMessageParam[];\n tools?: OpenAiToolParam[];\n // Allow forward-compat fields without complaint.\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsageDetails {\n cached_tokens?: number;\n reasoning_tokens?: number;\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n prompt_tokens_details?: OpenAiCompletionUsageDetails;\n completion_tokens_details?: OpenAiCompletionUsageDetails;\n [key: string]: unknown;\n}\n\nexport interface OpenAiChatCompletionResponse {\n id?: string;\n model?: string;\n usage?: OpenAiCompletionUsage;\n choices?: Array<{ message?: { content?: unknown; [k: string]: unknown }; [k: string]: unknown }>;\n [key: string]: unknown;\n}\n\n/**\n * Structural shape of the plain OpenAI client we wrap. We only require\n * that `client.chat.completions.create` is a function returning a Promise.\n */\nexport interface OpenAiClientLike {\n chat: {\n completions: {\n create(\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ): Promise<OpenAiChatCompletionResponse>;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level sentinel */\n/* ------------------------------------------------------------------------ */\n\nconst WRAPPED_CLIENT = Symbol.for(\"morse.openai.wrapped\");\n\n/* ------------------------------------------------------------------------ */\n/* Public API */\n/* ------------------------------------------------------------------------ */\n\nexport interface WrapOpenAiOptions {\n /**\n * When provided, used as the parent agent span's name when\n * `chat.completions.create` is called outside an active trace. Defaults\n * to `\"openai.chat.completions.create\"`.\n */\n defaultAgentName?: string;\n}\n\n/**\n * Wrap an OpenAI client so every `chat.completions.create` call records an\n * `llm` span with `model`, token counts, `cost_usd`, and `context_segments`.\n * The wrapper preserves the rest of the client's surface (other resources,\n * helpers, etc.) via a Proxy.\n *\n * Idempotent — a previously wrapped client is returned as-is.\n *\n * @example\n * ```ts\n * import { wrapOpenAI } from \"@morsehq-dev/sdk/openai\";\n * import OpenAI from \"openai\";\n *\n * const client = wrapOpenAI(new OpenAI());\n * const response = await client.chat.completions.create({\n * model: \"gpt-4o-mini\",\n * messages: [{ role: \"user\", content: \"hello\" }],\n * });\n * ```\n */\nexport function wrapOpenAI<C extends OpenAiClientLike>(\n client: C,\n options: WrapOpenAiOptions = {},\n): C {\n if (!client || typeof client !== \"object\") return client;\n // Already wrapped? Pass through.\n if ((client as Record<string | symbol, unknown>)[WRAPPED_CLIENT]) return client;\n\n const originalCreate = client.chat?.completions?.create?.bind(client.chat.completions);\n if (typeof originalCreate !== \"function\") return client;\n\n const patchedCreate = buildPatchedCreate(originalCreate, options);\n\n // Construct a new `completions` object that proxies to the original for\n // any non-`create` access, wrapped in a `chat` proxy, wrapped in a\n // top-level client proxy — mirrors `wrapAnthropic`'s nested-Proxy shape.\n const patchedCompletions = new Proxy(client.chat.completions, {\n get(target, prop, receiver) {\n if (prop === \"create\") return patchedCreate;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const patchedChat = new Proxy(client.chat, {\n get(target, prop, receiver) {\n if (prop === \"completions\") return patchedCompletions;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const wrapper = new Proxy(client, {\n get(target, prop, receiver) {\n if (prop === WRAPPED_CLIENT) return true;\n if (prop === \"chat\") return patchedChat;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n return wrapper as C;\n}\n\n/** True when a wrapped OpenAI client is passed in. */\nexport function isOpenAiWrapped(client: unknown): boolean {\n if (!client || typeof client !== \"object\") return false;\n return (client as Record<string | symbol, unknown>)[WRAPPED_CLIENT] === true;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Internals — wrapper that records an `llm` span */\n/* ------------------------------------------------------------------------ */\n\nfunction buildPatchedCreate(\n original: (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ) => Promise<OpenAiChatCompletionResponse>,\n options: WrapOpenAiOptions,\n): (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n) => Promise<OpenAiChatCompletionResponse> {\n const agentName = options.defaultAgentName ?? \"openai.chat.completions.create\";\n\n return async function patchedCreate(\n this: unknown,\n params: OpenAiChatCompletionCreateParams,\n callOptions?: unknown,\n ): Promise<OpenAiChatCompletionResponse> {\n const inTrace = getActiveTraceState() !== null;\n\n const work = async (): Promise<OpenAiChatCompletionResponse> => {\n setActiveFramework(\"openai\");\n const handle = startSpan({\n name: \"openai.chat.completions.create\",\n type: \"llm\",\n metadata: { model: params?.model },\n });\n let response: OpenAiChatCompletionResponse | undefined;\n try {\n response = await original.call(this, params, callOptions);\n } catch (err) {\n // Customer's own API error — mark the span failed, then re-raise\n // unchanged. Never swallow the caller's real error.\n handle?.end(err);\n throw err;\n }\n\n // Recording is best-effort — never let it throw at the caller.\n try {\n if (handle) {\n const segments = safeExtractSegments(params);\n const usage = response?.usage ?? {};\n const inputTokens = usage.prompt_tokens ?? 0;\n const outputTokens = usage.completion_tokens ?? 0;\n const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const model = response?.model ?? params?.model;\n\n handle.span.model = model;\n if (inputTokens > 0) handle.span.input_tokens = inputTokens;\n if (outputTokens > 0) handle.span.output_tokens = outputTokens;\n if (cachedTokens > 0) handle.span.cached_input_tokens = cachedTokens;\n const totalTokens =\n usage.total_tokens ??\n (inputTokens > 0 || outputTokens > 0 ? inputTokens + outputTokens : 0);\n if (totalTokens > 0) handle.span.total_tokens = totalTokens;\n\n if (model && (inputTokens > 0 || outputTokens > 0)) {\n const cost = calculateCost({\n inputTokens,\n outputTokens,\n model,\n cachedInputTokens: cachedTokens,\n });\n if (cost !== undefined) handle.span.cost_usd = cost;\n }\n handle.span.context_segments = segments;\n // MHQ-858: prompt/response capture, parity with the Python\n // adapter's input_data/output_data.\n const inputData = captureInputMessages(params?.messages);\n if (inputData !== undefined) handle.span.input_data = inputData;\n const outputText = extractOpenAiOutputText(response?.choices);\n if (outputText !== undefined) handle.span.output_data = { content: outputText };\n }\n } catch (err) {\n // swallow — telemetry must never break the caller\n reportSwallowed(err, \"morse.openai.stream_wrap\");\n }\n handle?.end();\n return response;\n };\n\n if (inTrace) {\n return work();\n }\n\n // No active trace — open a fresh agent span around the call so the\n // llm span has a parent. Mirrors `wrapAnthropic`'s pattern: we can't\n // return `work()`'s Promise from inside `_runAsync`'s callback while\n // preserving typing AND having the fresh trace own the lifecycle, so\n // we wrap explicitly.\n let result: OpenAiChatCompletionResponse | undefined;\n let thrown: unknown;\n await _runAsync({ agentName }, async (runHandle) => {\n runHandle.setOutcome(true);\n try {\n result = await work();\n } catch (err) {\n thrown = err;\n throw err;\n }\n return null;\n });\n if (thrown !== undefined) throw thrown;\n return result as OpenAiChatCompletionResponse;\n };\n}\n\nfunction safeExtractSegments(params: OpenAiChatCompletionCreateParams): ContextSegment[] {\n try {\n if (!params || typeof params !== \"object\") return [];\n return decomposeOpenAiMessages(params.messages, params.tools);\n } catch {\n return [];\n }\n}\n\n/**\n * Route a swallowed internal error to the diagnostics channel (SDKDIAG-07).\n *\n * Control flow is unchanged — the site still swallows, still returns the same\n * value. It is simply no longer invisible to us.\n */\nfunction reportSwallowed(err: unknown, fingerprint: string): void {\n try {\n report(err, { fingerprint });\n } catch {\n // Reporting must never break the swallow it is reporting on.\n }\n}\n"]}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { setActiveFramework, captureInputMessages, extractOpenAiOutputText } from '../chunk-NH54DAJP.js';
|
|
1
2
|
import { calculateCost } from '../chunk-4FALQMOZ.js';
|
|
2
|
-
import { decomposeOpenAiMessages } from '../chunk-
|
|
3
|
-
import { init_tracing, getActiveTraceState, runAsync, startSpan } from '../chunk-
|
|
3
|
+
import { decomposeOpenAiMessages } from '../chunk-MLDVXMQD.js';
|
|
4
|
+
import { init_tracing, init_diagnostics, getActiveTraceState, runAsync, startSpan, report } from '../chunk-OOGAYEJF.js';
|
|
4
5
|
|
|
5
6
|
// src/openai/index.ts
|
|
6
7
|
init_tracing();
|
|
8
|
+
init_diagnostics();
|
|
7
9
|
var WRAPPED_CLIENT = /* @__PURE__ */ Symbol.for("morse.openai.wrapped");
|
|
8
10
|
function wrapOpenAI(client, options = {}) {
|
|
9
11
|
if (!client || typeof client !== "object") return client;
|
|
@@ -47,6 +49,7 @@ function buildPatchedCreate(original, options) {
|
|
|
47
49
|
return async function patchedCreate(params, callOptions) {
|
|
48
50
|
const inTrace = getActiveTraceState() !== null;
|
|
49
51
|
const work = async () => {
|
|
52
|
+
setActiveFramework("openai");
|
|
50
53
|
const handle = startSpan({
|
|
51
54
|
name: "openai.chat.completions.create",
|
|
52
55
|
type: "llm",
|
|
@@ -83,8 +86,13 @@ function buildPatchedCreate(original, options) {
|
|
|
83
86
|
if (cost !== void 0) handle.span.cost_usd = cost;
|
|
84
87
|
}
|
|
85
88
|
handle.span.context_segments = segments;
|
|
89
|
+
const inputData = captureInputMessages(params?.messages);
|
|
90
|
+
if (inputData !== void 0) handle.span.input_data = inputData;
|
|
91
|
+
const outputText = extractOpenAiOutputText(response?.choices);
|
|
92
|
+
if (outputText !== void 0) handle.span.output_data = { content: outputText };
|
|
86
93
|
}
|
|
87
|
-
} catch {
|
|
94
|
+
} catch (err) {
|
|
95
|
+
reportSwallowed(err, "morse.openai.stream_wrap");
|
|
88
96
|
}
|
|
89
97
|
handle?.end();
|
|
90
98
|
return response;
|
|
@@ -116,6 +124,12 @@ function safeExtractSegments(params) {
|
|
|
116
124
|
return [];
|
|
117
125
|
}
|
|
118
126
|
}
|
|
127
|
+
function reportSwallowed(err, fingerprint) {
|
|
128
|
+
try {
|
|
129
|
+
report(err, { fingerprint });
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
119
133
|
|
|
120
134
|
export { isOpenAiWrapped, wrapOpenAI };
|
|
121
135
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/openai/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * `@morsehq-dev/sdk/openai` — plain OpenAI SDK instrumentation.\n *\n * Wraps a raw `openai` npm package client (`new OpenAI()` /\n * `new AzureOpenAI()` / any structurally-compatible client) so every\n * `chat.completions.create` call emits an `llm` span carrying model,\n * token counts, `cost_usd`, and pre-segmented `context_segments`.\n *\n * TS port of `apps/sdk/python/src/morse_ai/adapters/openai.py`'s `wrap()`.\n * This module intentionally covers only the `wrap(client)` recommended\n * path (mirrors the Python module's three-layer doc comment, layer 2) —\n * no module-level monkey-patch / `install()` layer, since plain `openai`\n * is not one of the auto-detect targets on either SDK (see\n * `_internal/auto-detect.ts` and Python's `_internal/auto_detect.py`\n * `_adapter_targets()`, which both omit bare `openai`).\n *\n * Distinct from `@morsehq-dev/sdk/openai-agents`, which instruments the\n * `@openai/agents` Agents SDK (a different package with its own Runner /\n * Agent abstractions). This module is for customers calling\n * `openai.chat.completions.create` directly.\n *\n * Context segments are extracted via the shared\n * `_agent-sdk-common/context-decomposer.ts` (`decomposeOpenAiMessages`,\n * MHQ-750) — the request's `messages`/`tools` are already in the native\n * OpenAI wire format the decomposer expects, so no translation layer is\n * needed.\n *\n * Idempotent — wrapping an already-wrapped client instance is a no-op\n * that returns the same wrapper. Recording is best-effort: any failure\n * while building/emitting the span is swallowed so it can never break the\n * customer's OpenAI call. The customer's own API error (thrown by the\n * underlying `create()` call) always propagates unchanged — the span is\n * marked `status: \"failed\"` first, then the error is re-raised.\n *\n * Mirrors `apps/sdk/typescript/src/anthropic/index.ts` in shape.\n */\n\nimport { decomposeOpenAiMessages } from \"../_agent-sdk-common/context-decomposer\";\nimport { calculateCost } from \"../_internal/pricing\";\nimport type { ContextSegment } from \"../_internal/spans\";\nimport { getActiveTraceState, runAsync as _runAsync, startSpan } from \"../_internal/tracing\";\n\n/* ------------------------------------------------------------------------ */\n/* Public types — minimal structural shapes, NOT a hard dep on `openai` */\n/* ------------------------------------------------------------------------ */\n\n/** Subset of the `openai` package's chat message shape we read from. */\nexport interface OpenAiChatMessageParam {\n role: string;\n content?: unknown;\n tool_call_id?: string;\n [key: string]: unknown;\n}\n\n/** Subset of the `openai` package's tool-definition shape we read from. */\nexport interface OpenAiToolParam {\n type?: string;\n function?: { name?: string; [key: string]: unknown };\n [key: string]: unknown;\n}\n\n/**\n * Subset of `openai`'s `ChatCompletionCreateParams` we read from. We\n * deliberately decline to import the real types so the SDK works even when\n * `openai` isn't installed (it's an optional peer dep).\n */\nexport interface OpenAiChatCompletionCreateParams {\n model: string;\n messages: OpenAiChatMessageParam[];\n tools?: OpenAiToolParam[];\n // Allow forward-compat fields without complaint.\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsageDetails {\n cached_tokens?: number;\n reasoning_tokens?: number;\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n prompt_tokens_details?: OpenAiCompletionUsageDetails;\n completion_tokens_details?: OpenAiCompletionUsageDetails;\n [key: string]: unknown;\n}\n\nexport interface OpenAiChatCompletionResponse {\n id?: string;\n model?: string;\n usage?: OpenAiCompletionUsage;\n choices?: Array<{ message?: { content?: unknown; [k: string]: unknown }; [k: string]: unknown }>;\n [key: string]: unknown;\n}\n\n/**\n * Structural shape of the plain OpenAI client we wrap. We only require\n * that `client.chat.completions.create` is a function returning a Promise.\n */\nexport interface OpenAiClientLike {\n chat: {\n completions: {\n create(\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ): Promise<OpenAiChatCompletionResponse>;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level sentinel */\n/* ------------------------------------------------------------------------ */\n\nconst WRAPPED_CLIENT = Symbol.for(\"morse.openai.wrapped\");\n\n/* ------------------------------------------------------------------------ */\n/* Public API */\n/* ------------------------------------------------------------------------ */\n\nexport interface WrapOpenAiOptions {\n /**\n * When provided, used as the parent agent span's name when\n * `chat.completions.create` is called outside an active trace. Defaults\n * to `\"openai.chat.completions.create\"`.\n */\n defaultAgentName?: string;\n}\n\n/**\n * Wrap an OpenAI client so every `chat.completions.create` call records an\n * `llm` span with `model`, token counts, `cost_usd`, and `context_segments`.\n * The wrapper preserves the rest of the client's surface (other resources,\n * helpers, etc.) via a Proxy.\n *\n * Idempotent — a previously wrapped client is returned as-is.\n *\n * @example\n * ```ts\n * import { wrapOpenAI } from \"@morsehq-dev/sdk/openai\";\n * import OpenAI from \"openai\";\n *\n * const client = wrapOpenAI(new OpenAI());\n * const response = await client.chat.completions.create({\n * model: \"gpt-4o-mini\",\n * messages: [{ role: \"user\", content: \"hello\" }],\n * });\n * ```\n */\nexport function wrapOpenAI<C extends OpenAiClientLike>(\n client: C,\n options: WrapOpenAiOptions = {},\n): C {\n if (!client || typeof client !== \"object\") return client;\n // Already wrapped? Pass through.\n if ((client as Record<string | symbol, unknown>)[WRAPPED_CLIENT]) return client;\n\n const originalCreate = client.chat?.completions?.create?.bind(client.chat.completions);\n if (typeof originalCreate !== \"function\") return client;\n\n const patchedCreate = buildPatchedCreate(originalCreate, options);\n\n // Construct a new `completions` object that proxies to the original for\n // any non-`create` access, wrapped in a `chat` proxy, wrapped in a\n // top-level client proxy — mirrors `wrapAnthropic`'s nested-Proxy shape.\n const patchedCompletions = new Proxy(client.chat.completions, {\n get(target, prop, receiver) {\n if (prop === \"create\") return patchedCreate;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const patchedChat = new Proxy(client.chat, {\n get(target, prop, receiver) {\n if (prop === \"completions\") return patchedCompletions;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const wrapper = new Proxy(client, {\n get(target, prop, receiver) {\n if (prop === WRAPPED_CLIENT) return true;\n if (prop === \"chat\") return patchedChat;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n return wrapper as C;\n}\n\n/** True when a wrapped OpenAI client is passed in. */\nexport function isOpenAiWrapped(client: unknown): boolean {\n if (!client || typeof client !== \"object\") return false;\n return (client as Record<string | symbol, unknown>)[WRAPPED_CLIENT] === true;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Internals — wrapper that records an `llm` span */\n/* ------------------------------------------------------------------------ */\n\nfunction buildPatchedCreate(\n original: (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ) => Promise<OpenAiChatCompletionResponse>,\n options: WrapOpenAiOptions,\n): (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n) => Promise<OpenAiChatCompletionResponse> {\n const agentName = options.defaultAgentName ?? \"openai.chat.completions.create\";\n\n return async function patchedCreate(\n this: unknown,\n params: OpenAiChatCompletionCreateParams,\n callOptions?: unknown,\n ): Promise<OpenAiChatCompletionResponse> {\n const inTrace = getActiveTraceState() !== null;\n\n const work = async (): Promise<OpenAiChatCompletionResponse> => {\n const handle = startSpan({\n name: \"openai.chat.completions.create\",\n type: \"llm\",\n metadata: { model: params?.model },\n });\n let response: OpenAiChatCompletionResponse | undefined;\n try {\n response = await original.call(this, params, callOptions);\n } catch (err) {\n // Customer's own API error — mark the span failed, then re-raise\n // unchanged. Never swallow the caller's real error.\n handle?.end(err);\n throw err;\n }\n\n // Recording is best-effort — never let it throw at the caller.\n try {\n if (handle) {\n const segments = safeExtractSegments(params);\n const usage = response?.usage ?? {};\n const inputTokens = usage.prompt_tokens ?? 0;\n const outputTokens = usage.completion_tokens ?? 0;\n const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const model = response?.model ?? params?.model;\n\n handle.span.model = model;\n if (inputTokens > 0) handle.span.input_tokens = inputTokens;\n if (outputTokens > 0) handle.span.output_tokens = outputTokens;\n if (cachedTokens > 0) handle.span.cached_input_tokens = cachedTokens;\n const totalTokens =\n usage.total_tokens ??\n (inputTokens > 0 || outputTokens > 0 ? inputTokens + outputTokens : 0);\n if (totalTokens > 0) handle.span.total_tokens = totalTokens;\n\n if (model && (inputTokens > 0 || outputTokens > 0)) {\n const cost = calculateCost({\n inputTokens,\n outputTokens,\n model,\n cachedInputTokens: cachedTokens,\n });\n if (cost !== undefined) handle.span.cost_usd = cost;\n }\n handle.span.context_segments = segments;\n }\n } catch {\n // swallow — telemetry must never break the caller\n }\n handle?.end();\n return response;\n };\n\n if (inTrace) {\n return work();\n }\n\n // No active trace — open a fresh agent span around the call so the\n // llm span has a parent. Mirrors `wrapAnthropic`'s pattern: we can't\n // return `work()`'s Promise from inside `_runAsync`'s callback while\n // preserving typing AND having the fresh trace own the lifecycle, so\n // we wrap explicitly.\n let result: OpenAiChatCompletionResponse | undefined;\n let thrown: unknown;\n await _runAsync({ agentName }, async (runHandle) => {\n runHandle.setOutcome(true);\n try {\n result = await work();\n } catch (err) {\n thrown = err;\n throw err;\n }\n return null;\n });\n if (thrown !== undefined) throw thrown;\n return result as OpenAiChatCompletionResponse;\n };\n}\n\nfunction safeExtractSegments(params: OpenAiChatCompletionCreateParams): ContextSegment[] {\n try {\n if (!params || typeof params !== \"object\") return [];\n return decomposeOpenAiMessages(params.messages, params.tools);\n } catch {\n return [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/openai/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * `@morsehq-dev/sdk/openai` — plain OpenAI SDK instrumentation.\n *\n * Wraps a raw `openai` npm package client (`new OpenAI()` /\n * `new AzureOpenAI()` / any structurally-compatible client) so every\n * `chat.completions.create` call emits an `llm` span carrying model,\n * token counts, `cost_usd`, and pre-segmented `context_segments`.\n *\n * TS port of `apps/sdk/python/src/morse_ai/adapters/openai.py`'s `wrap()`.\n * This module intentionally covers only the `wrap(client)` recommended\n * path (mirrors the Python module's three-layer doc comment, layer 2) —\n * no module-level monkey-patch / `install()` layer, since plain `openai`\n * is not one of the auto-detect targets on either SDK (see\n * `_internal/auto-detect.ts` and Python's `_internal/auto_detect.py`\n * `_adapter_targets()`, which both omit bare `openai`).\n *\n * Distinct from `@morsehq-dev/sdk/openai-agents`, which instruments the\n * `@openai/agents` Agents SDK (a different package with its own Runner /\n * Agent abstractions). This module is for customers calling\n * `openai.chat.completions.create` directly.\n *\n * Context segments are extracted via the shared\n * `_agent-sdk-common/context-decomposer.ts` (`decomposeOpenAiMessages`,\n * MHQ-750) — the request's `messages`/`tools` are already in the native\n * OpenAI wire format the decomposer expects, so no translation layer is\n * needed.\n *\n * Idempotent — wrapping an already-wrapped client instance is a no-op\n * that returns the same wrapper. Recording is best-effort: any failure\n * while building/emitting the span is swallowed so it can never break the\n * customer's OpenAI call. The customer's own API error (thrown by the\n * underlying `create()` call) always propagates unchanged — the span is\n * marked `status: \"failed\"` first, then the error is re-raised.\n *\n * Mirrors `apps/sdk/typescript/src/anthropic/index.ts` in shape.\n */\n\nimport { decomposeOpenAiMessages } from \"../_agent-sdk-common/context-decomposer\";\nimport { setActiveFramework } from \"../_internal/active-framework\";\nimport { captureInputMessages, extractOpenAiOutputText } from \"../_internal/llm-io\";\nimport { calculateCost } from \"../_internal/pricing\";\nimport type { ContextSegment } from \"../_internal/spans\";\nimport { getActiveTraceState, runAsync as _runAsync, startSpan } from \"../_internal/tracing\";\nimport { report } from \"../_internal/diagnostics\";\n\n/* ------------------------------------------------------------------------ */\n/* Public types — minimal structural shapes, NOT a hard dep on `openai` */\n/* ------------------------------------------------------------------------ */\n\n/** Subset of the `openai` package's chat message shape we read from. */\nexport interface OpenAiChatMessageParam {\n role: string;\n content?: unknown;\n tool_call_id?: string;\n [key: string]: unknown;\n}\n\n/** Subset of the `openai` package's tool-definition shape we read from. */\nexport interface OpenAiToolParam {\n type?: string;\n function?: { name?: string; [key: string]: unknown };\n [key: string]: unknown;\n}\n\n/**\n * Subset of `openai`'s `ChatCompletionCreateParams` we read from. We\n * deliberately decline to import the real types so the SDK works even when\n * `openai` isn't installed (it's an optional peer dep).\n */\nexport interface OpenAiChatCompletionCreateParams {\n model: string;\n messages: OpenAiChatMessageParam[];\n tools?: OpenAiToolParam[];\n // Allow forward-compat fields without complaint.\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsageDetails {\n cached_tokens?: number;\n reasoning_tokens?: number;\n [key: string]: unknown;\n}\n\nexport interface OpenAiCompletionUsage {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n prompt_tokens_details?: OpenAiCompletionUsageDetails;\n completion_tokens_details?: OpenAiCompletionUsageDetails;\n [key: string]: unknown;\n}\n\nexport interface OpenAiChatCompletionResponse {\n id?: string;\n model?: string;\n usage?: OpenAiCompletionUsage;\n choices?: Array<{ message?: { content?: unknown; [k: string]: unknown }; [k: string]: unknown }>;\n [key: string]: unknown;\n}\n\n/**\n * Structural shape of the plain OpenAI client we wrap. We only require\n * that `client.chat.completions.create` is a function returning a Promise.\n */\nexport interface OpenAiClientLike {\n chat: {\n completions: {\n create(\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ): Promise<OpenAiChatCompletionResponse>;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Module-level sentinel */\n/* ------------------------------------------------------------------------ */\n\nconst WRAPPED_CLIENT = Symbol.for(\"morse.openai.wrapped\");\n\n/* ------------------------------------------------------------------------ */\n/* Public API */\n/* ------------------------------------------------------------------------ */\n\nexport interface WrapOpenAiOptions {\n /**\n * When provided, used as the parent agent span's name when\n * `chat.completions.create` is called outside an active trace. Defaults\n * to `\"openai.chat.completions.create\"`.\n */\n defaultAgentName?: string;\n}\n\n/**\n * Wrap an OpenAI client so every `chat.completions.create` call records an\n * `llm` span with `model`, token counts, `cost_usd`, and `context_segments`.\n * The wrapper preserves the rest of the client's surface (other resources,\n * helpers, etc.) via a Proxy.\n *\n * Idempotent — a previously wrapped client is returned as-is.\n *\n * @example\n * ```ts\n * import { wrapOpenAI } from \"@morsehq-dev/sdk/openai\";\n * import OpenAI from \"openai\";\n *\n * const client = wrapOpenAI(new OpenAI());\n * const response = await client.chat.completions.create({\n * model: \"gpt-4o-mini\",\n * messages: [{ role: \"user\", content: \"hello\" }],\n * });\n * ```\n */\nexport function wrapOpenAI<C extends OpenAiClientLike>(\n client: C,\n options: WrapOpenAiOptions = {},\n): C {\n if (!client || typeof client !== \"object\") return client;\n // Already wrapped? Pass through.\n if ((client as Record<string | symbol, unknown>)[WRAPPED_CLIENT]) return client;\n\n const originalCreate = client.chat?.completions?.create?.bind(client.chat.completions);\n if (typeof originalCreate !== \"function\") return client;\n\n const patchedCreate = buildPatchedCreate(originalCreate, options);\n\n // Construct a new `completions` object that proxies to the original for\n // any non-`create` access, wrapped in a `chat` proxy, wrapped in a\n // top-level client proxy — mirrors `wrapAnthropic`'s nested-Proxy shape.\n const patchedCompletions = new Proxy(client.chat.completions, {\n get(target, prop, receiver) {\n if (prop === \"create\") return patchedCreate;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const patchedChat = new Proxy(client.chat, {\n get(target, prop, receiver) {\n if (prop === \"completions\") return patchedCompletions;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n const wrapper = new Proxy(client, {\n get(target, prop, receiver) {\n if (prop === WRAPPED_CLIENT) return true;\n if (prop === \"chat\") return patchedChat;\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") return (value as (...a: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n\n return wrapper as C;\n}\n\n/** True when a wrapped OpenAI client is passed in. */\nexport function isOpenAiWrapped(client: unknown): boolean {\n if (!client || typeof client !== \"object\") return false;\n return (client as Record<string | symbol, unknown>)[WRAPPED_CLIENT] === true;\n}\n\n/* ------------------------------------------------------------------------ */\n/* Internals — wrapper that records an `llm` span */\n/* ------------------------------------------------------------------------ */\n\nfunction buildPatchedCreate(\n original: (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n ) => Promise<OpenAiChatCompletionResponse>,\n options: WrapOpenAiOptions,\n): (\n params: OpenAiChatCompletionCreateParams,\n options?: unknown,\n) => Promise<OpenAiChatCompletionResponse> {\n const agentName = options.defaultAgentName ?? \"openai.chat.completions.create\";\n\n return async function patchedCreate(\n this: unknown,\n params: OpenAiChatCompletionCreateParams,\n callOptions?: unknown,\n ): Promise<OpenAiChatCompletionResponse> {\n const inTrace = getActiveTraceState() !== null;\n\n const work = async (): Promise<OpenAiChatCompletionResponse> => {\n setActiveFramework(\"openai\");\n const handle = startSpan({\n name: \"openai.chat.completions.create\",\n type: \"llm\",\n metadata: { model: params?.model },\n });\n let response: OpenAiChatCompletionResponse | undefined;\n try {\n response = await original.call(this, params, callOptions);\n } catch (err) {\n // Customer's own API error — mark the span failed, then re-raise\n // unchanged. Never swallow the caller's real error.\n handle?.end(err);\n throw err;\n }\n\n // Recording is best-effort — never let it throw at the caller.\n try {\n if (handle) {\n const segments = safeExtractSegments(params);\n const usage = response?.usage ?? {};\n const inputTokens = usage.prompt_tokens ?? 0;\n const outputTokens = usage.completion_tokens ?? 0;\n const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;\n const model = response?.model ?? params?.model;\n\n handle.span.model = model;\n if (inputTokens > 0) handle.span.input_tokens = inputTokens;\n if (outputTokens > 0) handle.span.output_tokens = outputTokens;\n if (cachedTokens > 0) handle.span.cached_input_tokens = cachedTokens;\n const totalTokens =\n usage.total_tokens ??\n (inputTokens > 0 || outputTokens > 0 ? inputTokens + outputTokens : 0);\n if (totalTokens > 0) handle.span.total_tokens = totalTokens;\n\n if (model && (inputTokens > 0 || outputTokens > 0)) {\n const cost = calculateCost({\n inputTokens,\n outputTokens,\n model,\n cachedInputTokens: cachedTokens,\n });\n if (cost !== undefined) handle.span.cost_usd = cost;\n }\n handle.span.context_segments = segments;\n // MHQ-858: prompt/response capture, parity with the Python\n // adapter's input_data/output_data.\n const inputData = captureInputMessages(params?.messages);\n if (inputData !== undefined) handle.span.input_data = inputData;\n const outputText = extractOpenAiOutputText(response?.choices);\n if (outputText !== undefined) handle.span.output_data = { content: outputText };\n }\n } catch (err) {\n // swallow — telemetry must never break the caller\n reportSwallowed(err, \"morse.openai.stream_wrap\");\n }\n handle?.end();\n return response;\n };\n\n if (inTrace) {\n return work();\n }\n\n // No active trace — open a fresh agent span around the call so the\n // llm span has a parent. Mirrors `wrapAnthropic`'s pattern: we can't\n // return `work()`'s Promise from inside `_runAsync`'s callback while\n // preserving typing AND having the fresh trace own the lifecycle, so\n // we wrap explicitly.\n let result: OpenAiChatCompletionResponse | undefined;\n let thrown: unknown;\n await _runAsync({ agentName }, async (runHandle) => {\n runHandle.setOutcome(true);\n try {\n result = await work();\n } catch (err) {\n thrown = err;\n throw err;\n }\n return null;\n });\n if (thrown !== undefined) throw thrown;\n return result as OpenAiChatCompletionResponse;\n };\n}\n\nfunction safeExtractSegments(params: OpenAiChatCompletionCreateParams): ContextSegment[] {\n try {\n if (!params || typeof params !== \"object\") return [];\n return decomposeOpenAiMessages(params.messages, params.tools);\n } catch {\n return [];\n }\n}\n\n/**\n * Route a swallowed internal error to the diagnostics channel (SDKDIAG-07).\n *\n * Control flow is unchanged — the site still swallows, still returns the same\n * value. It is simply no longer invisible to us.\n */\nfunction reportSwallowed(err: unknown, fingerprint: string): void {\n try {\n report(err, { fingerprint });\n } catch {\n // Reporting must never break the swallow it is reporting on.\n }\n}\n"]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var chunkN3PXJCOE_cjs = require('../chunk-N3PXJCOE.cjs');
|
|
4
|
+
var chunk63ZI6PGP_cjs = require('../chunk-63ZI6PGP.cjs');
|
|
5
|
+
var chunkYMEU6XQQ_cjs = require('../chunk-YMEU6XQQ.cjs');
|
|
6
6
|
var chunkSWQOPFE4_cjs = require('../chunk-SWQOPFE4.cjs');
|
|
7
|
-
var
|
|
8
|
-
var
|
|
7
|
+
var chunkRBNCTHK3_cjs = require('../chunk-RBNCTHK3.cjs');
|
|
8
|
+
var chunkA5EYWAOT_cjs = require('../chunk-A5EYWAOT.cjs');
|
|
9
9
|
|
|
10
10
|
// src/openai-agents/runner-wrapper.ts
|
|
11
|
-
|
|
11
|
+
chunkA5EYWAOT_cjs.init_tracing();
|
|
12
12
|
var WRAPPED_RUNNER = /* @__PURE__ */ Symbol.for("morse.openai-agents.wrapped");
|
|
13
13
|
var INSTALL_SENTINEL = /* @__PURE__ */ Symbol.for("morse.openai-agents.installed");
|
|
14
14
|
var ORIGINALS_SENTINEL = /* @__PURE__ */ Symbol.for("morse.openai-agents.originals");
|
|
@@ -34,7 +34,7 @@ function wrapRunner(runner, options = {}) {
|
|
|
34
34
|
});
|
|
35
35
|
return wrapper;
|
|
36
36
|
}
|
|
37
|
-
var instrumentOpenAIAgents =
|
|
37
|
+
var instrumentOpenAIAgents = chunkYMEU6XQQ_cjs.absorbErrorsAsync(
|
|
38
38
|
async (runnerOrOptions = void 0, maybeOptions = {}) => {
|
|
39
39
|
if (runnerOrOptions && isRunner(runnerOrOptions)) {
|
|
40
40
|
const opts2 = maybeOptions ?? {};
|
|
@@ -67,7 +67,7 @@ var instrumentOpenAIAgents = chunk3BGG7GH3_cjs.absorbErrorsAsync(
|
|
|
67
67
|
},
|
|
68
68
|
{ fingerprint: "morse.adapters.openai-agents.install" }
|
|
69
69
|
);
|
|
70
|
-
var uninstallOpenAIAgents =
|
|
70
|
+
var uninstallOpenAIAgents = chunkYMEU6XQQ_cjs.absorbErrorsSync(
|
|
71
71
|
(openaiAgentsModule) => {
|
|
72
72
|
if (!openaiAgentsModule || typeof openaiAgentsModule !== "object") return;
|
|
73
73
|
const mod = openaiAgentsModule;
|
|
@@ -120,7 +120,7 @@ function buildPatchedRun(original, options) {
|
|
|
120
120
|
const agentName = (agent && typeof agent === "object" && typeof agent.name === "string" && agent.name.length > 0 ? agent.name : void 0) ?? options.defaultAgentName ?? "openai-agent";
|
|
121
121
|
const model = stringifyModel(agent?.model);
|
|
122
122
|
const runWithSpan = async () => {
|
|
123
|
-
const handle =
|
|
123
|
+
const handle = chunk63ZI6PGP_cjs.emitAgentSpan({
|
|
124
124
|
adapter: "openai-agents",
|
|
125
125
|
agentName,
|
|
126
126
|
model
|
|
@@ -138,13 +138,13 @@ function buildPatchedRun(original, options) {
|
|
|
138
138
|
throw err;
|
|
139
139
|
}
|
|
140
140
|
};
|
|
141
|
-
if (
|
|
141
|
+
if (chunkA5EYWAOT_cjs.getActiveTraceState() !== null) {
|
|
142
142
|
return runWithSpan();
|
|
143
143
|
}
|
|
144
144
|
return (async () => {
|
|
145
145
|
let captured;
|
|
146
146
|
let caughtErr;
|
|
147
|
-
const runResult = await
|
|
147
|
+
const runResult = await chunkA5EYWAOT_cjs.runAsync({ agentName }, async (runHandle) => {
|
|
148
148
|
try {
|
|
149
149
|
captured = await runWithSpan();
|
|
150
150
|
runHandle.setOutcome(true);
|
|
@@ -173,10 +173,10 @@ function installLlmAndToolInstrumentation(runner, options) {
|
|
|
173
173
|
const opts = options ?? {};
|
|
174
174
|
const llmStack = [];
|
|
175
175
|
const toolStack = [];
|
|
176
|
-
const onLlmStart =
|
|
176
|
+
const onLlmStart = chunkYMEU6XQQ_cjs.absorbErrorsSync(
|
|
177
177
|
(event) => {
|
|
178
|
-
const contextSegments =
|
|
179
|
-
const handle =
|
|
178
|
+
const contextSegments = chunkRBNCTHK3_cjs.decomposeOpenAiMessages(event.llm?.messages, event.llm?.tools);
|
|
179
|
+
const handle = chunk63ZI6PGP_cjs.emitLlmSpan({
|
|
180
180
|
adapter: ADAPTER,
|
|
181
181
|
model: typeof event.llm?.model === "string" && event.llm.model.length > 0 ? event.llm.model : "unknown",
|
|
182
182
|
provider: typeof event.llm?.provider === "string" ? event.llm.provider : void 0,
|
|
@@ -186,7 +186,7 @@ function installLlmAndToolInstrumentation(runner, options) {
|
|
|
186
186
|
},
|
|
187
187
|
{ fingerprint: "morse.openai-agents.llm_call_start" }
|
|
188
188
|
);
|
|
189
|
-
const onLlmEnd =
|
|
189
|
+
const onLlmEnd = chunkYMEU6XQQ_cjs.absorbErrorsSync(
|
|
190
190
|
(event) => {
|
|
191
191
|
const handle = llmStack.shift();
|
|
192
192
|
if (handle === void 0) return;
|
|
@@ -218,14 +218,14 @@ function installLlmAndToolInstrumentation(runner, options) {
|
|
|
218
218
|
},
|
|
219
219
|
{ fingerprint: "morse.openai-agents.llm_call_end" }
|
|
220
220
|
);
|
|
221
|
-
const onToolStart =
|
|
221
|
+
const onToolStart = chunkYMEU6XQQ_cjs.absorbErrorsSync(
|
|
222
222
|
(event) => {
|
|
223
223
|
const toolName = typeof event.tool?.name === "string" && event.tool.name.length > 0 ? event.tool.name : "unknown";
|
|
224
|
-
const inputSummary =
|
|
224
|
+
const inputSummary = chunkN3PXJCOE_cjs.summarizeToolInput(
|
|
225
225
|
event.tool?.input,
|
|
226
226
|
opts.toolInputMaxBytes !== void 0 ? { maxBytes: opts.toolInputMaxBytes } : void 0
|
|
227
227
|
);
|
|
228
|
-
const handle =
|
|
228
|
+
const handle = chunk63ZI6PGP_cjs.emitToolSpan({
|
|
229
229
|
adapter: ADAPTER,
|
|
230
230
|
toolName,
|
|
231
231
|
inputSummary
|
|
@@ -234,11 +234,11 @@ function installLlmAndToolInstrumentation(runner, options) {
|
|
|
234
234
|
},
|
|
235
235
|
{ fingerprint: "morse.openai-agents.tool_call_start" }
|
|
236
236
|
);
|
|
237
|
-
const onToolEnd =
|
|
237
|
+
const onToolEnd = chunkYMEU6XQQ_cjs.absorbErrorsSync(
|
|
238
238
|
(event) => {
|
|
239
239
|
const handle = toolStack.shift();
|
|
240
240
|
if (handle === void 0) return;
|
|
241
|
-
const outputSummary =
|
|
241
|
+
const outputSummary = chunkN3PXJCOE_cjs.summarizeToolOutput(
|
|
242
242
|
event.tool?.output,
|
|
243
243
|
opts.toolOutputMaxBytes !== void 0 ? { maxBytes: opts.toolOutputMaxBytes } : void 0
|
|
244
244
|
);
|
|
@@ -292,7 +292,7 @@ var _handleHandoffEvent = (event) => {
|
|
|
292
292
|
handoff_from: from
|
|
293
293
|
};
|
|
294
294
|
if (reason !== void 0) attributes.handoff_reason = reason;
|
|
295
|
-
const handle =
|
|
295
|
+
const handle = chunk63ZI6PGP_cjs.emitSubagentSpan({
|
|
296
296
|
adapter: "openai-agents",
|
|
297
297
|
parentAgentName: from,
|
|
298
298
|
subagentName: to,
|
|
@@ -301,7 +301,7 @@ var _handleHandoffEvent = (event) => {
|
|
|
301
301
|
});
|
|
302
302
|
handle?.end();
|
|
303
303
|
};
|
|
304
|
-
var handleHandoffEvent =
|
|
304
|
+
var handleHandoffEvent = chunkYMEU6XQQ_cjs.absorbErrorsSync(_handleHandoffEvent, {
|
|
305
305
|
fingerprint: "morse.adapters.openai-agents.handoff.listener"
|
|
306
306
|
});
|
|
307
307
|
function installHandoffInstrumentation(runner) {
|
|
@@ -338,7 +338,7 @@ var _handleGuardrailEvent = (payload) => {
|
|
|
338
338
|
const guardrailMeta = { passed };
|
|
339
339
|
if (name !== void 0) guardrailMeta.name = name;
|
|
340
340
|
if (reason !== void 0) guardrailMeta.reason = reason;
|
|
341
|
-
const handle =
|
|
341
|
+
const handle = chunk63ZI6PGP_cjs.emitToolSpan({
|
|
342
342
|
adapter: "openai-agents",
|
|
343
343
|
toolName: `guardrail.${name ?? "anon"}`,
|
|
344
344
|
success: passed
|
|
@@ -355,7 +355,7 @@ var _handleGuardrailEvent = (payload) => {
|
|
|
355
355
|
handle.end(new Error(`guardrail failed: ${reason ?? "no reason"}`));
|
|
356
356
|
}
|
|
357
357
|
};
|
|
358
|
-
var handleGuardrailEvent =
|
|
358
|
+
var handleGuardrailEvent = chunkYMEU6XQQ_cjs.absorbErrorsSync(
|
|
359
359
|
_handleGuardrailEvent,
|
|
360
360
|
{ fingerprint: "morse.openai-agents.guardrail.handle" }
|
|
361
361
|
);
|
|
@@ -384,6 +384,7 @@ function installGuardrailInstrumentation(runner) {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
// src/openai-agents/trace-processor.ts
|
|
387
|
+
chunkA5EYWAOT_cjs.init_diagnostics();
|
|
387
388
|
var ADAPTER2 = "openai-agents";
|
|
388
389
|
function safe(fn) {
|
|
389
390
|
try {
|
|
@@ -500,32 +501,32 @@ var MorseTraceProcessor = class {
|
|
|
500
501
|
const name = typeof d.name === "string" && d.name.length > 0 ? d.name : void 0;
|
|
501
502
|
switch (type) {
|
|
502
503
|
case "agent":
|
|
503
|
-
return
|
|
504
|
+
return chunk63ZI6PGP_cjs.emitAgentSpan({
|
|
504
505
|
adapter: ADAPTER2,
|
|
505
506
|
agentName: name ?? "openai-agent"
|
|
506
507
|
});
|
|
507
508
|
case "generation":
|
|
508
509
|
case "response": {
|
|
509
510
|
const model = typeof d.model === "string" && d.model.length > 0 ? d.model : "unknown";
|
|
510
|
-
return
|
|
511
|
+
return chunk63ZI6PGP_cjs.emitLlmSpan({ adapter: ADAPTER2, model });
|
|
511
512
|
}
|
|
512
513
|
case "function": {
|
|
513
514
|
const inputOpts = this.options.toolInputMaxBytes ? { maxBytes: this.options.toolInputMaxBytes } : void 0;
|
|
514
|
-
return
|
|
515
|
+
return chunk63ZI6PGP_cjs.emitToolSpan({
|
|
515
516
|
adapter: ADAPTER2,
|
|
516
517
|
toolName: name ?? "function",
|
|
517
|
-
inputSummary: d.input !== void 0 ?
|
|
518
|
+
inputSummary: d.input !== void 0 ? chunkN3PXJCOE_cjs.summarizeToolInput(d.input, inputOpts) : void 0
|
|
518
519
|
});
|
|
519
520
|
}
|
|
520
521
|
case "handoff":
|
|
521
|
-
return
|
|
522
|
+
return chunk63ZI6PGP_cjs.emitSubagentSpan({
|
|
522
523
|
adapter: ADAPTER2,
|
|
523
524
|
parentAgentName: typeof d.from_agent === "string" ? d.from_agent : "unknown",
|
|
524
525
|
subagentName: typeof d.to_agent === "string" ? d.to_agent : name ?? "unknown",
|
|
525
526
|
taskDescription: typeof d.reason === "string" ? d.reason : void 0
|
|
526
527
|
});
|
|
527
528
|
case "guardrail": {
|
|
528
|
-
const handle =
|
|
529
|
+
const handle = chunk63ZI6PGP_cjs.emitToolSpan({
|
|
529
530
|
adapter: ADAPTER2,
|
|
530
531
|
toolName: `guardrail.${name ?? "anon"}`
|
|
531
532
|
});
|
|
@@ -543,17 +544,17 @@ var MorseTraceProcessor = class {
|
|
|
543
544
|
return handle;
|
|
544
545
|
}
|
|
545
546
|
case "mcp_list_tools":
|
|
546
|
-
return
|
|
547
|
+
return chunk63ZI6PGP_cjs.emitToolSpan({
|
|
547
548
|
adapter: ADAPTER2,
|
|
548
549
|
toolName: `mcp.list_tools.${name ?? "anon"}`
|
|
549
550
|
});
|
|
550
551
|
case "custom":
|
|
551
|
-
return
|
|
552
|
+
return chunk63ZI6PGP_cjs.emitToolSpan({
|
|
552
553
|
adapter: ADAPTER2,
|
|
553
554
|
toolName: `custom.${name ?? "anon"}`
|
|
554
555
|
});
|
|
555
556
|
default:
|
|
556
|
-
return
|
|
557
|
+
return chunk63ZI6PGP_cjs.emitToolSpan({
|
|
557
558
|
adapter: ADAPTER2,
|
|
558
559
|
toolName: `${type || "unknown"}.${name ?? "anon"}`
|
|
559
560
|
});
|
|
@@ -582,7 +583,7 @@ var MorseTraceProcessor = class {
|
|
|
582
583
|
const outputOpts = this.options.toolOutputMaxBytes ? { maxBytes: this.options.toolOutputMaxBytes } : void 0;
|
|
583
584
|
handle.span.metadata = {
|
|
584
585
|
...handle.span.metadata ?? {},
|
|
585
|
-
tool_output_summary:
|
|
586
|
+
tool_output_summary: chunkN3PXJCOE_cjs.summarizeToolOutput(d.output, outputOpts)
|
|
586
587
|
};
|
|
587
588
|
}
|
|
588
589
|
}
|
|
@@ -612,6 +613,13 @@ function safeEnd(handle, err) {
|
|
|
612
613
|
} else {
|
|
613
614
|
handle.end();
|
|
614
615
|
}
|
|
616
|
+
} catch (err2) {
|
|
617
|
+
reportSwallowed(err2, "morse.openai_agents.trace_processor");
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function reportSwallowed(err, fingerprint) {
|
|
621
|
+
try {
|
|
622
|
+
chunkA5EYWAOT_cjs.report(err, { fingerprint });
|
|
615
623
|
} catch {
|
|
616
624
|
}
|
|
617
625
|
}
|