From 7dd6c21c53d75bb4d7d5acb0093161e9fe0d02ad Mon Sep 17 00:00:00 2001 From: Rohit Agarwal Date: Tue, 25 Nov 2025 17:52:45 -0800 Subject: [PATCH] fix: use external openai NotGiven/Omit types for compatibility When libraries like pydantic-ai pass NOT_GIVEN values from the standard openai package, Portkey's vendored SDK now recognizes them because they use the exact same class. This fixes TypeError: Object of type NotGiven is not JSON serializable errors when using pydantic-ai or similar libraries with Portkey. --- portkey_ai/_vendor/openai/_types.py | 87 ++++++++++++++++------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/portkey_ai/_vendor/openai/_types.py b/portkey_ai/_vendor/openai/_types.py index 2387d7e0..81543115 100644 --- a/portkey_ai/_vendor/openai/_types.py +++ b/portkey_ai/_vendor/openai/_types.py @@ -116,61 +116,70 @@ class RequestOptions(TypedDict, total=False): # Sentinel class used until PEP 0661 is accepted -class NotGiven: - """ - For parameters with a meaningful None value, we need to distinguish between - the user explicitly passing None, and the user not passing the parameter at - all. +# Use external openai's NotGiven/Omit if available for compatibility with libraries +# that use the standard openai package (e.g., pydantic-ai, langchain) +try: + from openai._types import NotGiven, Omit, NOT_GIVEN - User code shouldn't need to use not_given directly. + # External openai only exports NOT_GIVEN, not the lowercase not_given + not_given = NOT_GIVEN + # External openai doesn't export lowercase omit, create from class + omit = Omit() +except ImportError: + # Fall back to defining our own (for standalone use without openai installed) - For example: + class NotGiven: + """ + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. - ```py - def create(timeout: Timeout | None | NotGiven = not_given): ... + User code shouldn't need to use not_given directly. + For example: - create(timeout=1) # 1s timeout - create(timeout=None) # No timeout - create() # Default timeout behavior - ``` - """ + ```py + def create(timeout: Timeout | None | NotGiven = not_given): ... - def __bool__(self) -> Literal[False]: - return False - @override - def __repr__(self) -> str: - return "NOT_GIVEN" + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior + ``` + """ + def __bool__(self) -> Literal[False]: + return False -not_given = NotGiven() -# for backwards compatibility: -NOT_GIVEN = NotGiven() + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + not_given = NotGiven() + # for backwards compatibility: + NOT_GIVEN = NotGiven() -class Omit: - """ - To explicitly omit something from being sent in a request, use `omit`. + class Omit: + """ + To explicitly omit something from being sent in a request, use `omit`. - ```py - # as the default `Content-Type` header is `application/json` that will be sent - client.post("/upload/files", files={"file": b"my raw file content"}) + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post("/upload/files", files={"file": b"my raw file content"}) - # you can't explicitly override the header as it has to be dynamically generated - # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' - client.post(..., headers={"Content-Type": "multipart/form-data"}) - - # instead you can remove the default `application/json` header by passing omit - client.post(..., headers={"Content-Type": omit}) - ``` - """ + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={"Content-Type": "multipart/form-data"}) - def __bool__(self) -> Literal[False]: - return False + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) + ``` + """ + def __bool__(self) -> Literal[False]: + return False -omit = Omit() + omit = Omit() Omittable = Union[_T, Omit]