Skip to content

Commit 0f9dab7

Browse files
committed
Update docs
1 parent 927a321 commit 0f9dab7

4 files changed

Lines changed: 29 additions & 12 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,15 @@ result = optimizer.optimize_trigger(
9696
```
9797

9898
You can replace any component in this recipe code with another compatible one; e.g., swap the loss or optimizer with a more sophisticated one to enhance the jailbreak.
99-
For more examples see [quickstart.ipynb](quickstart.ipynb) notebook, and the detailed guide on [adding a recipe](docs/guides/adding_a_recipe.md).
99+
For more examples see [quickstart.ipynb](quickstart.ipynb) notebook, and the detailed guide on [adding a recipe](https://tropt.dev/guides/adding_a_recipe.html).
100100

101101
### Build New Optimizers & Losses 🔬
102102

103103
TROPT is designed as a **factory for new optimizers and losses**. Each is a self-contained module behind a compact, standardized interface. This makes optimizer and loss modules more transparent and easy to read, and easily extensible: creating a new optimizer largely amounts to defining its search algorithm, and a new loss to defining its core computation.
104104
TROPT internally handles the repeated logic required to operate these modules, including input--trigger management, batching, tokenization blocking, trigger gradient computation, etc.
105105
Your new optimizer or loss then composes automatically with every existing model and counterpart component.
106106

107-
Quick examples for a custom optimizer and loss are in [quickstart.ipynb](quickstart.ipynb); the docs have more detailed guides on building [optimizers](docs/guides/adding_an_optimizer.md) and [losses](docs/guides/adding_a_loss.md).
107+
Quick examples for a custom optimizer and loss are in [quickstart.ipynb](quickstart.ipynb); the docs have more detailed guides on building [optimizers](https://tropt.dev/guides/adding_an_optimizer.html) and [losses](https://tropt.dev/guides/adding_a_loss.html).
108108

109109

110110
## 🤖 Use TROPT with Your Coding Agent

docs/_static/custom.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ html[data-theme="dark"] {
3131
/* ==========================================================================
3232
Base API-page tweaks (kept from previous custom.css)
3333
========================================================================== */
34+
/* Sphinx's basic.css bolds any inline code that's a cross-reference link
35+
(`code.xref, a code { font-weight: bold }`). With autodoc xrefs all over the
36+
guides this made most inline code look bold by default — match the weight
37+
of plain (unlinked) inline literals instead. */
38+
code.xref,
39+
a code {
40+
font-weight: inherit;
41+
}
42+
3443
dl.class {
3544
margin: 20px 0;
3645
border: 0;

docs/guides/adding_a_model.md

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -418,14 +418,5 @@ class MyHFLMModel(
418418
def invoke_from_texts(self, input_texts, **kwargs): ...
419419
```
420420

421-
You do **not** implement `compute_loss_from_tokens`, `compute_grad_from_tokens`, or `compute_grad_from_embeds` — those come from `HuggingFaceBackendModel`.
421+
You do **not** need to implement `compute_loss_from_tokens`, `compute_grad_from_tokens`, or `compute_grad_from_embeds` — those come from `HuggingFaceBackendModel`.
422422

423-
---
424-
425-
## Checklist
426-
427-
1. **Verify optimizer compatibility** — Instantiate an optimizer that requires your model's mixins and confirm `model_requirements` validation passes.
428-
2. **Usage stats** — Confirm `invoke_from_tokens` calls `_update_invoke_stats` with `n_tokens`, `n_samples`, and `count_backward`. Gradient methods should pass `count_backward=True` to `invoke_from_tokens`. For HuggingFace models this is already handled by `HuggingFaceBackendModel`. This takes care of usage tracking and FLOPs.
429-
3. **Test** — Write tests covering initialization, the inference method, and each mixin method. Test both single and multi-template cases. See `tests/models/` for examples.
430-
431-
> Want to contribute your model backend back to the TROPT package? See [CONTRIBUTING.md](https://github.com/matanbt/TROPT/blob/main/CONTRIBUTING.md) for the file placement, export, and testing steps.

tropt/optimizer/gcgplus_optimizer.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __init__(
7070
sample_n_replace: Union[int, Tuple[int, int]] = (1, 1),
7171
candidate_oversample_factor: float = 1.1,
7272
momentum: float = 0.0,
73+
skip_visited: bool = False,
7374
# Trigger buffer size:
7475
buffer_size: Optional[int] = None,
7576
n_grad_avg: int = 1,
@@ -97,6 +98,8 @@ def __init__(
9798
m = mu*m + (1-mu)*grad for candidate ranking instead of raw gradient.
9899
Defaults to 0.0 (no momentum).
99100
Reference: https://arxiv.org/abs/2405.01229 .
101+
skip_visited: If True, never re-select a previously accepted trigger
102+
(PAL-style); avoids cycling/stalling. Defaults to False.
100103
buffer_size: If set, maintain a buffer of the best triggers seen (from QCG paper). Each step
101104
starts from the best buffer entry and updates it with improved candidates.
102105
Defaults to None (no buffer).
@@ -147,6 +150,7 @@ def __init__(
147150
self.use_retokenize = use_retokenize
148151
self.candidate_oversample_factor = candidate_oversample_factor
149152
self.momentum = momentum
153+
self.skip_visited = skip_visited
150154
self.use_token_input_for_loss = use_token_input_for_loss
151155
self.buffer_size = buffer_size
152156
self.n_grad_avg = n_grad_avg
@@ -185,6 +189,7 @@ def optimize_trigger(
185189

186190
best = RunningBest()
187191
momentum_buffer: Optional[Tensor] = None
192+
visited: set[str] = set()
188193

189194
# Buffer initialization
190195
buffer: Optional[TriggerBuffer] = None
@@ -202,6 +207,8 @@ def optimize_trigger(
202207
).item()
203208
trigger_str = proxy_tokenizer.decode_trigger(trigger_ids)
204209
self.log(loss=current_loss, trigger_str=trigger_str)
210+
if self.skip_visited:
211+
visited.add(trigger_str)
205212

206213
n_replace_start, n_replace_end = self.sample_n_replace
207214

@@ -286,6 +293,14 @@ def optimize_trigger(
286293
candidate_trigger_ids = retokenize_filtering(
287294
candidate_trigger_ids, proxy_tokenizer
288295
)
296+
# Drop already-accepted triggers (PAL-style) before truncating
297+
if self.skip_visited and len(candidate_trigger_ids) > 0:
298+
cand_strs = proxy_tokenizer.decode_triggers(candidate_trigger_ids)
299+
keep = torch.tensor(
300+
[s not in visited for s in cand_strs],
301+
device=candidate_trigger_ids.device,
302+
)
303+
candidate_trigger_ids = candidate_trigger_ids[keep]
289304
# Truncate to n_candidates (after oversample + filter)
290305
candidate_trigger_ids = candidate_trigger_ids[: self.n_candidates]
291306

@@ -314,6 +329,8 @@ def optimize_trigger(
314329

315330
best.update(loss=current_loss, trigger_ids=trigger_ids, trigger_str=trigger_str)
316331
self.log(loss=current_loss, trigger_str=trigger_str)
332+
if self.skip_visited:
333+
visited.add(trigger_str)
317334

318335
# --- Finalize ---
319336
result = best.to_result()

0 commit comments

Comments
 (0)