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
2 changes: 1 addition & 1 deletion pyrit/cli/_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ async def print_scenario_result_async(*, result_dict: dict[str, Any]) -> None:
Args:
result_dict: ``ScenarioResult.to_dict()`` payload from the REST API.
"""
from pyrit.models.scenario_result import ScenarioResult
from pyrit.models import ScenarioResult
from pyrit.output.scenario_result.pretty import PrettyScenarioResultMemoryPrinter

scenario_result = ScenarioResult.from_dict(result_dict)
Expand Down
1 change: 0 additions & 1 deletion pyrit/memory/memory_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
SeedSimulatedConversation,
SeedType,
)
from pyrit.models.scenario_result import ScenarioRunState

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we do something like

_MOVED_TO_MEMORY_STORAGE: dict[str, str] = {
?

if not, we should at least label the PR as breaking

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rich agrees with this comment but it is copilot generated: We went the other direction and removed the deep-path shims entirely instead of adding more. The rule we settled on: if a symbol only moves within pyrit.models and is still re-exported from the package root, there's no shim — from pyrit.models import ScenarioRunState (and every other symbol) keeps working, and that's the only import form we support/document. Warning shims are reserved for symbols that actually leave the package (e.g. the pyrit.memory.storage moves). For consistency I also deleted the older silent shims that were still lingering (attack_result, message, message_piece, strategy_result). The only thing that breaks is the uncanonical deep path from pyrit.models.scenario_result import ..., which was never the documented import.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(it got messy having so many deprecated shims, and I think when the import mostly stays the same it's likely okay to just move it)


logger = logging.getLogger(__name__)

Expand Down
16 changes: 9 additions & 7 deletions pyrit/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@
from typing import TYPE_CHECKING, Any

from pyrit.common.deprecation import print_deprecation_message
from pyrit.models.chat_message import (
ALLOWED_CHAT_MESSAGE_ROLES,
ChatMessage,
ChatMessagesDataset,
)
from pyrit.models.conversation_reference import ConversationReference, ConversationType
from pyrit.models.conversation_stats import ConversationStats
from pyrit.models.embeddings import EmbeddingData, EmbeddingResponse, EmbeddingSupport, EmbeddingUsageInformation
from pyrit.models.harm_definition import HarmDefinition, ScaleDescription, get_all_harm_definitions
Expand Down Expand Up @@ -79,11 +73,18 @@
group_message_pieces_into_conversations,
sort_message_pieces,
)
from pyrit.models.messages.chat_message import (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a back-compat shim at every old path? message.py, message_piece.py, attack_result.py, strategy_result.py all still have them.

from pyrit.models import X still resolves for all three so nothing in-repo breaks, but from pyrit.models.scenario_result import ScenarioResult is now a hard ModuleNotFoundError for any downstream code on the old deep path. I think we should either add the 3 shims or add a note about the breaking change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rich agrees with this comment but it is copilot generated: Good catch on the inconsistency. Rather than add three more shims, we removed the deep-path shims altogether — including the four existing silent ones (message, message_piece, attack_result, strategy_result). Principle: internal pyrit.models reshuffles stay shim-free because everything is re-exported from pyrit.models (the canonical import); warning shims are only for symbols that move to a different package. So the only thing that breaks is from pyrit.models.scenario_result import X, which was never a supported path — from pyrit.models import X is unchanged.

ALLOWED_CHAT_MESSAGE_ROLES,
ChatMessage,
ChatMessagesDataset,
ToolCall,
)
from pyrit.models.messages.conversation_reference import ConversationReference, ConversationType
from pyrit.models.question_answering import QuestionAnsweringDataset, QuestionAnsweringEntry, QuestionChoice
from pyrit.models.results.attack_result import AttackOutcome, AttackResult, AttackResultT
from pyrit.models.results.scenario_result import ScenarioIdentifier, ScenarioResult, ScenarioRunState
from pyrit.models.results.strategy_result import StrategyResult, StrategyResultT
from pyrit.models.retry_event import RetryEvent
from pyrit.models.scenario_result import ScenarioIdentifier, ScenarioResult, ScenarioRunState
from pyrit.models.score import Score, ScoreType, UnvalidatedScore

# Seeds - import from new seeds submodule for forward compatibility
Expand Down Expand Up @@ -192,6 +193,7 @@
"TARGET_EVAL_PARAM_FALLBACKS",
"TARGET_EVAL_PARAMS",
"TextDataTypeSerializer",
"ToolCall",
"UnvalidatedScore",
"validate_registry_name",
"VideoPathDataTypeSerializer",
Expand Down
23 changes: 0 additions & 23 deletions pyrit/models/attack_result.py

This file was deleted.

37 changes: 0 additions & 37 deletions pyrit/models/message.py

This file was deleted.

22 changes: 0 additions & 22 deletions pyrit/models/message_piece.py

This file was deleted.

18 changes: 17 additions & 1 deletion pyrit/models/messages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@

- MessagePiece: A single piece of a message exchanged with a target.
- Message: One request/response to a target, made up of one or more pieces.
- ChatMessage: OpenAI-style wire shape consumed/emitted by prompt targets.
- Conversation: Conversation-scoped metadata shared by every piece.
- ConversationReference: Immutable reference to a conversation in an attack.
- conversations: Free functions that operate on collections of messages/pieces.
"""

