Skip to content

Conversation

@Sai-Tom
Copy link

@Sai-Tom Sai-Tom commented Dec 4, 2025

Summary

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse().
The previous async/await instrumentation converted these to regular Promises, losing those methods.

This PR uses Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.

Changes

  • Use Proxy instead of async function wrapper
  • Use handleCallbackErrors for non-streaming responses (preserves Promise subclass)
  • Add handleStreamingError helper for consistency
  • Add tests for APIPromise.withResponse() preservation

Test Plan

  • yarn build:dev passes
  • yarn test passes
  • yarn lint passes
  • Added tests for APIPromise preservation

The OpenAI SDK returns APIPromise objects with additional methods like .withResponse(). The previous async/await instrumentation converted these to regular Promises, losing those methods.

Use Proxy + handleCallbackErrors pattern (matching anthropic-ai) to preserve the original return type.
  - 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
  - Add tests for error handling (sync throw + async reject)
  - Update tests to match real OpenAI SDK behavior"
- 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
@AbhiPrasad AbhiPrasad requested a review from RulaKhaled December 4, 2025 17:38
Comment on lines 202 to 210
const span = startInactiveSpan({
name: `${operationName} ${model}`,
op: getSpanOperation(methodPath),
attributes: requestAttributes as Record<string, SpanAttributeValue>,
});

if (options.recordInputs && params) {
addRequestAttributes(span, params);
}

This comment was marked as outdated.

@RulaKhaled RulaKhaled self-assigned this Dec 9, 2025
Copy link
Member

@RulaKhaled RulaKhaled left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contributing! I have a question

// We use startInactiveSpan instead of startSpan/startSpanManual because those
// internally use handleCallbackErrors which calls .then() on Promises, creating
// a new Promise instance and losing APIPromise's custom methods like .withResponse().
const span = startInactiveSpan({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried using start span here?
I understand that your purpose here is to use handleCallbackErrors and handle promises, you can still achieve this with start span, and as long as we didn't end the span in a different scope/context, it should work fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants