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
6 changes: 3 additions & 3 deletions build_scripts/evaluate_scorers.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ async def evaluate_scorers(tags: list[str] | None = None, max_concurrency: int =
if tags:
scorer_names: list[str] = []
for tag in tags:
entries = registry.get_by_tag(tag=tag)
entries = registry.instances.get_by_tag(tag=tag)
scorer_names.extend(entry.name for entry in entries if entry.name not in scorer_names)
scorer_names.sort()
print(f"\nFiltering by tags: {tags}")
else:
scorer_names = registry.get_names()
scorer_names = registry.instances.get_names()

if not scorer_names:
print("No scorers registered. Check environment variable configuration.")
Expand All @@ -85,7 +85,7 @@ async def evaluate_scorers(tags: list[str] | None = None, max_concurrency: int =

# Evaluate each scorer
for i, scorer_name in scorer_iterator:
scorer = registry.get_instance_by_name(scorer_name)
scorer = registry.instances.get(scorer_name)
print(f"\n[{i}/{len(scorer_names)}] Evaluating {scorer_name}...")
print(" Status: Starting evaluation (this may take several minutes)...")

Expand Down
20 changes: 10 additions & 10 deletions doc/code/registry/2_instance_registry.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"source": [
"## Listing Available Instances\n",
"\n",
"Use `get_names()` to see registered instances, or `list_metadata()` for details."
"Use `instances.get_names()` to see registered instances, or `instances.list_metadata()` for details."
]
},
{
Expand Down Expand Up @@ -69,10 +69,10 @@
"# Register a scorer instance for demonstration\n",
"chat_target = OpenAIChatTarget()\n",
"refusal_scorer = SelfAskRefusalScorer(chat_target=chat_target)\n",
"registry.register_instance(refusal_scorer)\n",
"registry.instances.register(refusal_scorer)\n",
"\n",
"# List what's available\n",
"names = registry.get_names()\n",
"names = registry.instances.get_names()\n",
"print(f\"Registered scorers: {names}\")"
]
},
Expand All @@ -83,7 +83,7 @@
"source": [
"## Getting an Instance\n",
"\n",
"Use `get()` to retrieve a pre-configured instance by name. The instance is ready to use immediately."
"Use `instances.get()` to retrieve a pre-configured instance by name. The instance is ready to use immediately."
]
},
{
Expand All @@ -105,7 +105,7 @@
"# Get the first registered scorer\n",
"if names:\n",
" scorer_name = names[0]\n",
" scorer = registry.get(scorer_name)\n",
" scorer = registry.instances.get(scorer_name)\n",
" print(f\"Retrieved scorer: {scorer}\")\n",
" print(f\"Scorer type: {type(scorer).__name__}\")"
]
Expand Down Expand Up @@ -151,7 +151,7 @@
"from pyrit.output import output_scorer_async\n",
"\n",
"# Get metadata for all registered scorers\n",
"metadata = registry.list_metadata()\n",
"metadata = registry.instances.list_metadata()\n",
"for item in metadata:\n",
" print(f\"\\n{item.unique_name}:\")\n",
" print(f\" Class: {item.class_name}\")\n",
Expand Down Expand Up @@ -188,15 +188,15 @@
],
"source": [
"# Filter by scorer_type (based on isinstance check against TrueFalseScorer/FloatScaleScorer)\n",
"true_false_scorers = registry.list_metadata(include_filters={\"scorer_type\": \"true_false\"})\n",
"true_false_scorers = registry.instances.list_metadata(include_filters={\"scorer_type\": \"true_false\"})\n",
"print(f\"True/False scorers: {[m.unique_name for m in true_false_scorers]}\")\n",
"\n",
"# Filter by class_name\n",
"refusal_scorers = registry.list_metadata(include_filters={\"class_name\": \"SelfAskRefusalScorer\"})\n",
"refusal_scorers = registry.instances.list_metadata(include_filters={\"class_name\": \"SelfAskRefusalScorer\"})\n",
"print(f\"Refusal scorers: {[m.unique_name for m in refusal_scorers]}\")\n",
"\n",
"# Combine multiple filters (AND logic)\n",
"specific_scorers = registry.list_metadata(\n",
"specific_scorers = registry.instances.list_metadata(\n",
" include_filters={\"scorer_type\": \"true_false\", \"class_name\": \"SelfAskRefusalScorer\"}\n",
")\n",
"print(f\"True/False refusal scorers: {[m.unique_name for m in specific_scorers]}\")"
Expand Down Expand Up @@ -248,7 +248,7 @@
"# Get the registry singleton\n",
"registry = TargetRegistry.get_registry_singleton()\n",
"# List registered targets\n",
"target_names = registry.get_names()\n",
"target_names = registry.instances.get_names()\n",
"print(f\"Registered targets after initialization: {target_names}\")"
]
}
Expand Down
20 changes: 10 additions & 10 deletions doc/code/registry/2_instance_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# %% [markdown]
# ## Listing Available Instances
#
# Use `get_names()` to see registered instances, or `list_metadata()` for details.
# Use `instances.get_names()` to see registered instances, or `instances.list_metadata()` for details.

