Skip to content
Merged
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 @@ -51,7 +51,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 @@ -31,7 +31,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