Skip to content

Commit 1e049bd

Browse files
authored
Add generate_summary and truncation to OpenAIResponsesModelSettings (#1328)
1 parent 546fd2c commit 1e049bd

File tree

3 files changed

+162
-11
lines changed

3 files changed

+162
-11
lines changed

pydantic_ai_slim/pydantic_ai/models/openai.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
from datetime import datetime, timezone
99
from typing import Literal, Union, cast, overload
1010

11-
from openai import NotGiven
12-
from openai.types import Reasoning
1311
from typing_extensions import assert_never
1412

1513
from pydantic_ai.providers import Provider, infer_provider
@@ -44,7 +42,7 @@
4442
)
4543

4644
try:
47-
from openai import NOT_GIVEN, APIStatusError, AsyncOpenAI, AsyncStream
45+
from openai import NOT_GIVEN, APIStatusError, AsyncOpenAI, AsyncStream, NotGiven
4846
from openai.types import ChatModel, chat, responses
4947
from openai.types.chat import (
5048
ChatCompletionChunk,
@@ -95,8 +93,7 @@ class OpenAIModelSettings(ModelSettings, total=False):
9593
"""
9694

9795
openai_reasoning_effort: ReasoningEffort
98-
"""
99-
Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning).
96+
"""Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning).
10097
10198
Currently supported values are `low`, `medium`, and `high`. Reducing reasoning effort can
10299
result in faster responses and fewer tokens used on reasoning in a response.
@@ -121,6 +118,27 @@ class OpenAIResponsesModelSettings(OpenAIModelSettings, total=False):
121118
See [OpenAI's built-in tools](https://platform.openai.com/docs/guides/tools?api-mode=responses) for more details.
122119
"""
123120

121+
openai_reasoning_generate_summary: Literal['detailed', 'concise']
122+
"""A summary of the reasoning performed by the model.
123+
124+
This can be useful for debugging and understanding the model's reasoning process.
125+
One of `concise` or `detailed`.
126+
127+
Check the [OpenAI Computer use documentation](https://platform.openai.com/docs/guides/tools-computer-use#1-send-a-request-to-the-model)
128+
for more details.
129+
"""
130+
131+
openai_truncation: Literal['disabled', 'auto']
132+
"""The truncation strategy to use for the model response.
133+
134+
It can be either:
135+
- `disabled` (default): If a model response will exceed the context window size for a model, the
136+
request will fail with a 400 error.
137+
- `auto`: If the context of this response and previous ones exceeds the model's context window size,
138+
the model will truncate the response to fit the context window by dropping input items in the
139+
middle of the conversation.
140+
"""
141+
124142