# %%
from pyrit.prompt_target import OpenAIChatTarget
Expand All @@ -37,22 +37,22 @@
# Register a scorer instance for demonstration
chat_target = OpenAIChatTarget()
refusal_scorer = SelfAskRefusalScorer(chat_target=chat_target)
registry.register_instance(refusal_scorer)
registry.instances.register(refusal_scorer)

# List what's available
names = registry.get_names()
names = registry.instances.get_names()
print(f"Registered scorers: {names}")

# %% [markdown]
# ## Getting an Instance
#
# Use `get()` to retrieve a pre-configured instance by name. The instance is ready to use immediately.
# Use `instances.get()` to retrieve a pre-configured instance by name. The instance is ready to use immediately.

# %%
# Get the first registered scorer
if names:
scorer_name = names[0]
scorer = registry.get(scorer_name)
scorer = registry.instances.get(scorer_name)
print(f"Retrieved scorer: {scorer}")
print(f"Scorer type: {type(scorer).__name__}")

Expand All @@ -65,7 +65,7 @@
from pyrit.output import output_scorer_async

# Get metadata for all registered scorers
metadata = registry.list_metadata()
metadata = registry.instances.list_metadata()
for item in metadata:
print(f"\n{item.unique_name}:")
print(f" Class: {item.class_name}")
Expand All @@ -80,15 +80,15 @@

# %%
# Filter by scorer_type (based on isinstance check against TrueFalseScorer/FloatScaleScorer)
true_false_scorers = registry.list_metadata(include_filters={"scorer_type": "true_false"})
true_false_scorers = registry.instances.list_metadata(include_filters={"scorer_type": "true_false"})
print(f"True/False scorers: {[m.unique_name for m in true_false_scorers]}")

# Filter by class_name
refusal_scorers = registry.list_metadata(include_filters={"class_name": "SelfAskRefusalScorer"})
refusal_scorers = registry.instances.list_metadata(include_filters={"class_name": "SelfAskRefusalScorer"})
print(f"Refusal scorers: {[m.unique_name for m in refusal_scorers]}")