from pyrit.models.messages.conversation import Conversation
from pyrit.models.messages.chat_message import (
ALLOWED_CHAT_MESSAGE_ROLES,
ChatMessage,
ChatMessagesDataset,
ToolCall,
)
from pyrit.models.messages.conversation_reference import ConversationReference, ConversationType
from pyrit.models.messages.conversations import (
Conversation,
construct_response_from_request,
flatten_to_message_pieces,
get_all_values,
Expand All @@ -21,9 +31,15 @@
from pyrit.models.messages.message_piece import MessagePiece, sort_message_pieces

__all__ = [
"ALLOWED_CHAT_MESSAGE_ROLES",
"ChatMessage",
"ChatMessagesDataset",
"Conversation",
"ConversationReference",
"ConversationType",
"Message",
"MessagePiece",
"ToolCall",
Comment thread
rlundeen2 marked this conversation as resolved.
"construct_response_from_request",
"flatten_to_message_pieces",
"get_all_values",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

"""
OpenAI-format chat message types.

``ChatMessage`` is the OpenAI Chat Completions wire shape — a ``role`` plus a
string-or-multipart ``content``, with the OpenAI ``name`` / ``tool_calls`` /
``tool_call_id`` fields. Prompt targets that speak the OpenAI API (and the many
providers that mirror it) consume and emit these objects directly.

It is intentionally distinct from the PyRIT domain ``Message`` / ``MessagePiece``
types in this same package: those model a persisted request/response exchange,
whereas ``ChatMessage`` is the lightweight OpenAI-shaped transport representation
handed to a model API.
"""

from typing import Any

from pydantic import BaseModel, ConfigDict
Expand All @@ -21,9 +35,9 @@ class ToolCall(BaseModel):

class ChatMessage(BaseModel):
"""
Represents a chat message for API consumption.
Represents a single OpenAI Chat Completions message.

The content field can be:
Mirrors the OpenAI message schema. The content field can be:
- A simple string for single-part text messages
- A list of dicts for multipart messages (e.g., text + images)
"""
Expand Down
31 changes: 0 additions & 31 deletions pyrit/models/messages/conversation.py

This file was deleted.

28 changes: 27 additions & 1 deletion pyrit/models/messages/conversations.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,47 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

"""Helpers that operate on collections of ``Message`` / ``MessagePiece``."""
"""``Conversation`` model plus helpers that operate on collections of ``Message`` / ``MessagePiece``."""

from __future__ import annotations

from typing import TYPE_CHECKING

from pydantic import BaseModel, ConfigDict

from pyrit.models.messages.message import Message
from pyrit.models.messages.message_piece import MessagePiece
from pyrit.models.score import ( # noqa: TC001 (runtime-required by Pydantic field annotations)
ComponentIdentifierField,
)

if TYPE_CHECKING:
from collections.abc import MutableSequence, Sequence

from pyrit.models.literals import PromptDataType, PromptResponseError


class Conversation(BaseModel):
"""
Conversation-scoped metadata shared by every piece in a conversation.

A ``Conversation`` records identifiers that belong to the conversation as a
whole rather than to any individual ``MessagePiece`` -- most importantly the
target the conversation is held with. Persisting these once per conversation
(instead of stamping them onto every piece/row) is what keeps ``MessagePiece``
small.
"""

model_config = ConfigDict(
arbitrary_types_allowed=True,
extra="forbid",
validate_assignment=False,
)

conversation_id: str
target_identifier: ComponentIdentifierField | None = None


def get_all_values(messages: Sequence[Message]) -> list[str]:
"""
Return all converted values across the provided messages.
Expand Down
13 changes: 12 additions & 1 deletion pyrit/models/results/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,31 @@
# Licensed under the MIT license.

"""
Results module - strategy and attack result types for PyRIT.
Results module - strategy, attack, and scenario result types for PyRIT.

- StrategyResult: Base class for all strategy results.
- AttackResult: Result of an attack execution, with conversation/scoring evidence.
- AttackOutcome: Enum of possible attack outcomes.
- ScenarioResult: Aggregate result of a scenario run.
- ScenarioIdentifier: Identifier describing the executed scenario.
- ScenarioRunState: Lifecycle state of a scenario run.
"""

from pyrit.models.results.attack_result import AttackOutcome, AttackResult, AttackResultT
from pyrit.models.results.scenario_result import (
ScenarioIdentifier,
ScenarioResult,
ScenarioRunState,
)
from pyrit.models.results.strategy_result import StrategyResult, StrategyResultT

__all__ = [
"AttackOutcome",
"AttackResult",
"AttackResultT",
"ScenarioIdentifier",
"ScenarioResult",
"ScenarioRunState",
"StrategyResult",
"StrategyResultT",
]
2 changes: 1 addition & 1 deletion pyrit/models/results/attack_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from pydantic import AwareDatetime, Field

from pyrit.common.deprecation import print_deprecation_message
from pyrit.models.conversation_reference import ConversationReference, ConversationType
from pyrit.models.identifiers.component_identifier import ComponentIdentifier
from pyrit.models.messages.conversation_reference import ConversationReference, ConversationType
from pyrit.models.messages.message_piece import MessagePiece
from pyrit.models.results.strategy_result import StrategyResult
from pyrit.models.retry_event import RetryEvent
Expand Down
22 changes: 0 additions & 22 deletions pyrit/models/strategy_result.py

This file was deleted.

3 changes: 1 addition & 2 deletions pyrit/output/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@

import os

from pyrit.models import AttackResult, ComponentIdentifier, Message, Score
from pyrit.models.scenario_result import ScenarioResult
from pyrit.models import AttackResult, ComponentIdentifier, Message, ScenarioResult, Score
from pyrit.output.attack_result.markdown import MarkdownAttackResultMemoryPrinter
from pyrit.output.attack_result.pretty import PrettyAttackResultMemoryPrinter
from pyrit.output.conversation.pretty import PrettyConversationMemoryPrinter
Expand Down
2 changes: 1 addition & 1 deletion pyrit/output/scenario_result/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from abc import abstractmethod

from pyrit.common.deprecation import print_deprecation_message
from pyrit.models.scenario_result import ScenarioResult
from pyrit.models import ScenarioResult
from pyrit.output.base import PrinterBase


Expand Down
3 changes: 1 addition & 2 deletions pyrit/output/scenario_result/pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from colorama import Fore, Style

from pyrit.common.deprecation import print_deprecation_message
from pyrit.models import AttackOutcome
from pyrit.models.scenario_result import ScenarioResult
from pyrit.models import AttackOutcome, ScenarioResult
from pyrit.output.scenario_result.base import ScenarioResultPrinterBase
from pyrit.output.scorer.base import ScorerPrinterBase
from pyrit.output.sink import Sink
Expand Down
2 changes: 1 addition & 1 deletion pyrit/scenario/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from types import ModuleType

from pyrit.common.parameter import Parameter
from pyrit.models.scenario_result import ScenarioIdentifier, ScenarioResult
from pyrit.models import ScenarioIdentifier, ScenarioResult
from pyrit.scenario.core import (
AtomicAttack,
AttackTechnique,
Expand Down
Loading
Loading