Skip to content

Commit 7dd6c21

Browse files
committed
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.
1 parent a13680c commit 7dd6c21

1 file changed

Lines changed: 48 additions & 39 deletions

File tree

portkey_ai/_vendor/openai/_types.py

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -116,61 +116,70 @@ class RequestOptions(TypedDict, total=False):
116116

117117

118118
# Sentinel class used until PEP 0661 is accepted
119-
class NotGiven:
120-
"""
121-
For parameters with a meaningful None value, we need to distinguish between
122-
the user explicitly passing None, and the user not passing the parameter at
123-
all.
119+
# Use external openai's NotGiven/Omit if available for compatibility with libraries
120+
# that use the standard openai package (e.g., pydantic-ai, langchain)
121+
try:
122+
from openai._types import NotGiven, Omit, NOT_GIVEN
124123

125-
User code shouldn't need to use not_given directly.
124+
# External openai only exports NOT_GIVEN, not the lowercase not_given
125+
not_given = NOT_GIVEN
126+
# External openai doesn't export lowercase omit, create from class
127+
omit = Omit()
128+
except ImportError:
129+
# Fall back to defining our own (for standalone use without openai installed)
126130

127-
For example:
131+
class NotGiven:
132+
"""
133+
For parameters with a meaningful None value, we need to distinguish between
134+
the user explicitly passing None, and the user not passing the parameter at
135+
all.
128136
129-
```py
130-
def create(timeout: Timeout | None | NotGiven = not_given): ...
137+
User code shouldn't need to use not_given directly.
131138
139+
For example:
132140
133-
create(timeout=1) # 1s timeout
134-
create(timeout=None) # No timeout
135-
create() # Default timeout behavior
136-
```
137-
"""
141+
```py
142+
def create(timeout: Timeout | None | NotGiven = not_given): ...
138143
139-
def __bool__(self) -> Literal[False]:
140-
return False
141144
142-
@override
143-
def __repr__(self) -> str:
144-
return "NOT_GIVEN"
145+
create(timeout=1) # 1s timeout
146+
create(timeout=None) # No timeout
147+
create() # Default timeout behavior
148+
```
149+
"""
145150

151+
def __bool__(self) -> Literal[False]:
152+
return False
146153

147-
not_given = NotGiven()
148-
# for backwards compatibility:
149-
NOT_GIVEN = NotGiven()
154+
@override
155+
def __repr__(self) -> str:
156+
return "NOT_GIVEN"
150157

158+
not_given = NotGiven()
159+
# for backwards compatibility:
160+
NOT_GIVEN = NotGiven()
151161

152-
class Omit:
153-
"""
154-
To explicitly omit something from being sent in a request, use `omit`.
162+
class Omit:
163+
"""
164+
To explicitly omit something from being sent in a request, use `omit`.
155165
156-
```py
157-
# as the default `Content-Type` header is `application/json` that will be sent
158-
client.post("/upload/files", files={"file": b"my raw file content"})
166+
```py
167+
# as the default `Content-Type` header is `application/json` that will be sent
168+
client.post("/upload/files", files={"file": b"my raw file content"})
159169
160-
# you can't explicitly override the header as it has to be dynamically generated
161-
# to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983'
162-
client.post(..., headers={"Content-Type": "multipart/form-data"})
163-
164-
# instead you can remove the default `application/json` header by passing omit
165-
client.post(..., headers={"Content-Type": omit})
166-
```
167-
"""
170+
# you can't explicitly override the header as it has to be dynamically generated
171+
# to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983'
172+
client.post(..., headers={"Content-Type": "multipart/form-data"})
168173
169-
def __bool__(self) -> Literal[False]:
170-
return False
174+
# instead you can remove the default `application/json` header by passing omit
175+
client.post(..., headers={"Content-Type": omit})
176+
```
177+
"""
171178

179+
def __bool__(self) -> Literal[False]:
180+
return False
172181

173-
omit = Omit()
182+
omit = Omit()
174183

175184
Omittable = Union[_T, Omit]
176185

0 commit comments

Comments
 (0)