# Combine multiple filters (AND logic)
specific_scorers = registry.list_metadata(
specific_scorers = registry.instances.list_metadata(
include_filters={"scorer_type": "true_false", "class_name": "SelfAskRefusalScorer"}
)
print(f"True/False refusal scorers: {[m.unique_name for m in specific_scorers]}")
Expand All @@ -111,5 +111,5 @@
# Get the registry singleton
registry = TargetRegistry.get_registry_singleton()
# List registered targets
target_names = registry.get_names()
target_names = registry.instances.get_names()
print(f"Registered targets after initialization: {target_names}")
6 changes: 3 additions & 3 deletions doc/code/scenarios/0_scenarios.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@
" ``supported_parameters``). Each target must already be registered in\n",
" ``TargetRegistry`` — typically by ``TargetInitializer`` from\n",
" ``ADVERSARIAL_CHAT_*`` env vars, or programmatically via\n",
" ``TargetRegistry.register_instance``. At run time,\n",
" ``_get_atomic_attacks_async`` performs the ``(technique ×\n",
" ``TargetRegistry.get_registry_singleton().instances.register``. At run\n",
" time, ``_get_atomic_attacks_async`` performs the ``(technique ×\n",
" adversarial_target × dataset)`` cross-product: for each selected\n",
" adversarial-capable ``core`` factory in the ``AttackTechniqueRegistry``\n",
" and each requested target, it calls\n",
Expand All @@ -356,7 +356,7 @@
" Default Datasets (1, max 8 per dataset):\n",
" harmbench\n",
" Supported Parameters:\n",
" - adversarial_targets (list[str]): Registry names of adversarial chat targets to benchmark. Each name must already be registered in TargetRegistry (via TargetInitializer or TargetRegistry.register_instance). Use 'pyrit_scan list-targets' to see registered targets. Settable via --adversarial-targets <name> [<name> ...] on the CLI, or scenario.args.adversarial_targets in .pyrit_conf.\n",
" - adversarial_targets (list[str]): Registry names of adversarial chat targets to benchmark. Each name must already be registered in TargetRegistry (via TargetInitializer or TargetRegistry instance registration). Use 'pyrit_scan list-targets' to see registered targets. Settable via --adversarial-targets <name> [<name> ...] on the CLI, or scenario.args.adversarial_targets in .pyrit_conf.\n",
"\u001b[1m\u001b[36m\n",
" foundry.red_team_agent\u001b[0m\n",
" Class: RedTeamAgent\n",
Expand Down
2 changes: 1 addition & 1 deletion doc/code/scenarios/1_common_scenario_parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"\n",
"await initialize_from_config_async(config_path=Path(\"../../scanner/pyrit_conf.yaml\")) # type: ignore\n",
"\n",
"objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name(\"openai_chat\")"
"objective_target = TargetRegistry.get_registry_singleton().instances.get(\"openai_chat\")"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion doc/code/scenarios/1_common_scenario_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

await initialize_from_config_async(config_path=Path("../../scanner/pyrit_conf.yaml")) # type: ignore

objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name("openai_chat")
objective_target = TargetRegistry.get_registry_singleton().instances.get("openai_chat")
# %% [markdown]
# ## Dataset Configuration
#
Expand Down
2 changes: 1 addition & 1 deletion doc/code/scenarios/3_adaptive_scenarios.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"\n",
"await initialize_from_config_async(config_path=Path(\"../../scanner/pyrit_conf.yaml\")) # type: ignore\n",
"\n",
"objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name(\"openai_chat\")\n",
"objective_target = TargetRegistry.get_registry_singleton().instances.get(\"openai_chat\")\n",
"printer = ConsoleScenarioResultPrinter()"
]
},
Expand Down
2 changes: 1 addition & 1 deletion doc/code/scenarios/3_adaptive_scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@

await initialize_from_config_async(config_path=Path("../../scanner/pyrit_conf.yaml")) # type: ignore

objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name("openai_chat")
objective_target = TargetRegistry.get_registry_singleton().instances.get("openai_chat")
printer = ConsoleScenarioResultPrinter()

# %% [markdown]
Expand Down
2 changes: 1 addition & 1 deletion doc/scanner/foundry.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"\n",
"await initialize_from_config_async(config_path=Path(\"pyrit_conf.yaml\")) # type: ignore\n",
"\n",
"objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name(\"openai_chat\")"
"objective_target = TargetRegistry.get_registry_singleton().instances.get(\"openai_chat\")"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion doc/scanner/foundry.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

