diff --git a/temporalio/contrib/openai_agents/__init__.py b/temporalio/contrib/openai_agents/__init__.py index aedf6eb85..d2969e509 100644 --- a/temporalio/contrib/openai_agents/__init__.py +++ b/temporalio/contrib/openai_agents/__init__.py @@ -8,6 +8,12 @@ Use with caution in production environments. """ +import importlib +import inspect +import pkgutil + +from pydantic import BaseModel + from temporalio.contrib.openai_agents._invoke_model_activity import ModelActivity from temporalio.contrib.openai_agents._model_parameters import ModelActivityParameters from temporalio.contrib.openai_agents._trace_interceptor import ( @@ -30,3 +36,23 @@ "TestModel", "TestModelProvider", ] + + +def _reload_models(module_name: str) -> None: + """Recursively walk through modules and rebuild BaseModel classes.""" + module = importlib.import_module(module_name) + + # Process classes in the current module + for _, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, BaseModel) and obj is not BaseModel: + obj.model_rebuild() + + # Recursively process submodules + if hasattr(module, "__path__"): + for _, submodule_name, _ in pkgutil.iter_modules(module.__path__): + full_submodule_name = f"{module_name}.{submodule_name}" + _reload_models(full_submodule_name) + + +# Recursively call model_rebuild() on all BaseModel classes in openai.types +_reload_models("openai.types") diff --git a/temporalio/contrib/openai_agents/_invoke_model_activity.py b/temporalio/contrib/openai_agents/_invoke_model_activity.py index 069ea001e..6b365902a 100644 --- a/temporalio/contrib/openai_agents/_invoke_model_activity.py +++ b/temporalio/contrib/openai_agents/_invoke_model_activity.py @@ -5,7 +5,6 @@ import enum import json -from dataclasses import dataclass from typing import Any, Optional, Union, cast from agents import ( @@ -24,6 +23,7 @@ WebSearchTool, ) from agents.models.multi_provider import MultiProvider +from pydantic.dataclasses import dataclass from typing_extensions import Required, TypedDict from temporalio import activity diff --git a/tests/contrib/openai_agents/test_openai.py b/tests/contrib/openai_agents/test_openai.py index 57dc5c252..484e4d61a 100644 --- a/tests/contrib/openai_agents/test_openai.py +++ b/tests/contrib/openai_agents/test_openai.py @@ -45,7 +45,7 @@ ) from openai.types.responses.response_function_web_search import ActionSearch from openai.types.responses.response_prompt_param import ResponsePromptParam -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, TypeAdapter from temporalio import activity, workflow from temporalio.client import Client, WorkflowFailureError, WorkflowHandle @@ -1778,3 +1778,22 @@ async def test_workflow_method_tools(client: Client): execution_timeout=timedelta(seconds=10), ) await workflow_handle.result() + + +async def test_response_serialization(): + import json + + from openai.types.responses.response_output_item import ImageGenerationCall + + data = json.loads( + b'{"id": "msg_68757ec43348819d86709f0fcb70316301a1194a3e05b38c","type": "image_generation_call","status": "completed"}' + ) + call = TypeAdapter(ImageGenerationCall).validate_python(data) + model_response = ModelResponse( + output=[ + call, + ], + usage=Usage(), + response_id="", + ) + encoded = await pydantic_data_converter.encode([model_response]) diff --git a/uv.lock b/uv.lock index 638b7a118..6cf32b17e 100644 --- a/uv.lock +++ b/uv.lock @@ -1091,7 +1091,7 @@ wheels = [ [[package]] name = "openai" -version = "1.92.3" +version = "1.96.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1103,9 +1103,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/bd/6c123e53d6727dc39086346df1161c8b6bb50e4cc0f2e9075cae909049b6/openai-1.92.3.tar.gz", hash = "sha256:6b707bc926a9fffab262dbd1eb4be29c36d627b9012633a66ccdbe2d615a9532", size = 485647, upload-time = "2025-06-27T17:06:55.96Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/7d/dbc636786f8bf029600abdbf89da74706bdde37c4fe1471ce78834a7ecaa/openai-1.96.0.tar.gz", hash = "sha256:36e34b5aa2c9c0380c1934fa16ba53b3b3c6462450b4c008b98859b9b6424cf7", size = 488898, upload-time = "2025-07-15T15:56:52.853Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/60/4956baa677a569a247336c7edff4b3b6aab7a54119e2342d391d1d370446/openai-1.92.3-py3-none-any.whl", hash = "sha256:979f00c097d023a28f22c63373a646a675e4b9e1fad586e2424bf7274a2689f2", size = 753350, upload-time = "2025-06-27T17:06:53.547Z" }, + { url = "https://files.pythonhosted.org/packages/dc/63/e29319a52449b7ac4c3a13a1448a9fa326aa1263478eb4c4a9bcbbe95648/openai-1.96.0-py3-none-any.whl", hash = "sha256:4dee023520f8a70ddeaa9f4d6fb247e6dcafd79d2ebb415e3f85932d95aa64a0", size = 757092, upload-time = "2025-07-15T15:56:50.387Z" }, ] [[package]]