Skip to content

Commit 72a3099

Browse files
committed
Minor fixes
1 parent f8d7380 commit 72a3099

10 files changed

Lines changed: 102 additions & 18 deletions

File tree

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,10 @@ tropt/recipe_hub/PGD.py
2424
tropt/recipe_hub/PGD__geisler2024.py
2525
tropt/optimizer/pgd_optimizer.py
2626

27+
# Local artifacts & dev scratch — never tracked or published
28+
wandb/
29+
logs/
30+
claude_stuff/
31+
.claude/
32+
*.egg-info/
33+

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Open an issue with `[feature]` or `[recipe]` before writing code, especially for
5858
A contribution that touches a component must conform to its interface contract. Read in this order:
5959

6060
- [`DESIGN.md`](DESIGN.md) — full design philosophy (modularity, backend vs. frontend, why the contracts are the way they are).
61-
- [`docs/guides/`](docs/guides/) — step-by-step per-component walkthroughs (source of truth for each contribution type).[TODO link to the actual docs]
61+
- [`docs/guides/`](docs/guides/) — step-by-step per-component walkthroughs (source of truth for each contribution type).
6262
- **Agent files.**
6363
- [`CLAUDE.md`](CLAUDE.md) — fast-path orientation: the four components, mixin contract, `ModelInput`/`ModelOutput`/`Targets`, setup-then-compute.
6464
- [`skills/tropt/SKILL.md`](skills/tropt/SKILL.md) — task-routed pointers and cross-cutting pitfalls (mixin mismatches, thinking-model alignment, multi-model OOM, etc.).
@@ -75,7 +75,7 @@ A contribution that touches a component must conform to its interface contract.
7575

7676
For every contribution:
7777

78-
1. **Mirror the source layout in `tests/`.** Cover output shape, sign conventions, mixin requirements, and known input/output pairs. See [`TESTING.md`](TESTING.md) for tolerances and per-component checklists.
78+
1. **Add tests** following the conventions and per-component checklists in [`TESTING.md`](TESTING.md); match the layout of the existing tests under [`tests/`](tests/). Cover output shape, sign conventions, mixin requirements, and known input/output pairs. Note that the end-to-end suite runs tiny CPU models with few steps, so tests assert shapes and finiteness — not loss decrease or trigger content, which are flaky at that scale.
7979
2. **Regenerate the compatibility matrix** if you touched a model, optimizer, mixin, or loss `require_*` flag: `python docs/scripts/generate_compat_matrix.py`. Commit the regenerated [`docs/guides/compatibility_matrix.md`](docs/guides/compatibility_matrix.md).
8080
3. **Update the relevant README row** — recipes need an entry in [`tropt/recipe_hub/README.md`](tropt/recipe_hub/README.md) (key, description, target model, required access, paper, file).
8181
4. **Smoke-test end-to-end** on a small model (e.g. `google/gemma-3-270m-it`) before submitting.

DESIGN.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,6 @@ The repository exposes two interfaces for managing recipes:
287287

