diff --git a/docs/docs/integrations/chat/openai.ipynb b/docs/docs/integrations/chat/openai.ipynb index cb60106cf26..76a6f9b191f 100644 --- a/docs/docs/integrations/chat/openai.ipynb +++ b/docs/docs/integrations/chat/openai.ipynb @@ -408,7 +408,7 @@ "\n", ":::\n", "\n", - "OpenAI supports a [Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions) API that is oriented toward building [agentic](/docs/concepts/agents/) applications. It includes a suite of [built-in tools](https://platform.openai.com/docs/guides/tools?api-mode=responses), including web and file search. It also supports management of [conversation state](https://platform.openai.com/docs/guides/conversation-state?api-mode=responses), allowing you to continue a conversational thread without explicitly passing in previous messages.\n", + "OpenAI supports a [Responses](https://platform.openai.com/docs/guides/responses-vs-chat-completions) API that is oriented toward building [agentic](/docs/concepts/agents/) applications. It includes a suite of [built-in tools](https://platform.openai.com/docs/guides/tools?api-mode=responses), including web and file search. It also supports management of [conversation state](https://platform.openai.com/docs/guides/conversation-state?api-mode=responses), allowing you to continue a conversational thread without explicitly passing in previous messages, as well as the output from [reasoning processes](https://platform.openai.com/docs/guides/reasoning?api-mode=responses).\n", "\n", "`ChatOpenAI` will route to the Responses API if one of these features is used. You can also specify `use_responses_api=True` when instantiating `ChatOpenAI`.\n", "\n", @@ -1056,6 +1056,77 @@ "print(second_response.text())" ] }, + { + "cell_type": "markdown", + "id": "67bf5bd2-0935-40a0-b1cd-c6662b681d4b", + "metadata": {}, + "source": [ + "### Reasoning output\n", + "\n", + "Some OpenAI models will generate separate text content illustrating their reasoning process. See OpenAI's [reasoning documentation](https://platform.openai.com/docs/guides/reasoning?api-mode=responses) for details.\n", + "\n", + "OpenAI can return a summary of the model's reasoning (although it doesn't expose the raw reasoning tokens). To configure `ChatOpenAI` to return this summary, specify the `reasoning` parameter:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8d322f3a-0732-45ab-ac95-dfd4596e0d85", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Output: 3^3 = 27.\n", + "\n", + "Reasoning: {'id': 'rs_67fffc44b1c08191b6ca9bead6d832590433145b1786f809', 'summary': [{'text': '**Calculating 3 cubed**\\n\\nThe user wants to know the value of 3 to the power of 3. That\\'s straightforward—it equals 27. I can respond simply: \"3^3 equals 27.\" To provide a little clarity, I might add that it comes from multiplying 3 by itself twice: 3 * 3 * 3 = 27. So ultimately, my answer to the user is: 27. I think keeping it plain and simple is the best approach here!', 'type': 'summary_text'}], 'type': 'reasoning'}\n" + ] + } + ], + "source": [ + "from langchain_openai import ChatOpenAI\n", + "\n", + "reasoning = {\n", + " \"effort\": \"medium\", # 'low', 'medium', or 'high'\n", + " \"summary\": \"auto\", # 'detailed', 'auto', or None\n", + "}\n", + "\n", + "llm = ChatOpenAI(\n", + " model=\"o4-mini\",\n", + " use_responses_api=True,\n", + " model_kwargs={\"reasoning\": reasoning},\n", + ")\n", + "response = llm.invoke(\"What is 3^3?\")\n", + "\n", + "print(f\"Output: {response.text()}\\n\")\n", + "print(f\"Reasoning: {response.additional_kwargs['reasoning']}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d7dcc082-b7c8-41b7-a5e2-441b9679e41b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': 'rs_67fffc44b1c08191b6ca9bead6d832590433145b1786f809',\n", + " 'summary': [{'text': '**Calculating 3 cubed**\\n\\nThe user wants to know the value of 3 to the power of 3. That\\'s straightforward—it equals 27. I can respond simply: \"3^3 equals 27.\" To provide a little clarity, I might add that it comes from multiplying 3 by itself twice: 3 * 3 * 3 = 27. So ultimately, my answer to the user is: 27. I think keeping it plain and simple is the best approach here!',\n", + " 'type': 'summary_text'}],\n", + " 'type': 'reasoning'}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response.additional_kwargs[\"reasoning\"]" + ] + }, { "cell_type": "markdown", "id": "57e27714", diff --git a/libs/partners/openai/langchain_openai/chat_models/base.py b/libs/partners/openai/langchain_openai/chat_models/base.py index 6670a3b7668..29fb8e9cde8 100644 --- a/libs/partners/openai/langchain_openai/chat_models/base.py +++ b/libs/partners/openai/langchain_openai/chat_models/base.py @@ -2132,6 +2132,40 @@ class ChatOpenAI(BaseChatOpenAI): # type: ignore[override] "Your name is Bob. How can I help you today, Bob?" + .. dropdown:: Reasoning output + + OpenAI's Responses API supports `reasoning models `_ + that expose a summary of internal reasoning processes. + + .. code-block:: python + + from langchain_openai import ChatOpenAI + + reasoning = { + "effort": "medium", # 'low', 'medium', or 'high' + "summary": "auto", # 'detailed', 'auto', or None + } + + llm = ChatOpenAI( + model="o4-mini", use_responses_api=True, model_kwargs={"reasoning": reasoning} + ) + response = llm.invoke("What is 3^3?") + + print(f"Output: {response.text()}") + print(f"Reasoning: {response.additional_kwargs['reasoning']}") + + .. code-block:: none + + Output: 3^3 = 27. + + Reasoning: { + 'id': 'rs_67fffc44b1c08191b6ca9bead6d832590433145b1786f809', + 'summary': [ + {'text': 'The user wants to know...', 'type': 'summary_text'} + ], + 'type': 'reasoning' + } + .. dropdown:: Structured output .. code-block:: python