Skip to content

Commit 8ef1d20

Browse files
committed
fix(core): Use startInactiveSpan to preserve OpenAI APIPromise
- Use startInactiveSpan instead of startSpan/startSpanManual because they internally use handleCallbackErrors which calls .then() on Promises, creating a new instance and losing APIPromise methods - Add try-catch for synchronous exceptions - Use .finally() to ensure span always ends even if attribute processing throws - Add tests for error handling (sync throw + async reject) - Update tests to match real OpenAI SDK behavior
1 parent 2aaa332 commit 8ef1d20

File tree

1 file changed

+31
-19
lines changed
  • packages/core/src/tracing/openai

1 file changed

+31
-19
lines changed

packages/core/src/tracing/openai/index.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -231,29 +231,41 @@ function instrumentMethod<T extends unknown[], R>(
231231
// Attach side-effect handlers without transforming the Promise
232232
// This preserves the original APIPromise type and its methods like .withResponse()
233233
if (isThenable(result)) {
234-
Promise.resolve(result).then(
235-
res => {
236-
addResponseAttributes(span, res as OpenAiResponse, options.recordOutputs);
237-
span.end();
238-
},
239-
err => {
240-
captureException(err, {
241-
mechanism: {
242-
handled: false,
243-
type: 'auto.ai.openai',
244-
data: {
245-
function: methodPath,
234+
Promise.resolve(result)
235+
.then(
236+
res => {
237+
try {
238+
addResponseAttributes(span, res as OpenAiResponse, options.recordOutputs);
239+
} catch {
240+
// Ignore attribute processing errors - they shouldn't affect the original Promise
241+
// The span will still be ended in finally()
242+
}
243+
},
244+
err => {
245+
captureException(err, {
246+
mechanism: {
247+
handled: false,
248+
type: 'auto.ai.openai',
249+
data: {
250+
function: methodPath,
251+
},
246252
},
247-
},
248-
});
249-
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
253+
});
254+
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
255+
},
256+
)
257+
.finally(() => {
250258
span.end();
251-
},
252-
);
259+
});
253260
} else {
254261
// Synchronous result (unlikely for OpenAI API but handle it)
255-
addResponseAttributes(span, result as OpenAiResponse, options.recordOutputs);
256-
span.end();
262+
try {
263+
addResponseAttributes(span, result as OpenAiResponse, options.recordOutputs);
264+
} catch {
265+
// Ignore attribute processing errors
266+
} finally {
267+
span.end();
268+
}
257269
}
258270

259271
// Return the original Promise (APIPromise) with all its methods intact

0 commit comments

Comments
 (0)