125143
@dataclass(init=False)
126144
class OpenAIModel(Model):
@@ -567,12 +585,7 @@ async def _responses_create(
567585
tool_choice = 'auto'
568586

569587
system_prompt, openai_messages = await self._map_message(messages)
570-
571-
reasoning_effort = model_settings.get('openai_reasoning_effort', NOT_GIVEN)
572-
if not isinstance(reasoning_effort, NotGiven):
573-
reasoning = Reasoning(effort=reasoning_effort)
574-
else:
575-
reasoning = NOT_GIVEN
588+
reasoning = self._get_reasoning(model_settings)
576589

577590
try:
578591
return await self.client.responses.create(
@@ -586,6 +599,7 @@ async def _responses_create(
586599
stream=stream,
587600
temperature=model_settings.get('temperature', NOT_GIVEN),
588601
top_p=model_settings.get('top_p', NOT_GIVEN),
602+
truncation=model_settings.get('openai_truncation', NOT_GIVEN),
589603
timeout=model_settings.get('timeout', NOT_GIVEN),
590604
reasoning=reasoning,
591605
user=model_settings.get('user', NOT_GIVEN),
@@ -595,6 +609,14 @@ async def _responses_create(
595609
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
596610
raise
597611

612+
def _get_reasoning(self, model_settings: OpenAIResponsesModelSettings) -> Reasoning | NotGiven:
613+
reasoning_effort = model_settings.get('openai_reasoning_effort', None)
614+
reasoning_generate_summary = model_settings.get('openai_reasoning_generate_summary', None)
615+
616+
if reasoning_effort is None and reasoning_generate_summary is None:
617+
return NOT_GIVEN
618+
return Reasoning(effort=reasoning_effort, generate_summary=reasoning_generate_summary)
619+
598620
def _get_tools(self, model_request_parameters: ModelRequestParameters) -> list[responses.FunctionToolParam]:
599621
tools = [self._map_tool_definition(r) for r in model_request_parameters.function_tools]
600622
if model_request_parameters.result_tools:
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
interactions:
2+
- request:
3+
headers:
4+
accept:
5+
- application/json
6+
accept-encoding:
7+
- gzip, deflate
8+
connection:
9+
- keep-alive
10+
content-length:
11+
- '218'
12+
content-type:
13+
- application/json
14+
host:
15+
- api.openai.com
16+
method: POST
17+
parsed_body:
18+
input:
19+
- content: What should I do to cross the street?
20+
role: user
21+
instructions: ''
22+
model: computer-use-preview
23+
reasoning:
24+
effort: null
25+
generate_summary: concise
26+
stream: false
27+
truncation: auto
28+
uri: https://api.openai.com/v1/responses
29+
response:
30+
headers:
31+
alt-svc:
32+
- h3=":443"; ma=86400
33+
connection:
34+
- keep-alive
35+
content-length:
36+
- '1976'
37+
content-type:
38+
- application/json
39+
openai-organization:
40+
- pydantic-28gund
41+
openai-processing-ms:
42+
- '2793'
43+
openai-version:
44+
- '2020-10-01'
45+
strict-transport-security:
46+
- max-age=31536000; includeSubDomains; preload
47+
transfer-encoding:
48+
- chunked
49+
parsed_body:
50+
created_at: 1743514924
51+
error: null
52+
id: resp_67ebed2c92cc81919d2fe37992a6a78b0e0766e7260ad9b9
53+
incomplete_details: null
54+
instructions: ''
55+
max_output_tokens: null
56+
metadata: {}
57+
model: computer-use-preview-2025-03-11
58+
object: response
59+
output:
60+
- content:
61+
- annotations: []
62+
text: |-
63+
To cross the street safely, follow these steps:
64+
65+
1. **Use a Crosswalk**: Always use a designated crosswalk or pedestrian crossing whenever available.
66+
2. **Press the Button**: If there is a pedestrian signal button, press it and wait for the signal.
67+
3. **Look Both Ways**: Look left, right, and left again before stepping off the curb.
68+
4. **Wait for the Signal**: Cross only when the pedestrian signal indicates it is safe to do so or when there is a clear gap in traffic.
69+
5. **Stay Alert**: Be mindful of turning vehicles and stay attentive while crossing.
70+
6. **Walk, Don't Run**: Walk across the street; running can increase the risk of falling or not noticing an oncoming vehicle.
71+
72+
Always follow local traffic rules and be cautious, even when crossing at a crosswalk. Safety is the priority.
73+
type: output_text
74+
id: msg_67ebed2db814819195e77f3dd1f057640e0766e7260ad9b9
75+
role: assistant
76+
status: completed
77+
type: message
78+
parallel_tool_calls: true
79+
previous_response_id: null
80+
reasoning:
81+
effort: medium
82+
generate_summary: concise
83+
status: completed
84+
store: true
85+
temperature: 1.0
86+
text:
87+
format:
88+
type: text
89+
tool_choice: auto
90+
tools: []
91+
top_p: 1.0
92+
truncation: auto
93+
usage:
94+
input_tokens: 15
95+
input_tokens_details:
96+
cached_tokens: 0
97+
output_tokens: 180
98+
output_tokens_details:
99+
reasoning_tokens: 0
100+
total_tokens: 195
101+
user: null
102+
status:
103+
code: 200
104+
message: OK
105+
version: 1

tests/models/test_openai_responses.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,30 @@ async def test_openai_responses_reasoning_effort(allow_model_requests: None, ope
118118
)
119119

120120

121+
async def test_openai_responses_reasoning_generate_summary(allow_model_requests: None, openai_api_key: str):
122+
model = OpenAIResponsesModel('computer-use-preview', provider=OpenAIProvider(api_key=openai_api_key))
123+
agent = Agent(
124+
model=model,
125+
model_settings=OpenAIResponsesModelSettings(
126+
openai_reasoning_generate_summary='concise',
127+
openai_truncation='auto',
128+
),
129+
)
130+
result = await agent.run('What should I do to cross the street?')
131+
assert result.data == snapshot("""\
132+
To cross the street safely, follow these steps:
133+
134+
1. **Use a Crosswalk**: Always use a designated crosswalk or pedestrian crossing whenever available.
135+
2. **Press the Button**: If there is a pedestrian signal button, press it and wait for the signal.
136+
3. **Look Both Ways**: Look left, right, and left again before stepping off the curb.
137+
4. **Wait for the Signal**: Cross only when the pedestrian signal indicates it is safe to do so or when there is a clear gap in traffic.
138+
5. **Stay Alert**: Be mindful of turning vehicles and stay attentive while crossing.
139+
6. **Walk, Don't Run**: Walk across the street; running can increase the risk of falling or not noticing an oncoming vehicle.
140+
141+
Always follow local traffic rules and be cautious, even when crossing at a crosswalk. Safety is the priority.\
142+
""")
143+
144+
121145
async def test_openai_responses_system_prompt(allow_model_requests: None, openai_api_key: str):
122146
model = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key))
123147
agent = Agent(model=model, system_prompt='You are a helpful assistant.')

0 commit comments

Comments
 (0)