Skip to content

Commit 8cae18c

Browse files
fix(models): allow setter on system_prompt and system_prompt_content (#1171)
1 parent 57e2081 commit 8cae18c

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

src/strands/agent/agent.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,8 @@ def __init__(
287287
"""
288288
self.model = BedrockModel() if not model else BedrockModel(model_id=model) if isinstance(model, str) else model
289289
self.messages = messages if messages is not None else []
290-
# initializing self.system_prompt for backwards compatibility
291-
self.system_prompt, self._system_prompt_content = self._initialize_system_prompt(system_prompt)
290+
# initializing self._system_prompt for backwards compatibility
291+
self._system_prompt, self._system_prompt_content = self._initialize_system_prompt(system_prompt)
292292
self._default_structured_output_model = structured_output_model
293293
self.agent_id = _identifier.validate(agent_id or _DEFAULT_AGENT_ID, _identifier.Identifier.AGENT)
294294
self.name = name or _DEFAULT_AGENT_NAME
@@ -365,6 +365,35 @@ def __init__(
365365
self.hooks.add_hook(hook)
366366
self.hooks.invoke_callbacks(AgentInitializedEvent(agent=self))
367367

368+
@property
369+
def system_prompt(self) -> str | None:
370+
"""Get the system prompt as a string for backwards compatibility.
371+
372+
Returns the system prompt as a concatenated string when it contains text content,
373+
or None if no text content is present. This maintains backwards compatibility
374+
with existing code that expects system_prompt to be a string.
375+
376+
Returns:
377+
The system prompt as a string, or None if no text content exists.
378+
"""
379+
return self._system_prompt
380+
381+
@system_prompt.setter
382+
def system_prompt(self, value: str | list[SystemContentBlock] | None) -> None:
383+
"""Set the system prompt and update internal content representation.
384+
385+
Accepts either a string or list of SystemContentBlock objects.
386+
When set, both the backwards-compatible string representation and the internal
387+
content block representation are updated to maintain consistency.
388+
389+
Args:
390+
value: System prompt as string, list of SystemContentBlock objects, or None.
391+
- str: Simple text prompt (most common use case)
392+
- list[SystemContentBlock]: Content blocks with features like caching
393+
- None: Clear the system prompt
394+
"""
395+
self._system_prompt, self._system_prompt_content = self._initialize_system_prompt(value)
396+
368397
@property
369398
def tool(self) -> ToolCaller:
370399
"""Call tool as a function.

tests/strands/agent/test_agent.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,37 @@ async def test_stream_async_multi_modal_input(mock_model, agent, agenerator, ali
12211221
assert tru_message == exp_message
12221222

12231223

1224+
def test_system_prompt_setter_string():
1225+
"""Test that setting system_prompt with string updates both internal fields."""
1226+
agent = Agent(system_prompt="initial prompt")
1227+
1228+
agent.system_prompt = "updated prompt"
1229+
1230+
assert agent.system_prompt == "updated prompt"
1231+
assert agent._system_prompt_content == [{"text": "updated prompt"}]
1232+
1233+
1234+
def test_system_prompt_setter_list():
1235+
"""Test that setting system_prompt with list updates both internal fields."""
1236+
agent = Agent()
1237+
1238+
content_blocks = [{"text": "You are helpful"}, {"cache_control": {"type": "ephemeral"}}]
1239+
agent.system_prompt = content_blocks
1240+
1241+
assert agent.system_prompt == "You are helpful"
1242+
assert agent._system_prompt_content == content_blocks
1243+
1244+
1245+
def test_system_prompt_setter_none():
1246+
"""Test that setting system_prompt to None clears both internal fields."""
1247+
agent = Agent(system_prompt="initial prompt")
1248+
1249+
agent.system_prompt = None
1250+
1251+
assert agent.system_prompt is None
1252+
assert agent._system_prompt_content is None
1253+
1254+
12241255
@pytest.mark.asyncio
12251256
async def test_stream_async_passes_invocation_state(agent, mock_model, mock_event_loop_cycle, agenerator, alist):
12261257
mock_model.mock_stream.side_effect = [

0 commit comments

Comments
 (0)