Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9f409ab
feat(setup): add plug-in loading to initialize_pyrit_async
Jul 7, 2026
c7a7528
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 8, 2026
e7d63cf
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 8, 2026
eca5928
Potential fix for pull request finding
ValbuenaVC Jul 8, 2026
0b11b6c
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 8, 2026
8781038
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 8, 2026
e6159e5
test(setup): verify plug-in scenarios are discovered end-to-end
Jul 8, 2026
f8e761d
fix(setup): restore overwritten registry entries on plug-in rollback
Jul 8, 2026
ed3fc0d
refactor(setup): extract plug-in wheels with safe_extract_zip
Jul 8, 2026
fb822e0
perf(setup): run blocking plug-in extraction off the event loop
Jul 8, 2026
95de68c
feat(setup): add granular PluginLoadError subclasses
Jul 8, 2026
c108578
fix(setup): use a unique temp dir for plug-in wheel extraction
Jul 8, 2026
b551008
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 9, 2026
c028f8b
refactor(setup): rename plugin_fail_open to plugin_accept_load_failures
Jul 9, 2026
67f8a85
Merge commit 'b55100804be2f4fae26d619cbd5b072e104a94ce' into vvalbuen…
Jul 9, 2026
067ffba
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 9, 2026
2ae6c55
feat(setup): configure plug-ins in .pyrit_conf with multi-plug-in sup…
Jul 9, 2026
b21f856
Merge commit '067ffba6461c6e89590a3d369a3d5bcc408508a3' into vvalbuen…
Jul 9, 2026
21ebf36
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 9, 2026
466679a
Merge commit '21ebf36815e93891ef55221912fee2345bd311c9' into vvalbuen…
Jul 9, 2026
88bd76a
feat(registry): auto-register external plug-in Scenario subclasses
Jul 9, 2026
b177f49
feat(setup): tolerate plug-in version drift and re-extract stale wheels
Jul 9, 2026
1538e6e
test(setup): cover plug-in auto-registration, discovery, instantiate,…
Jul 9, 2026
bbd3da8
Merge branch 'main' into vvalbuena-microsoft-plugin-mechanism
ValbuenaVC Jul 9, 2026
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
30 changes: 30 additions & 0 deletions .env_example
Comment thread
ValbuenaVC marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,36 @@ AZURE_OPENAI_VIDEO_MODEL="sora-2"
AZURE_OPENAI_VIDEO_UNDERLYING_MODEL="sora-2"

OPENAI_VIDEO_ENDPOINT = ${AZURE_OPENAI_VIDEO_ENDPOINT}

##################################
# PLUG-INS
#
# PyRIT can load a non-disclosable plug-in (extra datasets + scenarios) from a
# pre-built wheel at initialization time. Plug-in loading runs automatically as a
# guaranteed-first phase inside initialize_pyrit_async (before the configured
# initializers) — there is nothing to add to .pyrit_conf. The wheel is extracted to
# .plugin/<name>/, prepended to sys.path, imported, and its bootstrap is run so its
# datasets/scenarios register like built-ins. Extraction only — never pip/.venv.
#
# WARNING: loading a plug-in executes third-party code in the PyRIT process.
# Whoever can set PLUGIN_* / write this .env can run code on the host. Treat this
# file as sensitive.
###################################

# Path to a pre-built plug-in wheel on disk. Setting it enables plug-in loading;
# leaving it unset is a no-op.
# PLUGIN_WHEEL="/abs/path/to/my_plugin-0.0.0-py3-none-any.whl"

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.

Does that mean we support only one plugin? If so, I think it's a design limitation that might bite us later.

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.

To answer your first question, yes, but not as an explicit constraint. I see two solutions to plug-in composition: we can just load multiple plug-ins and place the burden of compatibility on the user, or we can add a layer of abstraction, so that plug-in components can be collected, compared, and initialized in a certain order. I lean towards the second long-term, but in either case I don't think we should support only one plugin on purpose.


