66from temporalio .client import Client , Plugin as ClientPlugin
77from temporalio .worker import Interceptor
88from temporalio .runtime import Runtime , TelemetryConfig , OpenTelemetryConfig
9- from temporalio .converter import PayloadCodec
9+ from temporalio .converter import PayloadCodec , DataConverter
1010from temporalio .contrib .pydantic import pydantic_data_converter
1111
1212# class DateTimeJSONEncoder(AdvancedJSONEncoder):
@@ -86,6 +86,7 @@ async def get_temporal_client(
8686 metrics_url : str | None = None ,
8787 plugins : list [Any ] = [],
8888 payload_codec : PayloadCodec | None = None ,
89+ data_converter : DataConverter | None = None ,
8990) -> Client :
9091 """
9192 Create a Temporal client with plugin integration.
@@ -94,7 +95,14 @@ async def get_temporal_client(
9495 temporal_address: Temporal server address
9596 metrics_url: Optional metrics endpoint URL
9697 plugins: List of Temporal plugins to include
97- payload_codec: Optional payload codec for encoding/decoding payloads (e.g. encryption, compression)
98+ payload_codec: Optional payload codec for encoding/decoding payloads
99+ (e.g. encryption, compression). Cannot be combined with the
100+ OpenAIAgentsPlugin via this kwarg — see ``data_converter``.
101+ data_converter: Optional pre-built ``DataConverter``. Use this when
102+ composing the OpenAIAgentsPlugin with a payload codec: build a
103+ ``DataConverter(payload_converter_class=OpenAIPayloadConverter,
104+ payload_codec=...)`` and pass it here. Mutually exclusive with
105+ ``payload_codec``.
98106
99107 Returns:
100108 Configured Temporal client
@@ -103,29 +111,40 @@ async def get_temporal_client(
103111 if plugins :
104112 validate_client_plugins (plugins )
105113
106- # Check if OpenAI plugin is present - it needs to configure its own data converter
114+ if payload_codec is not None and data_converter is not None :
115+ raise ValueError (
116+ "Pass payload_codec inside `data_converter` "
117+ "(DataConverter(..., payload_codec=...)) instead of as a separate "
118+ "kwarg. Specifying both is ambiguous."
119+ )
120+
107121 # Lazy import to avoid pulling in opentelemetry.sdk for non-Temporal agents
108122 from temporalio .contrib .openai_agents import OpenAIAgentsPlugin
109123
110124 has_openai_plugin = any (isinstance (p , OpenAIAgentsPlugin ) for p in (plugins or []))
111125
112- if has_openai_plugin and payload_codec is not None :
126+ if has_openai_plugin and payload_codec is not None and data_converter is None :
113127 raise ValueError (
114- "payload_codec is not supported alongside OpenAIAgentsPlugin: the plugin "
115- "installs its own data converter and the codec would be silently ignored, "
116- "leaving payloads unencoded. Remove one or the other."
128+ "payload_codec passed as a kwarg alongside OpenAIAgentsPlugin would "
129+ "be silently dropped by the plugin's data-converter transformer. "
130+ "Build a DataConverter explicitly with "
131+ "`payload_converter_class=OpenAIPayloadConverter` (or a subclass) "
132+ "and `payload_codec=...`, then pass it via the `data_converter` "
133+ "kwarg instead."
117134 )
118135
119- connect_kwargs = {
136+ connect_kwargs : dict [ str , Any ] = {
120137 "target_host" : temporal_address ,
121138 "plugins" : plugins ,
122139 }
123140
124- if not has_openai_plugin :
125- data_converter = pydantic_data_converter
126- if payload_codec :
127- data_converter = dataclasses .replace (data_converter , payload_codec = payload_codec )
141+ if data_converter is not None :
128142 connect_kwargs ["data_converter" ] = data_converter
143+ elif not has_openai_plugin :
144+ dc = pydantic_data_converter
145+ if payload_codec :
146+ dc = dataclasses .replace (dc , payload_codec = payload_codec )
147+ connect_kwargs ["data_converter" ] = dc
129148
130149 if not metrics_url :
131150 client = await Client .connect (** connect_kwargs )
0 commit comments