Skip to content

Commit bed0e15

Browse files
romanlutzCopilot
andauthored
Fix two failing E2E dataset providers (ComicJailbreak timeout, VLGuard 401) (#1863)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 95eb196 commit bed0e15

5 files changed

Lines changed: 81 additions & 4 deletions

File tree

pyrit/datasets/seed_datasets/remote/comic_jailbreak_dataset.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def __init__(
105105
),
106106
source_type: Literal["public_url", "file"] = "public_url",
107107
templates: list[str] | None = None,
108+
max_examples: int | None = None,
108109
) -> None:
109110
"""
110111
Initialize the ComicJailbreak dataset loader.
@@ -114,13 +115,17 @@ def __init__(
114115
at a pinned commit.
115116
source_type: The type of source ('public_url' or 'file').
116117
templates: List of template names to include. If None, all 5 templates are used.
118+
max_examples: Maximum number of source goals to render. Each goal produces up to
119+
``len(templates)`` image+text pairs. If None, all goals are rendered. Useful for
120+
CI and quick validations where rendering all 300 goals × 5 templates is too slow.
117121
118122
Raises:
119123
ValueError: If any template name is invalid.
120124
"""
121125
self.source = source
122126
self.source_type: Literal["public_url", "file"] = source_type
123127
self.templates = templates or list(self.TEMPLATE_NAMES)
128+
self.max_examples = max_examples
124129

125130
invalid = set(self.templates) - set(self.TEMPLATE_NAMES)
126131
if invalid:
@@ -166,6 +171,7 @@ async def fetch_dataset_async(self, *, cache: bool = True) -> SeedDataset:
166171
template_paths[template_name] = await self._fetch_template_async(template_name)
167172

168173
seeds: list[Seed] = []
174+
processed_goals = 0
169175

170176
for row_idx, example in enumerate(examples):
171177
missing_keys = required_keys - example.keys()
@@ -204,6 +210,10 @@ async def fetch_dataset_async(self, *, cache: bool = True) -> SeedDataset:
204210
)
205211
seeds.extend(pair)
206212

213+
processed_goals += 1
214+
if self.max_examples is not None and processed_goals >= self.max_examples:
215+
break
216+
207217
logger.info(f"Successfully loaded {len(seeds)} seeds from ComicJailbreak dataset")
208218
return SeedDataset(seeds=seeds, dataset_name=self.dataset_name)
209219

