Skip to content

Commit e9fe30b

Browse files
committed
fix: concat text blocks for system_prompt
1 parent 2d4bc80 commit e9fe30b

File tree

4 files changed

+23
-26
lines changed

4 files changed

+23
-26
lines changed

src/strands/agent/agent.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def _find_normalized_tool_name(self, name: str) -> str:
213213

214214
def __init__(
215215
self,
216-
model: Model | str | None = None,
216+
model: Union[Model, str, None] = None,
217217
messages: Optional[Messages] = None,
218218
tools: Optional[list[Union[str, dict[str, str], "ToolProvider", Any]]] = None,
219219
system_prompt: Optional[str | list[SystemContentBlock]] = None,
@@ -224,7 +224,7 @@ def __init__(
224224
conversation_manager: Optional[ConversationManager] = None,
225225
record_direct_tool_call: bool = True,
226226
load_tools_from_directory: bool = False,
227-
trace_attributes: Mapping[str, AttributeValue] | None = None,
227+
trace_attributes: Optional[Mapping[str, AttributeValue]] = None,
228228
*,
229229
agent_id: Optional[str] = None,
230230
name: Optional[str] = None,
@@ -970,27 +970,27 @@ def _filter_tool_parameters_for_recording(self, tool_name: str, input_params: di
970970

971971
def _initialize_system_prompt(
972972
self, system_prompt: str | list[SystemContentBlock] | None
973-
) -> tuple[str | None, list[SystemContentBlock]]:
973+
) -> tuple[str | None, list[SystemContentBlock] | None]:
974974
"""Initialize system prompt fields from constructor input.
975975
976976
Maintains backwards compatibility by keeping system_prompt as str when string input
977977
provided, avoiding breaking existing consumers.
978978
979979
Maps system_prompt input to both string and content block representations:
980980
- If string: system_prompt=string, _system_prompt_content=[{text: string}]
981-
- If list with single text element: system_prompt=text, _system_prompt_content=list
982-
- If list (other cases): system_prompt=None, _system_prompt_content=list
983-
- If None: system_prompt=None, _system_prompt_content=[]
981+
- If list with text elements: system_prompt=concatenated_text, _system_prompt_content=list
982+
- If list without text elements: system_prompt=None, _system_prompt_content=list
983+
- If None: system_prompt=None, _system_prompt_content=None
984984
"""
985985
if isinstance(system_prompt, str):
986986
return system_prompt, [{"text": system_prompt}]
987987
elif isinstance(system_prompt, list):
988-
# If list has single element with text, also set system_prompt for backwards compatibility
989-
if len(system_prompt) == 1 and "text" in system_prompt[0]:
990-
return system_prompt[0]["text"], system_prompt
991-
return None, system_prompt
988+
# Concatenate all text elements for backwards compatibility, None if no text found
989+
text_parts = [block["text"] for block in system_prompt if "text" in block]
990+
system_prompt_str = "\n".join(text_parts) if text_parts else None
991+
return system_prompt_str, system_prompt
992992
else:
993-
return None, []
993+
return None, None
994994

995995
def _append_message(self, message: Message) -> None:
996996
"""Appends a message to the agent's list of messages and invokes the callbacks for the MessageCreatedEvent."""

src/strands/event_loop/streaming.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -427,12 +427,11 @@ async def stream_messages(
427427
428428
Args:
429429
model: Model provider.
430+
system_prompt: The system prompt string, used for backwards compatibility with models that expect it.
430431
messages: List of messages to send.
431432
tool_specs: The list of tool specs.
432-
system_prompt: Optional system prompt string for backwards compatibility.
433433
tool_choice: Optional tool choice constraint for forcing specific tool usage.
434-
system_prompt_content: The system prompt content blocks to send.
435-
434+
system_prompt_content: The authoritative system prompt content blocks that always contains the system prompt data.
436435
Yields:
437436
The reason for stopping, the final message, and the usage metrics
438437
"""
@@ -449,5 +448,5 @@ async def stream_messages(
449448
system_prompt_content=system_prompt_content,
450449
)
451450

452-
async for event in process_stream(chunks):
451+
async for event in process_stream(chunks, start_time):
453452
yield event

src/strands/models/bedrock.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,8 @@ def _format_request(
191191
self,
192192
messages: Messages,
193193
tool_specs: Optional[list[ToolSpec]] = None,
194-
system_prompt: Optional[str] = None,
195-
tool_choice: ToolChoice | None = None,
196194
system_prompt_content: Optional[list[SystemContentBlock]] = None,
195+
tool_choice: ToolChoice | None = None,
197196
) -> dict[str, Any]:
198197
"""Format a Bedrock converse stream request.
199198
@@ -632,7 +631,7 @@ def callback(event: Optional[StreamEvent] = None) -> None:
632631
loop = asyncio.get_event_loop()
633632
queue: asyncio.Queue[Optional[StreamEvent]] = asyncio.Queue()
634633

635-
thread = asyncio.to_thread(self._stream, callback, messages, tool_specs, system_prompt, tool_choice, system_prompt_content)
634+
thread = asyncio.to_thread(self._stream, callback, messages, tool_specs, system_prompt_content, tool_choice)
636635
task = asyncio.create_task(thread)
637636

638637
while True:
@@ -649,9 +648,8 @@ def _stream(
649648
callback: Callable[..., None],
650649
messages: Messages,
651650
tool_specs: Optional[list[ToolSpec]] = None,
652-
system_prompt: Optional[str] = None,
653-
tool_choice: ToolChoice | None = None,
654651
system_prompt_content: Optional[list[SystemContentBlock]] = None,
652+
tool_choice: ToolChoice | None = None,
655653
) -> None:
656654
"""Stream conversation with the Bedrock model.
657655
@@ -662,16 +660,16 @@ def _stream(
662660
callback: Function to send events to the main thread.
663661
messages: List of message objects to be processed by the model.
664662
tool_specs: List of tool specifications to make available to the model.
665-
tool_choice: Selection strategy for tool invocation.
666663
system_prompt_content: System prompt content blocks to provide context to the model.
664+
tool_choice: Selection strategy for tool invocation.
667665
668666
Raises:
669667
ContextWindowOverflowException: If the input exceeds the model's context window.
670668
ModelThrottledException: If the model service is throttling requests.
671669
"""
672670
try:
673671
logger.debug("formatting request")
674-
request = self._format_request(messages, tool_specs, None, tool_choice, system_prompt_content)
672+
request = self._format_request(messages, tool_specs, system_prompt_content, tool_choice)
675673
logger.debug("request=<%s>", request)
676674

677675
logger.debug("invoking model")

tests/strands/agent/test_agent.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,7 +2193,7 @@ def test_agent_multiple_blocks_system_prompt():
21932193
]
21942194
agent = Agent(system_prompt=system_prompt_content)
21952195

2196-
assert agent.system_prompt is None
2196+
assert agent.system_prompt == "You are a helpful assistant.\nAdditional instructions."
21972197
assert agent._system_prompt_content == system_prompt_content
21982198

21992199

@@ -2211,7 +2211,7 @@ def test_agent_none_system_prompt():
22112211
agent = Agent(system_prompt=None)
22122212

22132213
assert agent.system_prompt is None
2214-
assert agent._system_prompt_content == []
2214+
assert agent._system_prompt_content == None
22152215

22162216

22172217
def test_agent_empty_list_system_prompt():
@@ -2270,7 +2270,7 @@ def test_agent_initialize_system_prompt_multiple_blocks_input():
22702270
]
22712271
result = agent._initialize_system_prompt(input_blocks)
22722272

2273-
assert result == (None, input_blocks)
2273+
assert result == ("First block\nSecond block", input_blocks)
22742274

22752275

22762276
def test_agent_initialize_system_prompt_single_non_text_block_input():
@@ -2287,7 +2287,7 @@ def test_agent_initialize_system_prompt_none_input():
22872287
agent = Agent()
22882288
result = agent._initialize_system_prompt(None)
22892289

2290-
assert result == (None, [])
2290+
assert result == (None, None)
22912291

22922292

22932293
def test_agent_initialize_system_prompt_empty_list_input():

0 commit comments

Comments
 (0)