@morsehq-dev/sdk 0.4.0-rc.1

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.
Files changed (161) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/dist/anthropic/index.cjs +39 -0
  4. package/dist/anthropic/index.cjs.map +1 -0
  5. package/dist/anthropic/index.d.cts +213 -0
  6. package/dist/anthropic/index.d.ts +213 -0
  7. package/dist/anthropic/index.js +6 -0
  8. package/dist/anthropic/index.js.map +1 -0
  9. package/dist/anthropic-agent-sdk/index.cjs +744 -0
  10. package/dist/anthropic-agent-sdk/index.cjs.map +1 -0
  11. package/dist/anthropic-agent-sdk/index.d.cts +371 -0
  12. package/dist/anthropic-agent-sdk/index.d.ts +371 -0
  13. package/dist/anthropic-agent-sdk/index.js +735 -0
  14. package/dist/anthropic-agent-sdk/index.js.map +1 -0
  15. package/dist/browser/anthropic/index.cjs +39 -0
  16. package/dist/browser/anthropic/index.cjs.map +1 -0
  17. package/dist/browser/anthropic/index.js +6 -0
  18. package/dist/browser/anthropic/index.js.map +1 -0
  19. package/dist/browser/anthropic-agent-sdk/index.cjs +744 -0
  20. package/dist/browser/anthropic-agent-sdk/index.cjs.map +1 -0
  21. package/dist/browser/anthropic-agent-sdk/index.js +735 -0
  22. package/dist/browser/anthropic-agent-sdk/index.js.map +1 -0
  23. package/dist/browser/chunk-3643DC7K.cjs +365 -0
  24. package/dist/browser/chunk-3643DC7K.cjs.map +1 -0
  25. package/dist/browser/chunk-4FALQMOZ.js +232 -0
  26. package/dist/browser/chunk-4FALQMOZ.js.map +1 -0
  27. package/dist/browser/chunk-5J2QBK75.js +884 -0
  28. package/dist/browser/chunk-5J2QBK75.js.map +1 -0
  29. package/dist/browser/chunk-7MSXWGAH.js +53 -0
  30. package/dist/browser/chunk-7MSXWGAH.js.map +1 -0
  31. package/dist/browser/chunk-A24L5N5N.js +596 -0
  32. package/dist/browser/chunk-A24L5N5N.js.map +1 -0
  33. package/dist/browser/chunk-B7DEUDP6.cjs +177 -0
  34. package/dist/browser/chunk-B7DEUDP6.cjs.map +1 -0
  35. package/dist/browser/chunk-F6CJACNH.cjs +604 -0
  36. package/dist/browser/chunk-F6CJACNH.cjs.map +1 -0
  37. package/dist/browser/chunk-JBOYFQSB.js +412 -0
  38. package/dist/browser/chunk-JBOYFQSB.js.map +1 -0
  39. package/dist/browser/chunk-KRBADE6R.cjs +183 -0
  40. package/dist/browser/chunk-KRBADE6R.cjs.map +1 -0
  41. package/dist/browser/chunk-MCJYZH6W.cjs +415 -0
  42. package/dist/browser/chunk-MCJYZH6W.cjs.map +1 -0
  43. package/dist/browser/chunk-NQG2IAQS.js +178 -0
  44. package/dist/browser/chunk-NQG2IAQS.js.map +1 -0
  45. package/dist/browser/chunk-O4HG3OSK.cjs +929 -0
  46. package/dist/browser/chunk-O4HG3OSK.cjs.map +1 -0
  47. package/dist/browser/chunk-QWRQJO57.js +363 -0
  48. package/dist/browser/chunk-QWRQJO57.js.map +1 -0
  49. package/dist/browser/chunk-SWQOPFE4.cjs +234 -0
  50. package/dist/browser/chunk-SWQOPFE4.cjs.map +1 -0
  51. package/dist/browser/chunk-TYDG747E.js +171 -0
  52. package/dist/browser/chunk-TYDG747E.js.map +1 -0
  53. package/dist/browser/chunk-TZRSDDFP.cjs +56 -0
  54. package/dist/browser/chunk-TZRSDDFP.cjs.map +1 -0
  55. package/dist/browser/index.cjs +587 -0
  56. package/dist/browser/index.cjs.map +1 -0
  57. package/dist/browser/index.js +522 -0
  58. package/dist/browser/index.js.map +1 -0
  59. package/dist/browser/integrations/pino.cjs +89 -0
  60. package/dist/browser/integrations/pino.cjs.map +1 -0
  61. package/dist/browser/integrations/pino.js +86 -0
  62. package/dist/browser/integrations/pino.js.map +1 -0
  63. package/dist/browser/langchain/index.cjs +535 -0
  64. package/dist/browser/langchain/index.cjs.map +1 -0
  65. package/dist/browser/langchain/index.js +528 -0
  66. package/dist/browser/langchain/index.js.map +1 -0
  67. package/dist/browser/langgraph/index.cjs +377 -0
  68. package/dist/browser/langgraph/index.cjs.map +1 -0
  69. package/dist/browser/langgraph/index.js +371 -0
  70. package/dist/browser/langgraph/index.js.map +1 -0
  71. package/dist/browser/openai/index.cjs +125 -0
  72. package/dist/browser/openai/index.cjs.map +1 -0
  73. package/dist/browser/openai/index.js +122 -0
  74. package/dist/browser/openai/index.js.map +1 -0
  75. package/dist/browser/openai-agents/index.cjs +689 -0
  76. package/dist/browser/openai-agents/index.cjs.map +1 -0
  77. package/dist/browser/openai-agents/index.js +678 -0
  78. package/dist/browser/openai-agents/index.js.map +1 -0
  79. package/dist/browser/vercel-ai/index.cjs +233 -0
  80. package/dist/browser/vercel-ai/index.cjs.map +1 -0
  81. package/dist/browser/vercel-ai/index.js +231 -0
  82. package/dist/browser/vercel-ai/index.js.map +1 -0
  83. package/dist/chunk-4R4SHGOK.js +363 -0
  84. package/dist/chunk-4R4SHGOK.js.map +1 -0
  85. package/dist/chunk-7EO7MQBA.cjs +183 -0
  86. package/dist/chunk-7EO7MQBA.cjs.map +1 -0
  87. package/dist/chunk-7YCENA54.cjs +604 -0
  88. package/dist/chunk-7YCENA54.cjs.map +1 -0
  89. package/dist/chunk-CKFOGDUF.js +596 -0
  90. package/dist/chunk-CKFOGDUF.js.map +1 -0
  91. package/dist/chunk-FJUNILZT.cjs +1324 -0
  92. package/dist/chunk-FJUNILZT.cjs.map +1 -0
  93. package/dist/chunk-HDAFUKQ3.js +171 -0
  94. package/dist/chunk-HDAFUKQ3.js.map +1 -0
  95. package/dist/chunk-KBWPNIH4.cjs +234 -0
  96. package/dist/chunk-KBWPNIH4.cjs.map +1 -0
  97. package/dist/chunk-KJEO52QS.cjs +365 -0
  98. package/dist/chunk-KJEO52QS.cjs.map +1 -0
  99. package/dist/chunk-KZBCOZIQ.cjs +177 -0
  100. package/dist/chunk-KZBCOZIQ.cjs.map +1 -0
  101. package/dist/chunk-ME5JALGT.js +53 -0
  102. package/dist/chunk-ME5JALGT.js.map +1 -0
  103. package/dist/chunk-PVHDEPRE.cjs +56 -0
  104. package/dist/chunk-PVHDEPRE.cjs.map +1 -0
  105. package/dist/chunk-RTL23YOQ.js +178 -0
  106. package/dist/chunk-RTL23YOQ.js.map +1 -0
  107. package/dist/chunk-TQWI4UYO.js +1277 -0
  108. package/dist/chunk-TQWI4UYO.js.map +1 -0
  109. package/dist/chunk-VXDBDPDR.cjs +415 -0
  110. package/dist/chunk-VXDBDPDR.cjs.map +1 -0
  111. package/dist/chunk-XTKMUJWI.js +232 -0
  112. package/dist/chunk-XTKMUJWI.js.map +1 -0
  113. package/dist/chunk-ZKUGOWER.js +412 -0
  114. package/dist/chunk-ZKUGOWER.js.map +1 -0
  115. package/dist/index.cjs +843 -0
  116. package/dist/index.cjs.map +1 -0
  117. package/dist/index.d.cts +464 -0
  118. package/dist/index.d.ts +464 -0
  119. package/dist/index.js +778 -0
  120. package/dist/index.js.map +1 -0
  121. package/dist/integrations/pino.cjs +89 -0
  122. package/dist/integrations/pino.cjs.map +1 -0
  123. package/dist/integrations/pino.d.cts +65 -0
  124. package/dist/integrations/pino.d.ts +65 -0
  125. package/dist/integrations/pino.js +86 -0
  126. package/dist/integrations/pino.js.map +1 -0
  127. package/dist/langchain/index.cjs +535 -0
  128. package/dist/langchain/index.cjs.map +1 -0
  129. package/dist/langchain/index.d.cts +265 -0
  130. package/dist/langchain/index.d.ts +265 -0
  131. package/dist/langchain/index.js +528 -0
  132. package/dist/langchain/index.js.map +1 -0
  133. package/dist/langgraph/index.cjs +377 -0
  134. package/dist/langgraph/index.cjs.map +1 -0
  135. package/dist/langgraph/index.d.cts +324 -0
  136. package/dist/langgraph/index.d.ts +324 -0
  137. package/dist/langgraph/index.js +371 -0
  138. package/dist/langgraph/index.js.map +1 -0
  139. package/dist/openai/index.cjs +125 -0
  140. package/dist/openai/index.cjs.map +1 -0
  141. package/dist/openai/index.d.cts +136 -0
  142. package/dist/openai/index.d.ts +136 -0
  143. package/dist/openai/index.js +122 -0
  144. package/dist/openai/index.js.map +1 -0
  145. package/dist/openai-agents/index.cjs +689 -0
  146. package/dist/openai-agents/index.cjs.map +1 -0
  147. package/dist/openai-agents/index.d.cts +502 -0
  148. package/dist/openai-agents/index.d.ts +502 -0
  149. package/dist/openai-agents/index.js +678 -0
  150. package/dist/openai-agents/index.js.map +1 -0
  151. package/dist/spans-DZtMuBvc.d.cts +73 -0
  152. package/dist/spans-DZtMuBvc.d.ts +73 -0
  153. package/dist/tracing-BYAqjT5Q.d.cts +114 -0
  154. package/dist/tracing-rz9cWQ8d.d.ts +114 -0
  155. package/dist/vercel-ai/index.cjs +233 -0
  156. package/dist/vercel-ai/index.cjs.map +1 -0
  157. package/dist/vercel-ai/index.d.cts +93 -0
  158. package/dist/vercel-ai/index.d.ts +93 -0
  159. package/dist/vercel-ai/index.js +231 -0
  160. package/dist/vercel-ai/index.js.map +1 -0
  161. package/package.json +182 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * `@morsehq-dev/sdk/openai` — plain OpenAI SDK instrumentation.