288288
<!-- * **Config Runner [`runner/main.py`].** A flexible runner that constructs a recipe from a YAML configuration file. The runner uses [Hydra](https://hydra.cc/) to manage configurations, allowing users to specify the model, loss, optimizer, and their parameters in a structured way without writing new code. -->
289289

290-
* **Full evaluations [WIP].** [TODO]
291-
292290
## Summary
293291

294292

docs/_static/custom.css

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,17 @@ html[data-theme="dark"] .tropt-cta-secondary:hover {
406406
/* (Dot-grid layer is now `.tropt-hero-dots` defined above with the hero.) */
407407

408408
@media (max-width: 720px) {
409-
.tropt-optbar { gap: 6px; padding: 8px 10px; }
409+
/* The slot-machine row is wider than a phone viewport when forced onto one
410+
line, so it gets clipped at the screen edge (the trailing "!" disappears)
411+
and reads as a broken banner. Let it wrap onto multiple lines and never
412+
exceed the viewport. */
413+
.tropt-optbar {
414+
gap: 6px;
415+
padding: 8px 10px;
416+
flex-wrap: wrap;
417+
max-width: 100%;
418+
box-sizing: border-box;
419+
}
410420
.tropt-tok { font-size: 12.5px; padding: 7px 10px; }
411421
.tropt-tok-opt { min-width: 100px; }
412422
}

pyproject.toml

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,28 @@ license = {text = "MIT"}
1212
authors = [
1313
{name = "Matan Ben-Tov"}
1414
]
15+
keywords = [
16+
"adversarial-attacks",
17+
"jailbreak",
18+
"red-teaming",
19+
"trigger-optimization",
20+
"discrete-optimization",
21+
"prompt-optimization",
22+
"llm",
23+
"nlp",
24+
"robustness",
25+
"ai-safety",
26+
]
27+
classifiers = [
28+
"Development Status :: 3 - Alpha",
29+
"Intended Audience :: Science/Research",
30+
"License :: OSI Approved :: MIT License",
31+
"Operating System :: OS Independent",
32+
"Programming Language :: Python :: 3.10",
33+
"Programming Language :: Python :: 3.11",
34+
"Programming Language :: Python :: 3.12",
35+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
36+
]
1537

1638
# Core dependencies - minimal for basic HuggingFace model optimization
1739
dependencies = [
@@ -37,8 +59,9 @@ dependencies = [
3759
]
3860

3961
[project.urls]
40-
Repository = "https://github.com/matanbt/tropt"
41-
Issues = "https://github.com/matanbt/tropt/issues"
62+
Homepage = "https://matanbt.github.io/TROPT"
63+
Repository = "https://github.com/matanbt/TROPT"
64+
Issues = "https://github.com/matanbt/TROPT/issues"
4265

4366
[project.optional-dependencies]
4467
# API model integrations (install separately as needed)
@@ -134,6 +157,15 @@ allow-direct-references = true
134157
[tool.hatch.build.targets.wheel]
135158
packages = ["tropt"]
136159

160+
# Explicit allowlist so the sdist ships only the package + metadata.
161+
[tool.hatch.build.targets.sdist]
162+
include = [
163+
"tropt/",
164+
"README.md",
165+
"LICENSE",
166+
"pyproject.toml",
167+
]
168+
137169
[tool.ruff]
138170
include = ["tropt/**/*.py", "tests/**/*.py"]
139171
extend-exclude = ["tests/_legacy"]

tropt/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Expose tropt.__version__, read from install metadata (pyproject) with a source-tree fallback.
2+
import importlib.metadata as _metadata
3+
4+
try:
5+
__version__ = _metadata.version("tropt")
6+
except _metadata.PackageNotFoundError: # running from a source tree without install
7+
__version__ = "0.0.0+unknown"
8+
9+
del _metadata

tropt/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ def to_device(self, device: torch.device | str) -> "Targets":
231231
for k, v in self:
232232
if isinstance(v, Tensor):
233233
updates[k] = v.to(device)
234-
if isinstance(v, list) and isinstance(v[0], Tensor):
234+
elif isinstance(v, list) and v and isinstance(v[0], Tensor):
235235
updates[k] = [t.to(device) for t in v]
236236
return self.model_copy(update=updates)
237237

tropt/model/google/encoder.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,31 +58,55 @@ class EncoderGeminiModel(EncoderBaseModel, LossTextAccessMixin):
5858
"""
5959

6060
def __init__(
61-
self, model_name="gemini-embedding-001", d_model: int = 3072, **kwargs
61+
self,
62+
model_name="gemini-embedding-001",
63+
d_model: int = 3072,
64+
65+
# Vertex configuration:
66+
use_vertex: bool = False,
67+
project: Optional[str] = None,
68+
location: str = "us-central1",
69+
default_text_type: Optional[str] = None,
70+
**kwargs,
6271
):
6372
"""
6473
Initializes the Gemini Encoder Model wrapper.
6574
6675
Args:
6776
model_name: The name of the Gemini embedding model to use.
6877
d_model: The dimensionality of the embeddings (e.g., 768, 3072).
78+
use_vertex: Embed via the Vertex AI backend (ADC) instead of AI Studio.
79+
Needed for Vertex-only models such as ``text-embedding-005``.
80+
project: Vertex project (only used when ``use_vertex``; falls back to the
81+
``GOOGLE_CLOUD_PROJECT`` env var when None). ``location`` is the region.
82+
default_text_type: Fallback ``text_type`` ("document"/"query") used when a
83+
caller doesn't pass one — e.g. the optimizer's
84+
``compute_loss_from_texts``, so candidates embed as documents.
6985
7086
Note:
71-
Requires `os.environ["GOOGLE_API_KEY"]` to be set externally.
72-
87+
AI Studio backend requires ``os.environ["GOOGLE_API_KEY"]``; Vertex backend
88+
requires Application Default Credentials.
7389
"""
7490
# Import google.genai only when instantiating (optional dependency)
7591
from google import genai
7692

77-
self._client = genai.Client(
78-
http_options=genai.types.HttpOptions(timeout=_REQUEST_TIMEOUT_MS),
79-
)
93+
http_options = genai.types.HttpOptions(timeout=_REQUEST_TIMEOUT_MS)
94+
if use_vertex:
95+
client_kwargs = {"vertexai": True, "location": location,
96+
"http_options": http_options}
97+
if project:
98+
client_kwargs["project"] = project
99+
self._client = genai.Client(**client_kwargs)
100+
else:
101+
self._client = genai.Client(http_options=http_options)
80102
self.model_name = model_name
81103
self._d_model = d_model # for gemini-embedding-001: could be 768, 1536, or 3072
104+
self._default_text_type = default_text_type
82105
self._text_to_task_type = {
83106
"document": "RETRIEVAL_DOCUMENT",
84107
"query": "RETRIEVAL_QUERY",
85108
}
109+
self._max_batch = 100
86110

87111
@property
88112
def d_model(self) -> int:
@@ -104,6 +128,8 @@ def invoke_from_texts(
104128
Returns:
105129
A ModelOutput containing the generated embeddings.
106130
"""
131+
if text_type is None:
132+
text_type = self._default_text_type
107133
assert text_type in (
108134
None,
109135
"document",
@@ -113,8 +139,6 @@ def invoke_from_texts(
113139

114140
import google.genai as genai
115141

116-
# Gemini's BatchEmbedContents caps at 100 requests per call; chunk.
117-
MAX_BATCH = 100
118142
all_embeddings = []
119143
total_tokens = 0
120144
cfg = genai.types.EmbedContentConfig(
@@ -134,8 +158,8 @@ def _embed_chunk(chunk):
134158
contents=chunk, model=self.model_name, config=cfg,
135159
)
136160

137-
for start in range(0, len(input_texts), MAX_BATCH):
138-
chunk = input_texts[start : start + MAX_BATCH]
161+
for start in range(0, len(input_texts), self._max_batch):
162+
chunk = input_texts[start : start + self._max_batch]
139163
response = _embed_chunk(chunk)
140164
assert response is not None and response.embeddings is not None, (
141165
"embed_content returned no embeddings"

tropt/model/huggingface/lm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ def invoke_from_texts(
531531
).to(self.device)
532532
padded_seq_len = inputs.input_ids.shape[1]
533533
n_prompt_tokens = inputs.input_ids.numel()
534+
# n_prompt_tokens = int(inputs.attention_mask.sum().item()) # [Disabled] an alternative that excludes padding tokens
534535

535536
# ---- Shared forward pass (prefill logits and/or first-token logprobs) ----
536537
prefill_response_logits = None

tropt/optimizer/gcg_optimizer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ def __init__(
7070
self.token_constraints = token_constraints
7171
self.use_retokenize = use_retokenize
7272

73+
# validations:
74+
assert self.loss_func.is_differentiable, "GCGOptimizer requires a differentiable loss function."
75+
7376
def optimize_trigger(
7477
self,
7578
templates: TextTemplates,

0 commit comments

Comments
 (0)