Skip to content

Commit a9657eb

Browse files
authored
bedrock: allow empty system prompt (#1405)
1 parent 25b4ed1 commit a9657eb

File tree

3 files changed

+71
-11
lines changed

3 files changed

+71
-11
lines changed

pydantic_ai_slim/pydantic_ai/models/bedrock.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import anyio
1212
import anyio.to_thread
13+
from mypy_boto3_bedrock_runtime.type_defs import ConverseRequestTypeDef, SystemContentBlockTypeDef
1314
from typing_extensions import ParamSpec, assert_never
1415

1516
from pydantic_ai import _utils, result
@@ -258,20 +259,19 @@ async def _messages_create(
258259
else:
259260
tool_choice = {'auto': {}}
260261

261-
system_prompt, bedrock_messages = await self._map_message(messages)
262+
system_prompt, bedrock_messages = await self._map_messages(messages)
262263
inference_config = self._map_inference_config(model_settings)
263264

264-
params = {
265+
params: ConverseRequestTypeDef = {
265266
'modelId': self.model_name,
266267
'messages': bedrock_messages,
267-
'system': [{'text': system_prompt}],
268+
'system': system_prompt,
268269
'inferenceConfig': inference_config,
269-
**(
270-
{'toolConfig': {'tools': tools, **({'toolChoice': tool_choice} if tool_choice else {})}}
271-
if tools
272-
else {}
273-
),
274270
}
271+
if tools:
272+
params['toolConfig'] = {'tools': tools}
273+
if tool_choice:
274+
params['toolConfig']['toolChoice'] = tool_choice
275275

276276
if stream:
277277
model_response = await anyio.to_thread.run_sync(functools.partial(self.client.converse_stream, **params))
@@ -299,15 +299,17 @@ def _map_inference_config(
299299

300300
return inference_config
301301

302-
async def _map_message(self, messages: list[ModelMessage]) -> tuple[str, list[MessageUnionTypeDef]]:
302+
async def _map_messages(
303+
self, messages: list[ModelMessage]
304+
) -> tuple[list[SystemContentBlockTypeDef], list[MessageUnionTypeDef]]:
303305
"""Just maps a `pydantic_ai.Message` to the Bedrock `MessageUnionTypeDef`."""
304-
system_prompt: str = ''
306+
system_prompt: list[SystemContentBlockTypeDef] = []
305307
bedrock_messages: list[MessageUnionTypeDef] = []
306308
for m in messages:
307309
if isinstance(m, ModelRequest):
308310
for part in m.parts:
309311
if isinstance(part, SystemPromptPart):
310-
system_prompt += part.content
312+
system_prompt.append({'text': part.content})
311313
elif isinstance(part, UserPromptPart):
312314
bedrock_messages.extend(await self._map_user_prompt(part))
313315
elif isinstance(part, ToolReturnPart):
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
interactions:
2+
- request:
3+
body: '{"messages": [{"role": "user", "content": [{"text": "What is the capital of France?"}]}], "system": [], "inferenceConfig":
4+
{}}'
5+
headers:
6+
amz-sdk-invocation-id:
7+
- !!binary |
8+
ZjdiY2FlMDgtYzQxMi00OTgxLWJiOGUtNzZiN2QzNGRmMDM2
9+
amz-sdk-request:
10+
- !!binary |
11+
YXR0ZW1wdD0x
12+
content-length:
13+
- '126'
14+
content-type:
15+
- !!binary |
16+
YXBwbGljYXRpb24vanNvbg==
17+
method: POST
18+
uri: https://bedrock-runtime.us-east-1.amazonaws.com/model/us.amazon.nova-micro-v1%3A0/converse
19+
response:
20+
headers:
21+
connection:
22+
- keep-alive
23+
content-length:
24+
- '784'
25+
content-type:
26+
- application/json
27+
parsed_body:
28+
metrics:
29+
latencyMs: 1406
30+
output:
31+
message:
32+
content:
33+
- text: The capital of France is Paris. Paris, officially known as "Ville de Paris," is not only the capital city
34+
but also the most populous city in France. It is located in the northern central part of the country along the
35+
Seine River. Paris is a major global city, renowned for its cultural, political, economic, and social influence.
36+
It is famous for its landmarks such as the Eiffel Tower, the Louvre Museum, Notre-Dame Cathedral, and the Champs-Élysées,
37+
among many other historic and modern attractions. The city has played a significant role in the history of art,
38+
fashion, gastronomy, and science.
39+
role: assistant
40+
stopReason: end_turn
41+
usage:
42+
inputTokens: 7
43+
outputTokens: 126
44+
totalTokens: 133
45+
status:
46+
code: 200
47+
message: OK
48+
version: 1

tests/models/test_bedrock.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,13 @@ async def test_text_as_binary_content_input(allow_model_requests: None, bedrock_
468468
469469
Since this is a test document, it probably doesn't contain any meaningful or specific information beyond what is necessary to serve its testing purpose. If you have specific questions about the format, structure, or any particular element within the document, feel free to ask!\
470470
""")
471+
472+
473+
@pytest.mark.vcr()
474+
async def test_bedrock_empty_system_prompt(allow_model_requests: None, bedrock_provider: BedrockProvider):
475+
m = BedrockConverseModel('us.amazon.nova-micro-v1:0', provider=bedrock_provider)
476+
agent = Agent(m)
477+
result = await agent.run('What is the capital of France?')
478+
assert result.data == snapshot(
479+
'The capital of France is Paris. Paris, officially known as "Ville de Paris," is not only the capital city but also the most populous city in France. It is located in the northern central part of the country along the Seine River. Paris is a major global city, renowned for its cultural, political, economic, and social influence. It is famous for its landmarks such as the Eiffel Tower, the Louvre Museum, Notre-Dame Cathedral, and the Champs-Élysées, among many other historic and modern attractions. The city has played a significant role in the history of art, fashion, gastronomy, and science.'
480+
)

0 commit comments

Comments
 (0)