await initialize_from_config_async(config_path=Path("pyrit_conf.yaml")) # type: ignore

objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name("openai_chat")
objective_target = TargetRegistry.get_registry_singleton().instances.get("openai_chat")
# %% [markdown]
# ## RedTeamAgent
#
Expand Down
2 changes: 1 addition & 1 deletion doc/scanner/garak.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"\n",
"await initialize_from_config_async(config_path=Path(\"pyrit_conf.yaml\")) # type: ignore\n",
"\n",
"objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name(\"openai_chat\")"
"objective_target = TargetRegistry.get_registry_singleton().instances.get(\"openai_chat\")"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion doc/scanner/garak.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

await initialize_from_config_async(config_path=Path("pyrit_conf.yaml")) # type: ignore

objective_target = TargetRegistry.get_registry_singleton().get_instance_by_name("openai_chat")
objective_target = TargetRegistry.get_registry_singleton().instances.get("openai_chat")
# %% [markdown]
# ## Encoding
#
Expand Down
4 changes: 2 additions & 2 deletions pyrit/backend/services/scenario_run_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,9 @@ def _resolve_target(self, *, request: RunScenarioRequest) -> "PromptTarget":
ValueError: If the target is not found in the registry.
"""
target_registry = TargetRegistry.get_registry_singleton()
objective_target = target_registry.get_instance_by_name(request.target_name)
objective_target = target_registry.instances.get(request.target_name)
if objective_target is None:
available_names = target_registry.get_names()
available_names = target_registry.instances.get_names()
if not available_names:
raise ValueError(
f"Target '{request.target_name}' not found. The target registry is empty. "
Expand Down
12 changes: 6 additions & 6 deletions pyrit/backend/services/target_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from pyrit.prompt_target.azure_ml_chat_target import AzureMLChatTarget
from pyrit.prompt_target.openai.openai_target import OpenAITarget
from pyrit.prompt_target.round_robin_target import RoundRobinTarget
from pyrit.registry.object_registries import TargetRegistry
from pyrit.registry import TargetRegistry

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -192,7 +192,7 @@ async def list_targets_async(
"""
items = [
self._build_instance_from_object(target_registry_name=entry.name, target_obj=entry.instance)
for entry in self._registry.get_all_instances()
for entry in self._registry.instances.get_all_instances()
]
page, has_more = self._paginate(items=items, cursor=cursor, limit=limit)
next_cursor = page[-1].target_registry_name if has_more and page else None
Expand Down Expand Up @@ -227,7 +227,7 @@ async def get_target_async(self, *, target_registry_name: str) -> TargetInstance
Returns:
TargetInstance if found, None otherwise.
"""
obj = self._registry.get_instance_by_name(target_registry_name)
obj = self._registry.instances.get(target_registry_name)
if obj is None:
return None
return self._build_instance_from_object(target_registry_name=target_registry_name, target_obj=obj)
Expand All @@ -239,7 +239,7 @@ def get_target_object(self, *, target_registry_name: str) -> Any | None:
Returns:
The PromptTarget object if found, None otherwise.
"""
return self._registry.get_instance_by_name(target_registry_name)
return self._registry.instances.get(target_registry_name)

