From c714ce80e2e6436810f805c580d2d37d67785b9b Mon Sep 17 00:00:00 2001 From: Tim Conley Date: Wed, 3 Sep 2025 08:18:32 -0700 Subject: [PATCH 1/2] Add a test for output type coercion --- temporalio/contrib/pydantic.py | 3 ++ tests/contrib/openai_agents/test_openai.py | 57 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/temporalio/contrib/pydantic.py b/temporalio/contrib/pydantic.py index 97f1b6ac3..365d8aea5 100644 --- a/temporalio/contrib/pydantic.py +++ b/temporalio/contrib/pydantic.py @@ -56,6 +56,7 @@ class PydanticJSONPlainPayloadConverter(EncodingPayloadConverter): def __init__(self, to_json_options: Optional[ToJsonOptions] = None): """Create a new payload converter.""" self._schema_serializer = SchemaSerializer(any_schema()) + print("Init:", self._schema_serializer) self._to_json_options = to_json_options @property @@ -71,6 +72,8 @@ def to_payload(self, value: Any) -> Optional[temporalio.api.common.v1.Payload]: See https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.to_json. """ + print("To payload:", self._schema_serializer) + print(f"value:{value}, type:{type(value)}") data = ( self._schema_serializer.to_json( value, exclude_unset=self._to_json_options.exclude_unset diff --git a/tests/contrib/openai_agents/test_openai.py b/tests/contrib/openai_agents/test_openai.py index 7c3df0897..52e626c6c 100644 --- a/tests/contrib/openai_agents/test_openai.py +++ b/tests/contrib/openai_agents/test_openai.py @@ -8,6 +8,7 @@ from typing import Any, AsyncIterator, Optional, Union, no_type_check import nexusrpc +import pydantic import pytest from agents import ( Agent, @@ -2184,3 +2185,59 @@ def provide( async for e in workflow_handle.fetch_history_events(): if e.HasField("activity_task_scheduled_event_attributes"): assert e.user_metadata.summary.data == b'"My summary"' + + +class OutputType(pydantic.BaseModel): + answer: str + model_config = ConfigDict(extra="forbid") # Forbid additional properties + + +@workflow.defn +class OutputTypeWorkflow: + @workflow.run + async def run(self) -> OutputType: + agent: Agent = Agent( + name="Assistant", + instructions="You are a helpful assistant, adhere to the json schema output", + output_type=OutputType, + ) + result = await Runner.run( + starting_agent=agent, + input="Hello!", + ) + return result.final_output + + +class OutputTypeModel(StaticTestModel): + responses = [ + ResponseBuilders.output_message( + '{"answer": "My answer"}', + ), + ] + + +async def test_output_type(client: Client): + new_config = client.config() + new_config["plugins"] = [ + openai_agents.OpenAIAgentsPlugin( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=120), + ), + model_provider=TestModelProvider(OutputTypeModel()), + ) + ] + client = Client(**new_config) + + async with new_worker( + client, + OutputTypeWorkflow, + ) as worker: + workflow_handle = await client.start_workflow( + OutputTypeWorkflow.run, + id=f"output-type-{uuid.uuid4()}", + task_queue=worker.task_queue, + execution_timeout=timedelta(seconds=10), + ) + result = await workflow_handle.result() + assert isinstance(result, OutputType) + assert result.answer == "My answer" From 23ba5c93be182c0d58e4ad93c7c1ae4a1c159dbe Mon Sep 17 00:00:00 2001 From: Tim Conley Date: Wed, 3 Sep 2025 08:19:31 -0700 Subject: [PATCH 2/2] remove prints --- temporalio/contrib/pydantic.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/temporalio/contrib/pydantic.py b/temporalio/contrib/pydantic.py index 365d8aea5..97f1b6ac3 100644 --- a/temporalio/contrib/pydantic.py +++ b/temporalio/contrib/pydantic.py @@ -56,7 +56,6 @@ class PydanticJSONPlainPayloadConverter(EncodingPayloadConverter): def __init__(self, to_json_options: Optional[ToJsonOptions] = None): """Create a new payload converter.""" self._schema_serializer = SchemaSerializer(any_schema()) - print("Init:", self._schema_serializer) self._to_json_options = to_json_options @property @@ -72,8 +71,6 @@ def to_payload(self, value: Any) -> Optional[temporalio.api.common.v1.Payload]: See https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.to_json. """ - print("To payload:", self._schema_serializer) - print(f"value:{value}, type:{type(value)}") data = ( self._schema_serializer.to_json( value, exclude_unset=self._to_json_options.exclude_unset