Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions src/strands/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@ def __init__(
"""
self.model = BedrockModel() if not model else BedrockModel(model_id=model) if isinstance(model, str) else model
self.messages = messages if messages is not None else []
# initializing self.system_prompt for backwards compatibility
self.system_prompt, self._system_prompt_content = self._initialize_system_prompt(system_prompt)
# initializing self._system_prompt for backwards compatibility
self._system_prompt, self._system_prompt_content = self._initialize_system_prompt(system_prompt)
self._default_structured_output_model = structured_output_model
self.agent_id = _identifier.validate(agent_id or _DEFAULT_AGENT_ID, _identifier.Identifier.AGENT)
self.name = name or _DEFAULT_AGENT_NAME
Expand Down Expand Up @@ -365,6 +365,35 @@ def __init__(
self.hooks.add_hook(hook)
self.hooks.invoke_callbacks(AgentInitializedEvent(agent=self))

@property
def system_prompt(self) -> str | None:
"""Get the system prompt as a string for backwards compatibility.

Returns the system prompt as a concatenated string when it contains text content,
or None if no text content is present. This maintains backwards compatibility
with existing code that expects system_prompt to be a string.

Returns:
The system prompt as a string, or None if no text content exists.
"""
return self._system_prompt

@system_prompt.setter
def system_prompt(self, value: str | list[SystemContentBlock] | None) -> None:
"""Set the system prompt and update internal content representation.

Accepts either a string or list of SystemContentBlock objects.
When set, both the backwards-compatible string representation and the internal
content block representation are updated to maintain consistency.

Args:
value: System prompt as string, list of SystemContentBlock objects, or None.
- str: Simple text prompt (most common use case)
- list[SystemContentBlock]: Content blocks with features like caching
- None: Clear the system prompt
"""
self._system_prompt, self._system_prompt_content = self._initialize_system_prompt(value)

@property
def tool(self) -> ToolCaller:
"""Call tool as a function.
Expand Down
31 changes: 31 additions & 0 deletions tests/strands/agent/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1221,6 +1221,37 @@ async def test_stream_async_multi_modal_input(mock_model, agent, agenerator, ali
assert tru_message == exp_message


def test_system_prompt_setter_string():
"""Test that setting system_prompt with string updates both internal fields."""
agent = Agent(system_prompt="initial prompt")

agent.system_prompt = "updated prompt"

assert agent.system_prompt == "updated prompt"
assert agent._system_prompt_content == [{"text": "updated prompt"}]


def test_system_prompt_setter_list():
"""Test that setting system_prompt with list updates both internal fields."""
agent = Agent()

content_blocks = [{"text": "You are helpful"}, {"cache_control": {"type": "ephemeral"}}]
agent.system_prompt = content_blocks

assert agent.system_prompt == "You are helpful"
assert agent._system_prompt_content == content_blocks


def test_system_prompt_setter_none():
"""Test that setting system_prompt to None clears both internal fields."""
agent = Agent(system_prompt="initial prompt")

agent.system_prompt = None

assert agent.system_prompt is None
assert agent._system_prompt_content is None


@pytest.mark.asyncio
async def test_stream_async_passes_invocation_state(agent, mock_model, mock_event_loop_cycle, agenerator, alist):
mock_model.mock_stream.side_effect = [
Expand Down
Loading