3
+ *
4
+ * Wraps a raw `openai` npm package client (`new OpenAI()` /
5
+ * `new AzureOpenAI()` / any structurally-compatible client) so every
6
+ * `chat.completions.create` call emits an `llm` span carrying model,
7
+ * token counts, `cost_usd`, and pre-segmented `context_segments`.
8
+ *
9
+ * TS port of `apps/sdk/python/src/morse_ai/adapters/openai.py`'s `wrap()`.
10
+ * This module intentionally covers only the `wrap(client)` recommended
11
+ * path (mirrors the Python module's three-layer doc comment, layer 2) —
12
+ * no module-level monkey-patch / `install()` layer, since plain `openai`
13
+ * is not one of the auto-detect targets on either SDK (see
14
+ * `_internal/auto-detect.ts` and Python's `_internal/auto_detect.py`
15
+ * `_adapter_targets()`, which both omit bare `openai`).
16
+ *
17
+ * Distinct from `@morsehq-dev/sdk/openai-agents`, which instruments the
18
+ * `@openai/agents` Agents SDK (a different package with its own Runner /
19
+ * Agent abstractions). This module is for customers calling
20
+ * `openai.chat.completions.create` directly.
21
+ *
22
+ * Context segments are extracted via the shared
23
+ * `_agent-sdk-common/context-decomposer.ts` (`decomposeOpenAiMessages`,
24
+ * MHQ-750) — the request's `messages`/`tools` are already in the native
25
+ * OpenAI wire format the decomposer expects, so no translation layer is
26
+ * needed.
27
+ *
28
+ * Idempotent — wrapping an already-wrapped client instance is a no-op
29
+ * that returns the same wrapper. Recording is best-effort: any failure
30
+ * while building/emitting the span is swallowed so it can never break the
31
+ * customer's OpenAI call. The customer's own API error (thrown by the
32
+ * underlying `create()` call) always propagates unchanged — the span is
33
+ * marked `status: "failed"` first, then the error is re-raised.
34
+ *
35
+ * Mirrors `apps/sdk/typescript/src/anthropic/index.ts` in shape.
36
+ */
37
+ /** Subset of the `openai` package's chat message shape we read from. */
38
+ interface OpenAiChatMessageParam {
39
+ role: string;
40
+ content?: unknown;
41
+ tool_call_id?: string;
42
+ [key: string]: unknown;
43
+ }
44
+ /** Subset of the `openai` package's tool-definition shape we read from. */
45
+ interface OpenAiToolParam {
46
+ type?: string;
47
+ function?: {
48
+ name?: string;
49
+ [key: string]: unknown;
50
+ };
51
+ [key: string]: unknown;
52
+ }
53
+ /**
54
+ * Subset of `openai`'s `ChatCompletionCreateParams` we read from. We
55
+ * deliberately decline to import the real types so the SDK works even when
56
+ * `openai` isn't installed (it's an optional peer dep).
57
+ */
58
+ interface OpenAiChatCompletionCreateParams {
59
+ model: string;
60
+ messages: OpenAiChatMessageParam[];
61
+ tools?: OpenAiToolParam[];
62
+ [key: string]: unknown;
63
+ }
64
+ interface OpenAiCompletionUsageDetails {
65
+ cached_tokens?: number;
66
+ reasoning_tokens?: number;
67
+ [key: string]: unknown;
68
+ }
69
+ interface OpenAiCompletionUsage {
70
+ prompt_tokens?: number;
71
+ completion_tokens?: number;
72
+ total_tokens?: number;
73
+ prompt_tokens_details?: OpenAiCompletionUsageDetails;
74
+ completion_tokens_details?: OpenAiCompletionUsageDetails;
75
+ [key: string]: unknown;
76
+ }
77
+ interface OpenAiChatCompletionResponse {
78
+ id?: string;
79
+ model?: string;
80
+ usage?: OpenAiCompletionUsage;
81
+ choices?: Array<{
82
+ message?: {
83
+ content?: unknown;
84
+ [k: string]: unknown;
85
+ };
86
+ [k: string]: unknown;
87
+ }>;
88
+ [key: string]: unknown;
89
+ }
90
+ /**
91
+ * Structural shape of the plain OpenAI client we wrap. We only require
92
+ * that `client.chat.completions.create` is a function returning a Promise.
93
+ */
94
+ interface OpenAiClientLike {
95
+ chat: {
96
+ completions: {
97
+ create(params: OpenAiChatCompletionCreateParams, options?: unknown): Promise<OpenAiChatCompletionResponse>;
98
+ [key: string]: unknown;
99
+ };
100
+ [key: string]: unknown;
101
+ };
102
+ [key: string]: unknown;
103
+ }
104
+ interface WrapOpenAiOptions {
105
+ /**
106
+ * When provided, used as the parent agent span's name when
107
+ * `chat.completions.create` is called outside an active trace. Defaults
108
+ * to `"openai.chat.completions.create"`.
109
+ */
110
+ defaultAgentName?: string;
111
+ }
112
+ /**
113
+ * Wrap an OpenAI client so every `chat.completions.create` call records an
114
+ * `llm` span with `model`, token counts, `cost_usd`, and `context_segments`.
115
+ * The wrapper preserves the rest of the client's surface (other resources,
116
+ * helpers, etc.) via a Proxy.
117
+ *
118
+ * Idempotent — a previously wrapped client is returned as-is.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import { wrapOpenAI } from "@morsehq-dev/sdk/openai";
123
+ * import OpenAI from "openai";
124
+ *
125
+ * const client = wrapOpenAI(new OpenAI());
126
+ * const response = await client.chat.completions.create({
127
+ * model: "gpt-4o-mini",
128
+ * messages: [{ role: "user", content: "hello" }],
129
+ * });
130
+ * ```
131
+ */
132
+ declare function wrapOpenAI<C extends OpenAiClientLike>(client: C, options?: WrapOpenAiOptions): C;
133
+ /** True when a wrapped OpenAI client is passed in. */
134
+ declare function isOpenAiWrapped(client: unknown): boolean;
135
+
136
+ export { type OpenAiChatCompletionCreateParams, type OpenAiChatCompletionResponse, type OpenAiChatMessageParam, type OpenAiClientLike, type OpenAiCompletionUsage, type OpenAiCompletionUsageDetails, type OpenAiToolParam, type WrapOpenAiOptions, isOpenAiWrapped, wrapOpenAI };
@@ -0,0 +1,136 @@
1
+ /**
2
+ * `@morsehq-dev/sdk/openai` — plain OpenAI SDK instrumentation.
3
+ *
4
+ * Wraps a raw `openai` npm package client (`new OpenAI()` /
5
+ * `new AzureOpenAI()` / any structurally-compatible client) so every
6
+ * `chat.completions.create` call emits an `llm` span carrying model,
7
+ * token counts, `cost_usd`, and pre-segmented `context_segments`.
8
+ *
9
+ * TS port of `apps/sdk/python/src/morse_ai/adapters/openai.py`'s `wrap()`.
10
+ * This module intentionally covers only the `wrap(client)` recommended
11
+ * path (mirrors the Python module's three-layer doc comment, layer 2) —
12
+ * no module-level monkey-patch / `install()` layer, since plain `openai`
13
+ * is not one of the auto-detect targets on either SDK (see
14
+ * `_internal/auto-detect.ts` and Python's `_internal/auto_detect.py`
15
+ * `_adapter_targets()`, which both omit bare `openai`).
16
+ *
17
+ * Distinct from `@morsehq-dev/sdk/openai-agents`, which instruments the
18
+ * `@openai/agents` Agents SDK (a different package with its own Runner /
19
+ * Agent abstractions). This module is for customers calling
20
+ * `openai.chat.completions.create` directly.
21
+ *
22
+ * Context segments are extracted via the shared
23
+ * `_agent-sdk-common/context-decomposer.ts` (`decomposeOpenAiMessages`,
24
+ * MHQ-750) — the request's `messages`/`tools` are already in the native
25
+ * OpenAI wire format the decomposer expects, so no translation layer is
26
+ * needed.
27
+ *
28
+ * Idempotent — wrapping an already-wrapped client instance is a no-op
29
+ * that returns the same wrapper. Recording is best-effort: any failure
30
+ * while building/emitting the span is swallowed so it can never break the
31
+ * customer's OpenAI call. The customer's own API error (thrown by the
32
+ * underlying `create()` call) always propagates unchanged — the span is
33
+ * marked `status: "failed"` first, then the error is re-raised.
34
+ *
35
+ * Mirrors `apps/sdk/typescript/src/anthropic/index.ts` in shape.
36
+ */
37
+ /** Subset of the `openai` package's chat message shape we read from. */
38
+ interface OpenAiChatMessageParam {
39
+ role: string;
40
+ content?: unknown;
41
+ tool_call_id?: string;
42
+ [key: string]: unknown;
43
+ }
44
+ /** Subset of the `openai` package's tool-definition shape we read from. */
45
+ interface OpenAiToolParam {
46
+ type?: string;
47
+ function?: {
48
+ name?: string;
49
+ [key: string]: unknown;
50
+ };
51
+ [key: string]: unknown;
52
+ }
53
+ /**
54
+ * Subset of `openai`'s `ChatCompletionCreateParams` we read from. We
55
+ * deliberately decline to import the real types so the SDK works even when
56
+ * `openai` isn't installed (it's an optional peer dep).
57
+ */
58
+ interface OpenAiChatCompletionCreateParams {
59
+ model: string;
60
+ messages: OpenAiChatMessageParam[];
61
+ tools?: OpenAiToolParam[];
62
+ [key: string]: unknown;
63
+ }
64
+ interface OpenAiCompletionUsageDetails {
65
+ cached_tokens?: number;
66
+ reasoning_tokens?: number;
67
+ [key: string]: unknown;
68
+ }
69
+ interface OpenAiCompletionUsage {
70
+ prompt_tokens?: number;
71
+ completion_tokens?: number;
72
+ total_tokens?: number;
73
+ prompt_tokens_details?: OpenAiCompletionUsageDetails;
74
+ completion_tokens_details?: OpenAiCompletionUsageDetails;
75
+ [key: string]: unknown;
76
+ }
77
+ interface OpenAiChatCompletionResponse {
78
+ id?: string;
79
+ model?: string;
80
+ usage?: OpenAiCompletionUsage;
81
+ choices?: Array<{
82
+ message?: {
83
+ content?: unknown;
84
+ [k: string]: unknown;
85
+ };
86
+ [k: string]: unknown;
87
+ }>;
88
+ [key: string]: unknown;
89
+ }
90
+ /**
91
+ * Structural shape of the plain OpenAI client we wrap. We only require
92
+ * that `client.chat.completions.create` is a function returning a Promise.
93
+ */
94
+ interface OpenAiClientLike {
95
+ chat: {
96
+ completions: {
97
+ create(params: OpenAiChatCompletionCreateParams, options?: unknown): Promise<OpenAiChatCompletionResponse>;
98
+ [key: string]: unknown;
99
+ };
100
+ [key: string]: unknown;
101
+ };
102
+ [key: string]: unknown;
103
+ }
104
+ interface WrapOpenAiOptions {
105
+ /**
106
+ * When provided, used as the parent agent span's name when
107
+ * `chat.completions.create` is called outside an active trace. Defaults
108
+ * to `"openai.chat.completions.create"`.
109
+ */
110
+ defaultAgentName?: string;
111
+ }
112
+ /**
113
+ * Wrap an OpenAI client so every `chat.completions.create` call records an
114
+ * `llm` span with `model`, token counts, `cost_usd`, and `context_segments`.
115
+ * The wrapper preserves the rest of the client's surface (other resources,
116
+ * helpers, etc.) via a Proxy.
117
+ *
118
+ * Idempotent — a previously wrapped client is returned as-is.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import { wrapOpenAI } from "@morsehq-dev/sdk/openai";
123
+ * import OpenAI from "openai";
124
+ *
125
+ * const client = wrapOpenAI(new OpenAI());
126
+ * const response = await client.chat.completions.create({
127
+ * model: "gpt-4o-mini",
128
+ * messages: [{ role: "user", content: "hello" }],
129
+ * });
130
+ * ```
131
+ */
132
+ declare function wrapOpenAI<C extends OpenAiClientLike>(client: C, options?: WrapOpenAiOptions): C;
133
+ /** True when a wrapped OpenAI client is passed in. */
134
+ declare function isOpenAiWrapped(client: unknown): boolean;
135
+
136
+ export { type OpenAiChatCompletionCreateParams, type OpenAiChatCompletionResponse, type OpenAiChatMessageParam, type OpenAiClientLike, type OpenAiCompletionUsage, type OpenAiCompletionUsageDetails, type OpenAiToolParam, type WrapOpenAiOptions, isOpenAiWrapped, wrapOpenAI };
@@ -0,0 +1,122 @@
1
+ import { calculateCost } from '../chunk-XTKMUJWI.js';
2
+ import { decomposeOpenAiMessages } from '../chunk-4R4SHGOK.js';
3
+ import { init_tracing, getActiveTraceState, runAsync, startSpan } from '../chunk-TQWI4UYO.js';
4
+
5
+ // src/openai/index.ts
6
+ init_tracing();
7
+ var WRAPPED_CLIENT = /* @__PURE__ */ Symbol.for("morse.openai.wrapped");
8
+ function wrapOpenAI(client, options = {}) {
9
+ if (!client || typeof client !== "object") return client;
10
+ if (client[WRAPPED_CLIENT]) return client;
11
+ const originalCreate = client.chat?.completions?.create?.bind(client.chat.completions);
12
+ if (typeof originalCreate !== "function") return client;
13
+ const patchedCreate = buildPatchedCreate(originalCreate, options);
14
+ const patchedCompletions = new Proxy(client.chat.completions, {
15
+ get(target, prop, receiver) {
16
+ if (prop === "create") return patchedCreate;
17
+ const value = Reflect.get(target, prop, receiver);
18
+ if (typeof value === "function") return value.bind(target);
19
+ return value;
20
+ }
21
+ });
22
+ const patchedChat = new Proxy(client.chat, {
23
+ get(target, prop, receiver) {
24
+ if (prop === "completions") return patchedCompletions;
25
+ const value = Reflect.get(target, prop, receiver);
26
+ if (typeof value === "function") return value.bind(target);
27
+ return value;
28
+ }
29
+ });
30
+ const wrapper = new Proxy(client, {
31
+ get(target, prop, receiver) {
32
+ if (prop === WRAPPED_CLIENT) return true;
33
+ if (prop === "chat") return patchedChat;
34
+ const value = Reflect.get(target, prop, receiver);
35
+ if (typeof value === "function") return value.bind(target);
36
+ return value;
37
+ }
38
+ });
39
+ return wrapper;
40
+ }
41
+ function isOpenAiWrapped(client) {
42
+ if (!client || typeof client !== "object") return false;
43
+ return client[WRAPPED_CLIENT] === true;
44
+ }
45
+ function buildPatchedCreate(original, options) {
46
+ const agentName = options.defaultAgentName ?? "openai.chat.completions.create";
47
+ return async function patchedCreate(params, callOptions) {
48
+ const inTrace = getActiveTraceState() !== null;
49
+ const work = async () => {
50
+ const handle = startSpan({
51
+ name: "openai.chat.completions.create",
52
+ type: "llm",
53
+ metadata: { model: params?.model }
54
+ });
55
+ let response;
56
+ try {
57
+ response = await original.call(this, params, callOptions);
58
+ } catch (err) {
59
+ handle?.end(err);
60
+ throw err;
61
+ }
62
+ try {
63
+ if (handle) {
64
+ const segments = safeExtractSegments(params);
65
+ const usage = response?.usage ?? {};
66
+ const inputTokens = usage.prompt_tokens ?? 0;
67
+ const outputTokens = usage.completion_tokens ?? 0;
68
+ const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
69
+ const model = response?.model ?? params?.model;
70
+ handle.span.model = model;
71
+ if (inputTokens > 0) handle.span.input_tokens = inputTokens;
72
+ if (outputTokens > 0) handle.span.output_tokens = outputTokens;
73
+ if (cachedTokens > 0) handle.span.cached_input_tokens = cachedTokens;
74
+ const totalTokens = usage.total_tokens ?? (inputTokens > 0 || outputTokens > 0 ? inputTokens + outputTokens : 0);
75
+ if (totalTokens > 0) handle.span.total_tokens = totalTokens;
76
+ if (model && (inputTokens > 0 || outputTokens > 0)) {
77
+ const cost = calculateCost({
78
+ inputTokens,
79
+ outputTokens,
80
+ model,
81
+ cachedInputTokens: cachedTokens
82
+ });
83
+ if (cost !== void 0) handle.span.cost_usd = cost;
84
+ }
85
+ handle.span.context_segments = segments;
86
+ }
87
+ } catch {
88
+ }
89
+ handle?.end();
90
+ return response;
91
+ };
92
+ if (inTrace) {
93
+ return work();
94
+ }
95
+ let result;
96
+ let thrown;
97
+ await runAsync({ agentName }, async (runHandle) => {
98
+ runHandle.setOutcome(true);
99
+ try {
100
+ result = await work();
101
+ } catch (err) {
102
+ thrown = err;
103
+ throw err;
104
+ }
105
+ return null;
106
+ });
107
+ if (thrown !== void 0) throw thrown;
108
+ return result;
109
+ };
110
+ }
111
+ function safeExtractSegments(params) {
112
+ try {
113
+ if (!params || typeof params !== "object") return [];
114
+ return decomposeOpenAiMessages(params.messages, params.tools);
115
+ } catch {
116
+ return [];
117
+ }
118
+ }
119
+
120
+ export { isOpenAiWrapped, wrapOpenAI };
121
+ //# sourceMappingURL=index.js.map
122
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/openai/index.ts"],"names":[],"mappings":";;;;;AAwCA,YAAA,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,GAAU,qBAAoB,KAAM,IAAA;AAE1C,IAAA,MAAM,OAAO,YAAmD;AAC9D,MAAA,MAAM,SAAS,SAAA,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,OAAO,aAAA,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;AAAA,QACjC;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;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,MAAM,QAAA,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,OAAO,uBAAA,CAAwB,MAAA,CAAO,QAAA,EAAU,MAAA,CAAO,KAAK,CAAA;AAAA,EAC9D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF","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"]}