3535 )
3636 from openai .types .responses .easy_input_message_param import EasyInputMessageParam
3737 from openai .types .responses .tool_param import ToolParam
38+ from openai .types .shared .reasoning_effort import ReasoningEffort
39+ from openai .types .shared_params .reasoning import Reasoning
3840 from openai .types .shared_params .responses_model import ResponsesModel
3941
4042 from .types .openai import ChatClientArgs
@@ -47,8 +49,9 @@ def ChatOpenAI(
4749 * ,
4850 system_prompt : Optional [str ] = None ,
4951 model : "Optional[ResponsesModel | str]" = None ,
50- api_key : Optional [str ] = None ,
5152 base_url : str = "https://api.openai.com/v1" ,
53+ reasoning : "Optional[ReasoningEffort | Reasoning]" = None ,
54+ api_key : Optional [str ] = None ,
5255 kwargs : Optional ["ChatClientArgs" ] = None ,
5356) -> Chat ["SubmitInputArgs" , Response ]:
5457 """
@@ -87,12 +90,15 @@ def ChatOpenAI(
8790 The model to use for the chat. The default, None, will pick a reasonable
8891 default, and warn you about it. We strongly recommend explicitly
8992 choosing a model for all but the most casual use.
93+ base_url
94+ The base URL to the endpoint; the default uses OpenAI.
95+ reasoning
96+ The reasoning effort to use (for reasoning-capable models like the o and
97+ gpt-5 series).
9098 api_key
9199 The API key to use for authentication. You generally should not supply
92100 this directly, but instead set the `OPENAI_API_KEY` environment
93101 variable.
94- base_url
95- The base URL to the endpoint; the default uses OpenAI.
96102 kwargs
97103 Additional arguments to pass to the `openai.OpenAI()` client
98104 constructor.
@@ -146,6 +152,14 @@ def ChatOpenAI(
146152 if model is None :
147153 model = log_model_default ("gpt-4.1" )
148154
155+ kwargs_chat : "SubmitInputArgs" = {}
156+ if reasoning is not None :
157+ if not is_reasoning_model (model ):
158+ warnings .warn (f"Model { model } is not reasoning-capable" , UserWarning )
159+ if isinstance (reasoning , str ):
160+ reasoning = {"effort" : reasoning , "summary" : "auto" }
161+ kwargs_chat = {"reasoning" : reasoning }
162+
149163 return Chat (
150164 provider = OpenAIProvider (
151165 api_key = api_key ,
@@ -154,6 +168,7 @@ def ChatOpenAI(
154168 kwargs = kwargs ,
155169 ),
156170 system_prompt = system_prompt ,
171+ kwargs_chat = kwargs_chat ,
157172 )
158173
159174
@@ -239,7 +254,7 @@ def _chat_perform_args(
239254
240255 # Request reasoning content for reasoning models
241256 include = []
242- if self . _is_reasoning (self .model ):
257+ if is_reasoning_model (self .model ):
243258 include .append ("reasoning.encrypted_content" )
244259
245260 if "log_probs" in kwargs_full :
@@ -254,7 +269,14 @@ def _chat_perform_args(
254269
255270 def stream_text (self , chunk ):
256271 if chunk .type == "response.output_text.delta" :
272+ # https://platform.openai.com/docs/api-reference/responses-streaming/response/output_text/delta
273+ return chunk .delta
274+ if chunk .type == "response.reasoning_summary_text.delta" :
275+ # https://platform.openai.com/docs/api-reference/responses-streaming/response/reasoning_summary_text/delta
257276 return chunk .delta
277+ if chunk .type == "response.reasoning_summary_text.done" :
278+ # https://platform.openai.com/docs/api-reference/responses-streaming/response/reasoning_summary_text/done
279+ return "\n \n "
258280 return None
259281
260282 def stream_merge_chunks (self , completion , chunk ):
@@ -337,11 +359,6 @@ def _response_as_turn(completion: Response, has_data_model: bool) -> Turn:
337359 completion = completion ,
338360 )
339361
340- @staticmethod
341- def _is_reasoning (model : str ) -> bool :
342- # https://platform.openai.com/docs/models/compare
343- return model .startswith ("o" ) or model .startswith ("gpt-5" )
344-
345362 @staticmethod
346363 def _turns_as_inputs (turns : list [Turn ]) -> "list[ResponseInputItemParam]" :
347364 res : "list[ResponseInputItemParam]" = []
@@ -456,3 +473,8 @@ def as_input_param(content: Content, role: Role) -> "ResponseInputItemParam":
456473
457474def as_message (x : "ResponseInputContentParam" , role : Role ) -> "EasyInputMessageParam" :
458475 return {"role" : role , "content" : [x ]}
476+
477+
478+ def is_reasoning_model (model : str ) -> bool :
479+ # https://platform.openai.com/docs/models/compare
480+ return model .startswith ("o" ) or model .startswith ("gpt-5" )
0 commit comments