From b720d5975ca32a2944c9064435a520a09f49dd0a Mon Sep 17 00:00:00 2001 From: Rohit Agarwal Date: Mon, 24 Nov 2025 18:48:08 -0800 Subject: [PATCH 1/2] fix: properly handle extra_body parameter in completions API The extra_body parameter was being passed incorrectly to the OpenAI SDK, causing the entire kwargs dict (including the 'extra_body' key itself) to be merged into the request body. Before this fix: - User calls: create(..., extra_body={'a': 'b'}) - Request body contained: {'extra_body': {'a': 'b'}} (wrong) After this fix: - User calls: create(..., extra_body={'a': 'b'}) - Request body contains: {'a': 'b'} (correct) The fix extracts extra_body from kwargs, merges its contents with remaining kwargs, and passes the merged result to the OpenAI SDK's extra_body parameter. This matches the expected OpenAI SDK behavior. Fixed in: - chat_complete.py: stream_create, normal_create (sync + async) - complete.py: stream_create, normal_create (sync + async) --- .../api_resources/apis/chat_complete.py | 40 +++++++++++++++---- portkey_ai/api_resources/apis/complete.py | 40 +++++++++++++++++-- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/portkey_ai/api_resources/apis/chat_complete.py b/portkey_ai/api_resources/apis/chat_complete.py index fc87b78..44f31de 100644 --- a/portkey_ai/api_resources/apis/chat_complete.py +++ b/portkey_ai/api_resources/apis/chat_complete.py @@ -74,7 +74,11 @@ def stream_create( # type: ignore[return] store, **kwargs, ) -> Union[ChatCompletions, Iterator[ChatCompletionChunk]]: - extra_headers = kwargs.get("extra_headers", {}) + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} return self.openai_client.chat.completions.create( model=model, messages=messages, @@ -90,7 +94,9 @@ def stream_create( # type: ignore[return] reasoning_effort=reasoning_effort, store=store, extra_headers=extra_headers, - extra_body=kwargs, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) def normal_create( @@ -110,7 +116,11 @@ def normal_create( store, **kwargs, ) -> ChatCompletions: - extra_headers = kwargs.get("extra_headers", {}) + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} response = self.openai_client.with_raw_response.chat.completions.create( model=model, messages=messages, @@ -126,7 +136,9 @@ def normal_create( reasoning_effort=reasoning_effort, store=store, extra_headers=extra_headers, - extra_body=kwargs, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) data = ChatCompletions(**json.loads(response.text)) data._headers = response.headers @@ -472,7 +484,11 @@ async def stream_create( store, **kwargs, ) -> Union[ChatCompletions, AsyncIterator[ChatCompletionChunk]]: - extra_headers = kwargs.get("extra_headers", {}) + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} return await self.openai_client.chat.completions.create( model=model, messages=messages, @@ -488,7 +504,9 @@ async def stream_create( reasoning_effort=reasoning_effort, store=store, extra_headers=extra_headers, - extra_body=kwargs, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) async def normal_create( @@ -508,7 +526,11 @@ async def normal_create( store, **kwargs, ) -> ChatCompletions: - extra_headers = kwargs.get("extra_headers", {}) + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} response = await self.openai_client.with_raw_response.chat.completions.create( model=model, messages=messages, @@ -524,7 +546,9 @@ async def normal_create( reasoning_effort=reasoning_effort, store=store, extra_headers=extra_headers, - extra_body=kwargs, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) data = ChatCompletions(**json.loads(response.text)) data._headers = response.headers diff --git a/portkey_ai/api_resources/apis/complete.py b/portkey_ai/api_resources/apis/complete.py index 0299c4f..f7fb3c5 100644 --- a/portkey_ai/api_resources/apis/complete.py +++ b/portkey_ai/api_resources/apis/complete.py @@ -39,6 +39,11 @@ def stream_create( # type: ignore[return] stream_options, **kwargs, ) -> Union[TextCompletion, Iterator[TextCompletionChunk]]: + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} return self.openai_client.completions.create( model=model, prompt=prompt, @@ -58,7 +63,10 @@ def stream_create( # type: ignore[return] suffix=suffix, user=user, stream_options=stream_options, - extra_body=kwargs, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) def normal_create( @@ -83,6 +91,11 @@ def normal_create( stream_options, **kwargs, ) -> TextCompletion: + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} response = self.openai_client.with_raw_response.completions.create( model=model, prompt=prompt, @@ -102,7 +115,10 @@ def normal_create( suffix=suffix, user=user, stream_options=stream_options, - extra_body=kwargs, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) data = TextCompletion(**json.loads(response.text)) data._headers = response.headers @@ -206,6 +222,11 @@ async def stream_create( stream_options, **kwargs, ) -> Union[TextCompletion, AsyncIterator[TextCompletionChunk]]: + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} return await self.openai_client.completions.create( model=model, prompt=prompt, @@ -225,7 +246,10 @@ async def stream_create( suffix=suffix, user=user, stream_options=stream_options, - extra_body=kwargs, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) async def normal_create( @@ -250,6 +274,11 @@ async def normal_create( stream_options, **kwargs, ) -> TextCompletion: + extra_headers = kwargs.pop("extra_headers", None) + extra_query = kwargs.pop("extra_query", None) + timeout = kwargs.pop("timeout", None) + user_extra_body = kwargs.pop("extra_body", None) or {} + merged_extra_body = {**user_extra_body, **kwargs} response = await self.openai_client.with_raw_response.completions.create( model=model, prompt=prompt, @@ -269,7 +298,10 @@ async def normal_create( suffix=suffix, user=user, stream_options=stream_options, - extra_body=kwargs, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=merged_extra_body if merged_extra_body else None, + timeout=timeout, ) data = TextCompletion(**json.loads(response.text)) data._headers = response.headers From 1d9c53ee5646780113d2c4028abfbe96119b07eb Mon Sep 17 00:00:00 2001 From: Rohit Agarwal Date: Mon, 24 Nov 2025 19:04:29 -0800 Subject: [PATCH 2/2] refactor: simplify extra_body passing per review feedback Remove unnecessary conditional when passing extra_body to OpenAI SDK. Empty dict and None are both handled correctly by the SDK. --- portkey_ai/api_resources/apis/chat_complete.py | 8 ++++---- portkey_ai/api_resources/apis/complete.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/portkey_ai/api_resources/apis/chat_complete.py b/portkey_ai/api_resources/apis/chat_complete.py index 44f31de..15a25d9 100644 --- a/portkey_ai/api_resources/apis/chat_complete.py +++ b/portkey_ai/api_resources/apis/chat_complete.py @@ -95,7 +95,7 @@ def stream_create( # type: ignore[return] store=store, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) @@ -137,7 +137,7 @@ def normal_create( store=store, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) data = ChatCompletions(**json.loads(response.text)) @@ -505,7 +505,7 @@ async def stream_create( store=store, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) @@ -547,7 +547,7 @@ async def normal_create( store=store, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) data = ChatCompletions(**json.loads(response.text)) diff --git a/portkey_ai/api_resources/apis/complete.py b/portkey_ai/api_resources/apis/complete.py index f7fb3c5..24fa0ef 100644 --- a/portkey_ai/api_resources/apis/complete.py +++ b/portkey_ai/api_resources/apis/complete.py @@ -65,7 +65,7 @@ def stream_create( # type: ignore[return] stream_options=stream_options, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) @@ -117,7 +117,7 @@ def normal_create( stream_options=stream_options, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) data = TextCompletion(**json.loads(response.text)) @@ -248,7 +248,7 @@ async def stream_create( stream_options=stream_options, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) @@ -300,7 +300,7 @@ async def normal_create( stream_options=stream_options, extra_headers=extra_headers, extra_query=extra_query, - extra_body=merged_extra_body if merged_extra_body else None, + extra_body=merged_extra_body, timeout=timeout, ) data = TextCompletion(**json.loads(response.text))