Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Selectively Converting

Use selective conversion to change part of a prompt while keeping the surrounding instructions unchanged. This guide runs locally: it uses Base64, ROT13, and a TextTarget, not an LLM or API credentials.

GoalUse
Select text you already knowPut ⟪ and ⟫ around each region.
Apply several stages to the same regionsUse keep_tokens=True in direct calls, or a preserving selective wrapper in a pipeline.
Consume one selection layer per stageNest marker pairs in the input.
Select words or a character range programmaticallyGive SelectiveTextConverter a selection strategy.

On an ordinary converter, convert_async changes the whole value, including marker characters. Use convert_tokens_async to select marked regions. SelectiveTextConverter.convert_async instead applies its configured selection strategy. Attack converter pipelines call convert_tokens_async automatically.

1. Convert Only Marked Text

Mark each region separately. The converter removes the selected marker pairs and leaves all other text unchanged. Multiple regions, line breaks, and empty regions are supported. Every opening marker must have a closing marker.

Decode: aGVsbG8= and d29ybGQ=

Important: once all markers are gone, the next converter converts the whole value. A single pair does not keep a region selected for an entire chain.

Qrpbqr: nTIfoT8= naq q29loTD=

2. Keep the Same Regions Selected Across Stages

For direct calls, set keep_tokens=True on convert_tokens_async. The shared parser retains each selected pair without changing the surrounding text. With no markers, it wraps the whole text result; non-text outputs are not wrapped. The default is False, so the final call below consumes the pairs.

Decode: ⟪aGVsbG8=⟫ and ⟪d29ybGQ=⟫
Decode: nTIfoT8= and q29loTD=

For attack and API pipelines, configure a selective wrapper: preserve_tokens=True keeps a marker pair around each converted region at its original position. A later TokenSelectionStrategy finds those regions without selecting the surrounding instructions. Use preserve_tokens=False on the final stage to consume the pairs. This setting uses the same shared retention logic as keep_tokens.

This example selects the second half by word position, then applies three transformations. Each token-based stage converts the regions produced by the first stage.

tell me how ⟪dG8=⟫ ⟪ZG8=⟫ ⟪aXQ=⟫
tell me how ⟪qT8=⟫ ⟪MT8=⟫ ⟪nKD=⟫
tell me how cVQ4PQ== TVQ4PQ== bktEPQ==

Without markers, a token-selection wrapper converts the whole prompt. If preserve_tokens=True, it wraps that whole result. It does not guess which words you intended to select.

⟪uryyb⟫

3. Use Nested Markers for a Fixed Number of Stages

Every stage processes all innermost regions in parallel. A parent region waits for a later stage. An ordinary converter consumes one pair from each selected region; a preserving wrapper consumes none. Explicit nesting in the input is never collapsed.

Two pairs per region keep Decode: unchanged through Base64 and ROT13:

Decode: ⟪aGVsbG8=⟫ and ⟪d29ybGQ=⟫
Decode: nTIfoT8= and q29loTD=

For Translate to French -> Base64 -> ROT13, use three pairs per region:

Decode this recursively: ⟪⟪⟪Hello⟫⟫⟫ and ⟪⟪⟪Goodbye⟫⟫⟫

If translation returns Bonjour and Au revoir, the stages produce:

StageFirst regionSecond region
Translate to French⟪⟪Bonjour⟫⟫⟪⟪Au revoir⟫⟫
Base64⟪Qm9uam91cg==⟫⟪QXUgcmV2b2ly⟫
ROT13Dz9hnz91pt==DKHtpzI2o2yl

The prefix and and stay unchanged. This table does not call a live translation target.

Regions can have different depths. A completed region stays unchanged while other marked regions remain. When no markers remain anywhere, later stages convert the whole prompt. If the chain stops before all layers are consumed, the remaining markers stay in the result.

4. Select Words or Characters Programmatically

Use a word strategy for separate words, or a character strategy for one continuous range. A selection strategy runs on the value entering that stage, not on the original prompt. Translation and other transformations can change word counts or character positions.

StrategySelection
WordIndexSelectionStrategyZero-based word indices.
WordKeywordSelectionStrategyWords matching configured keywords.
WordRegexSelectionStrategyWords matching a regular expression.
WordPositionSelectionStrategyA range of proportional word positions.
WordProportionSelectionStrategyA random proportion of words; set a seed for repeatable selection.
IndexSelectionStrategyA continuous character range; the end is exclusive.
TokenSelectionStrategyInnermost regions marked by an earlier stage or by the input.

