Skip to content
Open
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
10 changes: 9 additions & 1 deletion pyrit/prompt_target/common/prompt_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ def _validate_request(self, *, normalized_conversation: list[Message]) -> None:

Called after the normalization pipeline has run. Validates the last
message (the current request) for piece count, data types, and checks
whether the full conversation violates multi-turn constraints.
whether the full conversation violates multi-turn or system prompt constraints.

Args:
normalized_conversation: The normalized conversation to validate.
Expand Down Expand Up @@ -438,6 +438,14 @@ def _validate_request(self, *, normalized_conversation: list[Message]) -> None:
if not self.configuration.includes(capability=CapabilityName.MULTI_TURN) and len(normalized_conversation) > 1:
raise ValueError(f"This target only supports a single turn conversation. {custom_configuration_message}")

if not self.configuration.includes(capability=CapabilityName.SYSTEM_PROMPT) and any(
piece.api_role == "system" for turn in normalized_conversation for piece in turn.message_pieces
):
raise ValueError(
"This target does not support system prompts and no normalizer adapted the system message. "
f"{custom_configuration_message}"
)

@staticmethod
def _get_web_search_config(message: Message) -> WebSearchConfig:
configs = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,42 @@ async def test_openai_chat_target_memory_not_mutated():
assert memory_conversation[0].get_piece().api_role == "system"


@pytest.mark.usefixtures("patch_central_database")
async def test_openai_chat_target_raises_for_unsupported_system_prompt_under_raise_policy():
"""A system message the target cannot accept must not reach the provider under RAISE."""
target = OpenAIChatTarget(
model_name="gpt-4o",
endpoint="https://mock.azure.com/",
api_key="mock-api-key",
custom_configuration=TargetConfiguration(
capabilities=TargetCapabilities(
supports_multi_turn=True,
supports_editable_history=True,
supports_system_prompt=False,
input_modalities=frozenset({frozenset(["text"])}),
),
policy=CapabilityHandlingPolicy(
behaviors={
CapabilityName.MULTI_TURN: UnsupportedCapabilityBehavior.RAISE,
CapabilityName.SYSTEM_PROMPT: UnsupportedCapabilityBehavior.RAISE,
}
),
),
)

mock_memory = MagicMock(spec=MemoryInterface)
mock_memory.get_conversation_messages_async = AsyncMock(
return_value=[_make_message(role="system", content="Policy")]
)
target._memory = mock_memory
target._async_client.chat.completions.create = AsyncMock(return_value=_create_mock_chat_completion())

with pytest.raises(ValueError, match="does not support system prompts"):
await target.send_prompt_async(message=_make_message(role="user", content="Question"))

target._async_client.chat.completions.create.assert_not_called()


# ---------------------------------------------------------------------------
# OpenAIResponseTarget — normalize_async is called
# ---------------------------------------------------------------------------
Expand Down
Loading