async def create_target_async(self, *, request: CreateTargetRequest) -> TargetInstance:
"""
Expand Down Expand Up @@ -281,7 +281,7 @@ async def create_target_async(self, *, request: CreateTargetRequest) -> TargetIn

target_obj = target_class(**params)

self._registry.register_instance(target_obj)
self._registry.instances.register(target_obj)

target_registry_name = target_obj.get_identifier().unique_name
return self._build_instance_from_object(target_registry_name=target_registry_name, target_obj=target_obj)
Expand Down Expand Up @@ -334,7 +334,7 @@ def _create_round_robin_target(self, *, params: dict[str, Any]) -> RoundRobinTar
resolved_weights: list[int] = []
duplicates: list[str] = []
for idx, name in enumerate(registry_names):
target_obj = self._registry.get_instance_by_name(name)
target_obj = self._registry.instances.get(name)
if target_obj is None:
raise ValueError(f"Target '{name}' not found in the registry.")
target_hash = target_obj.get_identifier().hash
Expand Down
19 changes: 15 additions & 4 deletions pyrit/models/identifiers/scorer_identifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from pyrit.models.identifiers.component_identifier import ComponentIdentifier
from pyrit.models.identifiers.evaluation_markers import Evaluate
from pyrit.models.identifiers.param_markers import Param
from pyrit.models.identifiers.target_identifier import ( # noqa: TC001
TargetIdentifier, # runtime-required by Pydantic field annotations
)
Expand All @@ -24,6 +25,12 @@ class ScorerIdentifier(ComponentIdentifier):
Promotes the ``scorer_type`` discriminator, the ``score_aggregator`` name, and
the scorer's own child slots — ``prompt_target`` (an LLM target) and
``sub_scorers`` (nested scorers).

Build markers (``Param.*``) declare how the child slots map to the scorer's
constructor: ``prompt_target`` is an included parameter aliased to the
``chat_target`` constructor arg, and ``sub_scorers`` is an included parameter
aliased to the composite scorer's ``scorers`` arg. Their identifier types make
them references resolved by name from the target and scorer registries.
"""

component_type: ClassVar[ComponentType] = ComponentType.SCORER
Expand All @@ -32,7 +39,11 @@ class ScorerIdentifier(ComponentIdentifier):
scorer_type: Annotated[str | None, Evaluate.Include()] = None
#: Name of the aggregator function combining sub-scores (e.g., ``"AND_"``).
score_aggregator: Annotated[str | None, Evaluate.Include()] = None
#: Target an LLM-backed scorer calls (e.g., ``SelfAskScaleScorer``).
prompt_target: Annotated[TargetIdentifier | None, Evaluate.Include()] = None
#: Nested scorers a composite wraps, typed recursively.
sub_scorers: Annotated[list[ScorerIdentifier], Evaluate.Include()] = Field(default_factory=list)
#: Target an LLM-backed scorer calls (e.g., ``SelfAskScaleScorer``). The
#: constructor arg is ``chat_target``, so the build marker aliases it.
prompt_target: Annotated[TargetIdentifier | None, Evaluate.Include(), Param.Include(alias="chat_target")] = None
#: Nested scorers a composite wraps, typed recursively. The composite
#: constructor arg is ``scorers`` (a list), so the build marker aliases it.
sub_scorers: Annotated[list[ScorerIdentifier], Evaluate.Include(), Param.Include(alias="scorers")] = Field(
default_factory=list
)
7 changes: 5 additions & 2 deletions pyrit/models/identifiers/target_identifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from pyrit.models.identifiers.component_identifier import ComponentIdentifier
from pyrit.models.identifiers.evaluation_markers import Evaluate
from pyrit.models.identifiers.param_markers import Param
from pyrit.models.parameter import ComponentType


Expand Down Expand Up @@ -46,5 +47,7 @@ class TargetIdentifier(ComponentIdentifier):
top_p: Annotated[float | None, Evaluate.Include()] = None
#: Maximum requests per minute.
max_requests_per_minute: Annotated[int | None, Evaluate.Exclude()] = None
#: Inner targets of a multi-target (e.g., ``RoundRobinTarget``), typed recursively.
targets: Annotated[list[TargetIdentifier], Evaluate.Unwrap()] = Field(default_factory=list)
#: Inner targets of a multi-target (e.g., ``RoundRobinTarget``), typed
#: recursively. An included constructor parameter (the ctor arg is also
#: ``targets``, a list) resolved by name from the target registry.
targets: Annotated[list[TargetIdentifier], Evaluate.Unwrap(), Param.Include()] = Field(default_factory=list)
Loading
Loading