Word selection uses a space as its default separator. Set word_separator when your input uses another separator. Character selection can operate on phrases or multiline text.

Codes MTIz and NDU2 stay separate
keep uryyb unchanged

5. Put the Chain in an Attack

Initialize PyRIT before constructing a target, normalizer, or attack. ConverterConfiguration.from_converters retains the converter order. The attack normalizer applies the same marker rules as the direct examples. Request converters change the outbound prompt; response converters, if configured, change the target reply.

TextTarget writes the converted request locally and returns no reply. Replace it with your configured model target when you want to send the request to an LLM.

Auto-discovered plaintext environment file ./.pyrit/.env will be loaded. Azure Key Vault through env_akv_ref is more secure for shared or deployed secrets; use .env.local only for deliberate local overrides. To inspect a resolved AKV-only configuration from a source checkout, run `python -m build_scripts.export_akv_environment`; it writes ~/.pyrit/.env_akv.
WARNING: Auto-discovered plaintext environment file ./.pyrit/.env will be loaded. Azure Key Vault through env_akv_ref is more secure for shared or deployed secrets; use .env.local only for deliberate local overrides. To inspect a resolved AKV-only configuration from a source checkout, run `python -m build_scripts.export_akv_environment`; it writes ~/.pyrit/.env_akv.
Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']
Loaded environment file: ./.pyrit/.env
Loaded environment file: ./.pyrit/.env.local
user: tell me how cVQ4PQ== TVQ4PQ== bktEPQ==

6. Configure Custom Markers

Unicode markers are defaults, not a requirement. Use a distinct, non-empty opening and closing string. Identical opening and closing strings form alternating flat pairs and cannot express nesting. Longer markers reduce accidental matches but do not eliminate them.

Use the same marker pair on a token-selection wrapper and its attack’s PromptNormalizer. An explicit convert_tokens_async call uses its start_token and end_token arguments for retained pairs. On a selective wrapper, either keep_tokens=True or configured preserve_tokens=True retains the pair. An empty normalizer marker is rejected at construction, before any pipeline runs. The GUI selection button still inserts the default Unicode pair.

Decode: <|pyrit_start_8f3a|>aGVsbG8=<|pyrit_end_8f3a|>
user: Decode: nTIfoT8=

API clients can set start_token and end_token on ConverterPreviewRequest and AddMessageRequest, including queued sends. The same settings apply to request and response converter chains. A preconverted message piece is sent as supplied; the backend does not run request converters on it again.

Choose markers that are unlikely to appear in prompts or replies. If response converters are configured, an unmatched marker in a reply raises before that reply is stored. Text such as echo x >> log or a Python >>> prompt is literal when using the longer markers above.

7. Compose Wrappers Without Adding Selection Layers

Wrapping a token-selection wrapper in another token-selection wrapper with the same markers does not create a new selection. If either wrapper preserves tokens, they retain one pair for that selection. Explicit input layers remain intact. Different marker pairs and programmatic selection strategies are separate selections; their boundaries are not collapsed. Per-call markers determine the outer selection, even when they differ from its constructor defaults.

Converter-generated marker text is not evidence that a wrapper already preserved its boundaries. The wrapper keeps its own pair, and generated markers are not processed again during the same call.

prefix ⟪jbeq⟫ suffix
prefix ⟪⟪jbeq⟫⟫ suffix
prefix [⟪jbeq⟫] suffix

Limits and Custom Converters

Selective conversion requires text input and output. Use ordinary converters for whole-value media conversion. Malformed markers raise before any selected region is converted. For the same seed and execution context, native token-selection calls produce the same result through direct calls, attack pipelines, and API previews.

Selective subclasses that override convert_async still receive the selected text. They can call super().convert_async to reuse selection without losing their own transformation.

A custom converter can override convert_tokens_async to own whole-prompt behavior. A token-selection wrapper with preserve_tokens=False and the default keep_tokens=False delegates to that override without new arguments. The custom override, not the wrapper, then determines which text changes. Requesting preserve_tokens=True or keep_tokens=True around such an override is rejected before it runs: the wrapper cannot recover selected-region boundaries from an arbitrary returned string. For per-region preservation, implement the transformation in convert_async and use the shared token parser. No signature probing, whole-prompt preservation fallback, or additional pipeline framework is needed.