Skip to content

Rewrite Rules

Rewrite rules in v2 live in reusable rule sets. A rule set can attach to one or more providers through provider_rule_sets, and the provider’s attached rules run after protocol transform and before channel request preparation.

client request
-> classify / auth / route / balance
-> protocol transform to provider-native body
-> process rule sets
-> channel shape_request
-> channel prepare / upstream send

This page covers the implemented rule kinds. transform is the generic locate + actions (+ limit) engine; provider-specific behavior should live in config or console presets where possible.

RecordPurpose
rule_setsNamed reusable collection.
rulesRule rows inside a set.
provider_rule_setsAttachment of a set to a provider with sort_order.

During snapshot rebuild, enabled rule sets are compiled. Unparsable rules warn and skip. Provider attachments are flattened in attachment order, then sorted by fixed kind order.

Every rule row has:

  • kind: one of system_text, cache_breakpoint, rewrite, transform, header.
  • config_json: kind-specific config.
  • filter_model_pattern: optional glob against the prefix-stripped upstream model name.
  • filter_operation_keys: optional list of Operation values such as generate_content or stream_generate_content.
  • sort_order and enabled.

Filters are ANDed. Omitted filters match everything.

rewrite mutates a JSON body path:

{
"path": "stream_options.include_usage",
"action": "set",
"value_json": true
}

Supported actions are:

ActionBehavior
setCreates missing object parents and writes value_json at the leaf.
deleteRemoves an object key or array element if present. Missing paths are skipped.
mergeShallow-merges an object value_json into an existing object at the path.

Paths are dot-separated. Object keys and numeric array indexes are supported, for example messages.0.content. This is intentionally simple and fail-soft.

transform applies generic text replacements over matched JSON paths or over the serialized body with Rust regex:

{
"phase": "request",
"locate": { "match": "\\binternal-tool\\b" },
"actions": [{ "op": "replace_text", "with": "tool" }]
}

For structural values, use locate.path with dot-separated segments, * wildcards, and an optional exact from guard:

{
"phase": "response",
"locate": { "path": "content.*.name" },
"actions": [{ "op": "replace_text", "from": "tasklist", "with": "todowrite" }]
}

phase is request, response, or both; it defaults to request. Regex matching is broad after JSON serialization, so keep patterns precise.

header sets or merges a request header:

{
"name": "anthropic-beta",
"value": "extended-cache-ttl-2025-04-11",
"mode": "merge"
}

override replaces the header. merge comma-appends with de-duplication, which is useful for list-valued headers such as anthropic-beta.

Rules apply in this fixed order, regardless of attachment order:

system_text -> cache_breakpoint -> rewrite -> transform -> header

Within each kind, set and rule sort order is preserved. A bad or non-applicable rule should not break traffic; it warns and skips.