# Optional: the wheel's top-level import package. Auto-detected from the wheel
# when omitted; set this to disambiguate multi-package wheels.
# PLUGIN_PACKAGE="my_plugin"

# Optional: continue (with a warning) instead of failing when the plug-in cannot
# be loaded. Equivalent to initialize_pyrit_async(plugin_fail_open=True).
# PLUGIN_FAIL_OPEN="false"

# Optional: override the base extraction directory (defaults to <pyrit home>/.plugin).
# PLUGIN_DIR="/abs/path/to/.plugin"
OPENAI_VIDEO_KEY = ${AZURE_OPENAI_VIDEO_KEY}
OPENAI_VIDEO_MODEL = ${AZURE_OPENAI_VIDEO_MODEL}
OPENAI_VIDEO_UNDERLYING_MODEL = ""
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ dbdata/
eval/
default_memory.json.memory

# Plug-in extraction working directory (see DefaultPluginInitializer / PLUGIN_WHEEL).
# The directory is kept; extracted plug-in artifacts are ignored.
.plugin/*
!.plugin/.gitkeep

# Frontend build artifacts copied to backend for packaging
pyrit/backend/frontend/

Expand Down
2 changes: 2 additions & 0 deletions .plugin/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Keeps the .plugin/ directory in version control while its contents stay ignored.
# PyRIT extracts plug-in wheels here at runtime (see PLUGIN_WHEEL).
4 changes: 4 additions & 0 deletions .pyrit_conf_example
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ memory_db_type: sqlite
# - load_default_datasets: Loads datasets into memory so scenarios can run
# - preload_scenario_metadata: Preloads scenario metadata into the registry
#
# Note: plug-ins (PLUGIN_WHEEL) are NOT configured here. They load automatically as a
# guaranteed-first phase inside initialize_pyrit_async — before these initializers — so
# there is no ordering to get right. See the PLUGIN_* variables in .env_example.
#
# Each initializer can be specified as:
# - A simple string (name only)
# - A dictionary with 'name' and optional 'args' for parameters
Expand Down
13 changes: 13 additions & 0 deletions pyrit/setup/initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ async def initialize_pyrit_async(
env_files: Sequence[pathlib.Path] | None = None,
env_akv_ref: Sequence[str] | None = None,
silent: bool = False,
plugin_fail_open: bool | None = None,
**memory_instance_kwargs: Any,
) -> None:
"""
Expand All @@ -224,6 +225,9 @@ async def initialize_pyrit_async(
so local files take precedence over AKV. Requires ``azure-keyvault-secrets``.
silent (bool): If True, suppresses print statements about environment file loading and
schema migration. Defaults to False.
plugin_fail_open (bool | None): Overrides ``PLUGIN_FAIL_OPEN`` for plug-in loading. When True,
Comment thread
ValbuenaVC marked this conversation as resolved.
Outdated
a plug-in (``PLUGIN_WHEEL``) that fails to load is skipped with a warning instead of raising.
Defaults to None (use the ``PLUGIN_FAIL_OPEN`` environment variable, else fail-closed).
**memory_instance_kwargs (Any | None): Additional keyword arguments to pass to the memory instance.

Raises:
Expand Down Expand Up @@ -259,6 +263,15 @@ async def initialize_pyrit_async(

CentralMemory.set_memory_instance(memory)

# Load a configured plug-in (PLUGIN_WHEEL) as a guaranteed-first phase: after memory
# is set (a plug-in bootstrap may use it) and BEFORE any configured initializers run,
# so plug-in datasets/scenarios are registered before LoadDefaultDatasets and
# PreloadScenarioMetadata read the registry. Ordering is true by construction here, so
# it does not depend on .pyrit_conf list position. No-op unless PLUGIN_WHEEL is set.
from pyrit.setup.plugin_loader import load_plugin_if_configured_async

await load_plugin_if_configured_async(fail_open=plugin_fail_open)

# Combine directly provided initializers with those loaded from scripts
all_initializers = list(initializers) if initializers else []

Expand Down
Loading
Loading