pyrit/datasets/seed_datasets/remote/vlguard_dataset.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import asyncio
55
import json
66
import logging
7+
import os
78
import uuid
89
import zipfile
910
from enum import Enum
@@ -108,14 +109,14 @@ def __init__(
108109
categories (list[VLGuardCategory] | None): List of VLGuard categories to filter by.
109110
If None, all categories are included.
110111
token (str | None): HuggingFace authentication token for accessing the gated dataset.
111-
If None, uses the default token from the environment or HuggingFace CLI login.
112+
If not provided, reads from the ``HUGGINGFACE_TOKEN`` environment variable.
112113
113114
Raises:
114115
ValueError: If any of the specified categories are invalid.
115116
"""
116117
self.subset = subset
117118
self.categories = categories
118-
self.token = token
119+
self.token = token if token is not None else os.environ.get("HUGGINGFACE_TOKEN")
119120
self.source = f"https://huggingface.co/datasets/{_HF_REPO_ID}"
120121

121122
if categories is not None:

tests/end_to_end/test_all_datasets.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@
2121

2222
from pyrit.datasets import SeedDatasetProvider
2323
from pyrit.datasets.seed_datasets.remote import (
24+
_ComicJailbreakDataset,
2425
_HarmBenchMultimodalDataset,
26+
_HiXSTestDataset,
2527
_PromptIntelDataset,
28+
_SGXSTestDataset,
2629
_SIUODataset,
30+
_SorryBenchDataset,
31+
_VLGuardDataset,
2732
_VLSUMultimodalDataset,
2833
)
2934
from pyrit.models import SeedDataset
@@ -41,6 +46,20 @@
4146
# due to rate-limiting, so an empty result is expected in some environments.
4247
_IMAGE_FETCHING_PROVIDERS: set[type] = {_HarmBenchMultimodalDataset, _SIUODataset, _VLSUMultimodalDataset}
4348

49+
# Providers that produce many seeds and would otherwise exceed _TEST_TIMEOUT.
50+
# Constructed with max_examples to keep CI fast; full coverage runs are out of scope here.
51+
_LIMITED_EXAMPLES_PROVIDERS: set[type] = {_ComicJailbreakDataset, _VLSUMultimodalDataset}
52+
53+
# Providers backed by HuggingFace-gated datasets. They require both a HUGGINGFACE_TOKEN
54+
# and that the token's account has accepted each dataset's terms; skipped when no token
55+
# is present (e.g. when running E2E locally without secrets).
56+
_HF_GATED_PROVIDERS: set[type] = {
57+
_HiXSTestDataset,
58+
_SGXSTestDataset,
59+
_SorryBenchDataset,
60+
_VLGuardDataset,
61+
}
62+
4463

4564
def get_dataset_providers():
4665
"""Helper to get all registered providers for parameterization."""
@@ -85,12 +104,14 @@ async def test_fetch_dataset(self, name, provider_cls):
85104
# Skip providers that require credentials not available in CI
86105
if provider_cls == _PromptIntelDataset and not os.environ.get("PROMPTINTEL_API_KEY"):
87106
pytest.skip("PROMPTINTEL_API_KEY not set")
107+
if provider_cls in _HF_GATED_PROVIDERS and not os.environ.get("HUGGINGFACE_TOKEN"):
108+
pytest.skip(f"HUGGINGFACE_TOKEN not set (required for gated dataset used by {name})")
88109

89110
logger.info(f"Testing provider: {name}")
90111

91112
try:
92-
# Limit examples for slow multimodal providers that fetch many remote images
93-
provider = provider_cls(max_examples=6) if provider_cls == _VLSUMultimodalDataset else provider_cls()
113+
# Limit examples for slow providers that would otherwise exceed _TEST_TIMEOUT
114+
provider = provider_cls(max_examples=6) if provider_cls in _LIMITED_EXAMPLES_PROVIDERS else provider_cls()
94115

95116
dataset = await _fetch_with_retry(provider)
96117
except Exception as e:

tests/unit/datasets/test_comic_jailbreak_dataset.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,27 @@ async def test_fetch_dataset_empty_goal_skipped(self):
169169
with pytest.raises(ValueError, match="SeedDataset cannot be empty"):
170170
await loader.fetch_dataset_async()
171171

172+
async def test_fetch_dataset_respects_max_examples(self):
173+
"""max_examples caps the number of source goals that get rendered."""
174+
mock_data = [_make_example(Goal=f"Goal {i}") for i in range(5)]
175+
loader = _ComicJailbreakDataset(templates=["article"], max_examples=2)
176+
177+
with (
178+
patch.object(loader, "_fetch_from_url", return_value=mock_data),
179+
patch.object(loader, "_fetch_template_async", new_callable=AsyncMock, return_value="/fake/template.png"),
180+
patch.object(loader, "_render_comic_async", new_callable=AsyncMock, return_value="/fake/rendered.png"),
181+
):
182+
dataset = await loader.fetch_dataset_async(cache=False)
183+
184+
# 2 goals × 1 template × 3 seeds (objective + image + text) = 6
185+
assert len(dataset.seeds) == 6
186+
goals = {s.metadata["goal"] for s in dataset.seeds if isinstance(s, SeedPrompt)}
187+
assert goals == {"Goal 0", "Goal 1"}
188+
189+
def test_init_default_max_examples_is_none(self):
190+
loader = _ComicJailbreakDataset()
191+
assert loader.max_examples is None
192+
172193

173194
class TestComicJailbreakTemplates:
174195
"""Tests for the COMIC_JAILBREAK_TEMPLATES constant."""

tests/unit/datasets/test_vlguard_dataset.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,27 @@ def mock_hf_download(*, repo_id, filename, repo_type, local_dir, token):
380380

381381
assert metadata == test_metadata
382382
assert result_dir == cache_dir / "test"
383+
384+
385+
class TestVLGuardTokenResolution:
386+
"""Tests for HuggingFace token resolution on _VLGuardDataset."""
387+
388+
def test_explicit_token_kwarg_used(self):
389+
with patch.dict("os.environ", {}, clear=True):
390+
loader = _VLGuardDataset(token="kwarg_token")
391+
assert loader.token == "kwarg_token"
392+
393+
def test_falls_back_to_huggingface_token_env(self):
394+
with patch.dict("os.environ", {"HUGGINGFACE_TOKEN": "env_token"}):
395+
loader = _VLGuardDataset()
396+
assert loader.token == "env_token"
397+
398+
def test_explicit_kwarg_overrides_env(self):
399+
with patch.dict("os.environ", {"HUGGINGFACE_TOKEN": "env_token"}):
400+
loader = _VLGuardDataset(token="kwarg_token")
401+
assert loader.token == "kwarg_token"
402+
403+
def test_token_is_none_when_neither_set(self):
404+
with patch.dict("os.environ", {}, clear=True):
405+
loader = _VLGuardDataset()
406+
assert loader.token is None

0 commit comments

Comments
 (0)