Skip to content

Conversation

@k11kirky
Copy link
Contributor

@k11kirky k11kirky commented May 22, 2025

Support both new and old openai sdks (since resource change) by changing to a composition approach over inheritance. This means we do not need to import the resources and should be more defensive to dependency changes

fixes: #234

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

This PR refactors the OpenAI SDK integration in posthog-python from inheritance to composition, making it more resilient to SDK updates and following better OOP practices.

  • Implements wrapper classes in posthog/ai/openai/openai_providers.py that store and delegate to original OpenAI objects instead of inheriting
  • Adds defensive programming with getattr checks in posthog/ai/openai/openai.py to verify attribute existence before wrapping
  • Adds support for newer OpenAI SDK features like responses, reasoning_tokens, and cached_tokens across all files
  • Improves error handling by implementing __getattr__ fallbacks for unhandled methods
  • Maintains backward compatibility by checking feature availability at runtime before attempting to use new SDK capabilities

3 file(s) reviewed, 2 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 33 to 36
self._original_chat = getattr(self, 'chat', None)
self._original_embeddings = getattr(self, 'embeddings', None)
self._original_beta = getattr(self, 'beta', None)
self._original_responses = getattr(self, 'responses', None)
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider using a helper method to reduce code duplication between AzureOpenAI and AsyncAzureOpenAI for getting original objects

Comment on lines 39 to 50
if self._original_chat is not None:
self.chat = WrappedChat(self, self._original_chat)

if self._original_embeddings is not None:
self.embeddings = WrappedEmbeddings(self, self._original_embeddings)

if self._original_beta is not None:
self.beta = WrappedBeta(self, self._original_beta)

# Only add responses if available (newer OpenAI versions)
if self._original_responses is not None:
self.responses = WrappedResponses(self, self._original_responses)
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider using a helper method to reduce code duplication between AzureOpenAI and AsyncAzureOpenAI for wrapping objects

@k11kirky k11kirky requested a review from Twixes May 23, 2025 00:00
Copy link
Member

@rafaeelaudibert rafaeelaudibert left a comment

Choose a reason for hiding this comment

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

Not expert in this code, but makes sense. We'd benefit from tests showing this works as expected (the fallback behaviour)

if self._original_beta is not None:
self.beta = WrappedBeta(self, self._original_beta)

# Only add responses if available (newer OpenAI versions)
Copy link
Member

Choose a reason for hiding this comment

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

Useless comment, the above comment explains it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fair. Thats the ai adding random comments that i didnt reject like i usually do

self._client = client
self._original = original_responses

def __getattr__(self, name):
Copy link
Member

Choose a reason for hiding this comment

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

Will this be called only in case there's no method, or is this called all the time? Do we have unit tests for this wrapper?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we do have unit tests, just not integration tests that will run with different versions. i have tested that manually.

that method is only called if we do not have an override. it allows us to fall back to the original client if we do not support

posthog_distinct_id: Optional ID to associate with the usage event.
posthog_trace_id: Optional trace UUID for linking events.
posthog_properties: Optional dictionary of extra properties to include in the event.
posthog_privacy_mode: Whether to store input and output in PostHog.
Copy link
Member

Choose a reason for hiding this comment

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

Are we removing args or just docs were wrong? If the former, we'd need a major release. I'll assume it's the latter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was incorrect docs. fixing

@k11kirky
Copy link
Contributor Author

Agree with your comments @rafaeelaudibert I am the only one that works on this so far and have a local test file that i moved out of this repo (without pyttest). will move back in and join with pytest soon

@k11kirky k11kirky merged commit 9f4ef4f into master May 23, 2025
6 checks passed
@k11kirky k11kirky deleted the feat/open-ai-upgrade branch May 23, 2025 00:34
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.

Incompatible with OpenAI 1.78.0

3 participants