Repository navigation
FEAT Add generic GCG model adapters and an open-weight SLM compatibility matrix #2489
Description
Is your feature request related to a problem? Please describe.
GCG loads models through AutoModelForCausalLM, but its gradient path still hard-codes exact classes for GPT-2/GPT-J, GPT-NeoX, Llama, Mistral/Mixtral, and Phi-3. A newly supported Hugging Face causal model can therefore load successfully and then fail when GCG tries to obtain its input embeddings.
Prompt slicing also assumes a tokenizer with a usable chat template and character-to-token mapping. There is no explicit preflight check or maintained compatibility matrix, so unsupported tokenizer behavior may fail late or silently compute incorrect goal, control, or target slices.
We want GCG to support the leading open-weight small language model families without losing its multi-model, multi-prompt, held-out evaluation, or progressive optimization capabilities.
Describe the solution you'd like
Introduce small model and tokenizer adapter interfaces for GCG.
The model adapter should:
- use the standard Hugging Face
get_input_embeddings()contract instead of architecture-specificisinstancebranches where possible; - expose the embedding matrix, device, dtype, vocabulary size, and relevant cache capabilities;
- validate that the model supports differentiable causal-LM input-embedding optimization;
- produce actionable capability errors before worker processes and long-running optimization begin.
The tokenizer adapter should:
- render conversations using the tokenizer's chat template;
- locate goal, control, assistant-role, and target token slices robustly;
- validate decode/encode round trips and vocabulary compatibility;
- expose a stable tokenizer/vocabulary identity that later multi-model aggregation work can use safely;
- allow narrowly scoped family-specific behavior only when the standard Hugging Face interfaces are insufficient.
Create and document an initial compatibility matrix for representative causal text models/tokenizers from these open-weight SLM families: Qwen, Phi, Gemma, Llama, Mistral/Mixtral/Nemo, SmolLM, and applicable DeepSeek-distilled models. Keep the matrix architecture-based so new checkpoints can be added without copying model-specific attack code.
Acceptance criteria:
- The hard-coded embedding helper branches are replaced by the adapter contract or reduced to documented exceptions.
- Every supported family has tokenizer/template/slice tests using a small representative tokenizer and a tiny or randomly initialized compatible model where practical.
- At least one opt-in GPU smoke test exercises the complete gradient and candidate-scoring path for representative modern architectures.
- Unsupported models fail during preflight with a clear explanation of the missing capability.
- Adding support for another standard Hugging Face causal architecture normally requires a compatibility test or adapter registration, not edits to the core GCG optimization loop.
- Existing single-model, arbitrary multi-model, single-prompt, multi-prompt, held-out evaluation, and progressive model/goal behavior are preserved.
- Support must not be restricted to a single model, exactly two models, or a single shared prompt.
Describe alternatives you've considered, if relevant
We could continue adding architecture-specific isinstance branches, but that does not scale with the Hugging Face model ecosystem and leaves tokenizer assumptions implicit. Replacing PyRIT's implementation with a single-model GCG library would simplify compatibility but would lose PyRIT's multi-model, multi-prompt, and progressive optimization properties.
Additional context
Track this as a focused child of #960. Coordinate performance-related cache work with #962, but keep prefix caching, quantization, model sharding, and heterogeneous-tokenizer gradient aggregation as separate changes so this issue remains reviewable.
Activity
devangpratap commentedon Aug 26, 2026
I would like to take this.
I reproduced the embeddings half on
main(9616789) first, so here is what I am working from.The three accessors in
pyrit/executor/promptgen/gcg/attack/base/attack_manager.py(get_embedding_layer,get_embedding_matrix,get_embeddings) each dispatch through anisinstancechain over six hard-coded classes and end inraise ValueError(f"Unknown model type: {type(model)}"). Since GCG loads throughAutoModelForCausalLM, anything outside that list loads fine and then fails at the gradient step.Building tiny random configs for a spread of architectures, no weights or GPU needed:
Every one of them implements
PreTrainedModel.get_input_embeddings(), so the information GCG needs is already reachable through the public API.Proposed approach
model.get_input_embeddings()in all three accessors instead of raising, so anyAutoModelForCausalLMworks by default. Keep the existing branches where there is a genuine quirk rather than a naming difference: GPT-2/GPT-J and GPT-NeoX currently call.half()inget_embeddingswhile the Llama/Mistral/Phi-3 paths do not, and I do not want to silently change that dtype behaviour for existing users.Two questions before I open anything:
.half()asymmetry: is that deliberate for the older models, or incidental? If it is incidental I would rather leave it exactly as-is in this PR and raise it separately than fold a behaviour change into a compatibility fix.romanlutz commentedon Aug 27, 2026
I think either way is fine. Heads up it may be a couple weeks before we get to reviewing this because of an upcoming release.
devangpratap commentedon Sep 10, 2026
Thanks for the review and the merge.
#2501 only covered the embedding accessors, but I wrote
Closes #2489in the description, so this issue auto-closed when it merged. That was my mistake. Could you reopen it?Still outstanding from the original scope:
I would like to take those next if the issue stays open.
romanlutz commentedon Sep 16, 2026
devangpratap you're quite right! Please feel free to start with those. However, I'd recommend keeping changesets as small as possible even if that means creating multiple PRs.
romanlutz commentedon Oct 18, 2026
Thanks again for the work here. I reviewed the current
mainstate against the original acceptance criteria.Completed
get_input_embeddings()contract and added tiny-model accessor coverage across legacy and newer architectures.Remaining
devangpratap, do you still intend to continue with the remaining pieces? If so, do you need any help splitting or reviewing the work? Otherwise, would you prefer that we make the issue available for someone else to pick up? If we do not hear back by October 22, 2026, we may make it available to other contributors.
devangpratap commentedon Oct 19, 2026
Yes, I'd like to keep going. I'll start with the preflight and keep each piece in its own PR.
One question for the modern-architecture coverage: are tiny randomly initialized configs built offline okay, or would you rather the tests load real tiny checkpoints from the Hub?
romanlutz commentedon Oct 22, 2026
Thanks for continuing this work. With #3338 merged, the capability-preflight portion is complete, but #2489 still has remaining scope.
Completed
get_input_embeddings()contract and added tiny-model coverage across legacy and newer architectures.use_fast=True.Remaining
Suggested next steps
Tiny randomly initialized configs built offline are appropriate for the family compatibility tests where practical; the opt-in GPU smoke test should exercise a real representative checkpoint and the full path.