From b35707cee5e416829bb7dbf1a3f29dc929632d6a Mon Sep 17 00:00:00 2001 From: Oxygen56 <1391083091@qq.com> Date: Tue, 2 Jun 2026 23:59:15 +0800 Subject: [PATCH 1/8] fix: use getattr for non-OpenAI provider response compatibility Fixes #6234 Fixes #6235 Use getattr with None fallback for system_fingerprint and output attributes to prevent AttributeError when non-OpenAI providers return response objects without these fields. --- python/packages/openai/agent_framework_openai/_chat_client.py | 2 +- .../openai/agent_framework_openai/_chat_completion_client.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index 261554fba35..23497c90199 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,7 +1971,7 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - for item in response.output: # type: ignore[reportUnknownMemberType] + for item in getattr(response, "output", []): # type: ignore[reportUnknownMemberType] match item.type: # types: # ParsedResponseOutputMessage[Unknown] | diff --git a/python/packages/openai/agent_framework_openai/_chat_completion_client.py b/python/packages/openai/agent_framework_openai/_chat_completion_client.py index a6878c9f2d2..0fd14aa2ef5 100644 --- a/python/packages/openai/agent_framework_openai/_chat_completion_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_completion_client.py @@ -788,13 +788,13 @@ def _parse_text_from_openai(self, choice: Choice | ChunkChoice) -> Content | Non def _get_metadata_from_chat_response(self, response: ChatCompletion) -> dict[str, Any]: """Get metadata from a chat response.""" return { - "system_fingerprint": response.system_fingerprint, + "system_fingerprint": getattr(response, "system_fingerprint", None), } def _get_metadata_from_streaming_chat_response(self, response: ChatCompletionChunk) -> dict[str, Any]: """Get metadata from a streaming chat response.""" return { - "system_fingerprint": response.system_fingerprint, + "system_fingerprint": getattr(response, "system_fingerprint", None), } def _get_metadata_from_chat_choice(self, choice: Choice | ChunkChoice) -> dict[str, Any]: From e631d30a2bb3c9c53458a9f5dbb19e4fab227ef6 Mon Sep 17 00:00:00 2001 From: Oxygen56 <1391083091@qq.com> Date: Wed, 3 Jun 2026 09:16:13 +0800 Subject: [PATCH 2/8] fix: use typed variable for response output to satisfy pyright Fixes #6235 Use getattr with None fallback for the output attribute, and assign to a typed list variable before the match statement to help pyright narrow the response item types correctly. --- python/packages/openai/agent_framework_openai/_chat_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index 23497c90199..abd60594964 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,7 +1971,8 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - for item in getattr(response, "output", []): # type: ignore[reportUnknownMemberType] + outputs: list = getattr(response, "output", None) or [] + for item in outputs: match item.type: # types: # ParsedResponseOutputMessage[Unknown] | From 0b870b87d45d803b0c555009a5366653fd55e756 Mon Sep 17 00:00:00 2001 From: Willow Lopez <100782273+Oxygen56@users.noreply.github.com> Date: Wed, 3 Jun 2026 20:29:44 +0800 Subject: [PATCH 3/8] fix: rename response_outputs to avoid name collision with case-block variable Fixes #6235 Rename outputs to response_outputs on line 1974 to avoid mypy error about conflicting variable names in the match statement's case blocks. Also use list[Any] for explicit generic type annotation. --- python/packages/openai/agent_framework_openai/_chat_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index abd60594964..e7b32b876bb 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,8 +1971,8 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - outputs: list = getattr(response, "output", None) or [] - for item in outputs: + response_outputs: list[Any] = getattr(response, "output", None) or [] + for item in response_outputs: match item.type: # types: # ParsedResponseOutputMessage[Unknown] | From 22363c7dc42e2c61c2aecdbd4c34d3d17340147c Mon Sep 17 00:00:00 2001 From: Willow Lopez <100782273+Oxygen56@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:47:41 +0800 Subject: [PATCH 4/8] fix: use cast(list[Any]) for response output to satisfy pyright Fixes #6235 The getattr() call returns Unknown type which pyright cannot narrow in the match statement. Use an explicit cast to list[Any]. --- python/packages/openai/agent_framework_openai/_chat_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index e7b32b876bb..0837221482a 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,7 +1971,7 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - response_outputs: list[Any] = getattr(response, "output", None) or [] + response_outputs = cast(list[Any], getattr(response, "output", None) or []) for item in response_outputs: match item.type: # types: From 60fd2c83fc127b13007da8585c7920bf2eb3759d Mon Sep 17 00:00:00 2001 From: Willow Lopez <100782273+Oxygen56@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:40:34 +0800 Subject: [PATCH 5/8] fix: use hasattr guard instead of getattr for response.output Fixes #6235 Using hasattr(response, 'output') and then accessing response.output directly gives pyright enough type information to verify the match statement exhaustiveness. This avoids the cast(list[Any]) approach which pyright still flagged as partially unknown. --- .../packages/openai/agent_framework_openai/_chat_client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index 0837221482a..354111a72a8 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,7 +1971,10 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - response_outputs = cast(list[Any], getattr(response, "output", None) or []) + if hasattr(response, "output") and response.output: + response_outputs = response.output # type: ignore[reportUnknownMemberType] + else: + response_outputs = [] for item in response_outputs: match item.type: # types: From cbefd8db84952d77877e57d84b297d8abfdd8700 Mon Sep 17 00:00:00 2001 From: Oxygen <1391083091@qq.com> Date: Fri, 5 Jun 2026 00:40:19 +0800 Subject: [PATCH 6/8] fix: use ternary operator for response_outputs assignment Replace if-else block with ternary expression to satisfy ruff SIM108 lint rule. This fixes the Package Checks (3.11) CI failure. --- .../packages/openai/agent_framework_openai/_chat_client.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index 354111a72a8..fb8f3c5a980 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,10 +1971,7 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - if hasattr(response, "output") and response.output: - response_outputs = response.output # type: ignore[reportUnknownMemberType] - else: - response_outputs = [] + response_outputs = response.output if hasattr(response, "output") and response.output else [] # type: ignore[reportUnknownMemberType] for item in response_outputs: match item.type: # types: From 79f14ff6821f5300701f9f97fe8045713c96d136 Mon Sep 17 00:00:00 2001 From: Oxygen <1391083091@qq.com> Date: Fri, 5 Jun 2026 00:54:18 +0800 Subject: [PATCH 7/8] fix: use ternary with cast for ruff SIM108 and pyright type safety Replace if-else block with ternary expression using cast(list[Any], ...) to satisfy: - ruff SIM108 (use ternary instead of if-else) - ruff E501 (line length < 120) - pyright type narrowing (cast preserves type info lost in ternary) All local checks pass: ruff check, ruff format, pyright, 298 tests. --- python/packages/openai/agent_framework_openai/_chat_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index fb8f3c5a980..b3d4346f45f 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,7 +1971,9 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - response_outputs = response.output if hasattr(response, "output") and response.output else [] # type: ignore[reportUnknownMemberType] + response_outputs: list[Any] = ( + cast(list[Any], response.output) if hasattr(response, "output") and response.output else [] + ) for item in response_outputs: match item.type: # types: From 9be16c3baba33e3f1590acc77a8badd9c0973d7b Mon Sep 17 00:00:00 2001 From: Oxygen <1391083091@qq.com> Date: Mon, 8 Jun 2026 23:04:41 +0800 Subject: [PATCH 8/8] fix: replace hasattr+cast with try/except to preserve pyright types --- .../openai/agent_framework_openai/_chat_client.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index b3d4346f45f..0d0238a4e26 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1971,10 +1971,11 @@ def _parse_response_from_openai( metadata: dict[str, Any] = response.metadata or {} contents: list[Content] = [] local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools")) - response_outputs: list[Any] = ( - cast(list[Any], response.output) if hasattr(response, "output") and response.output else [] - ) - for item in response_outputs: + try: + response_outputs = response.output # type: ignore[reportUnknownMemberType] + except AttributeError: + response_outputs = [] + for item in response_outputs: # type: ignore[reportUnknownVariableType] match item.type: # types: # ParsedResponseOutputMessage[Unknown] |