> Raw Markdown twin (generated at build time from the source Markdown). Rendered page: https://docs.gatellm.io/en/howto/script-protocol-translate · Doc index: https://docs.gatellm.io/en/llms.txt


# Fix protocol translation with scripts

Cross-protocol translation is a core gateway capability, but sometimes you need to do things beyond the default translation — branch field handling by client protocol, inject different parameters by group, correct a protocol's dialect differences, etc. This chapter gives several practical before / after slot script examples.

Prerequisite: read [Write your first script transform](/en/howto/write-script-transform.md) first to understand the basic structure, mounting, and Context API.

The complete script for switching models by Claude Code plan mode has its own page, see [Switch model by Claude Code plan mode with a script](/en/howto/script-switch-model-by-plan-mode.md).

## Example 1: multimodal detection + routing switch (before slot)

Detect image input or web access parameters and switch to a multimodal model. Put it in the **before slot** (`request_transform_before`):

```javascript
function transform(body, context) {
    const proto = context.sourceProtocol;
    let hasImage = false;

    if (proto === "OpenAIChatCompletions") {
        const msgs = body.messages;
        if (Array.isArray(msgs)) {
            for (const msg of msgs) {
                if (Array.isArray(msg.content) &&
                    msg.content.some(b => b.type === "image_url")) {
                    hasImage = true;
                    break;
                }
            }
        }
    } else if (proto === "Anthropic") {
        const msgs = body.messages;
        if (Array.isArray(msgs)) {
            for (const msg of msgs) {
                if (Array.isArray(msg.content) &&
                    msg.content.some(b => b.type === "image")) {
                    hasImage = true;
                    break;
                }
            }
        }
    } else if (proto === "Google") {
        const contents = body.contents;
        if (Array.isArray(contents)) {
            for (const c of contents) {
                if (Array.isArray(c.parts) &&
                    c.parts.some(p => p.inlineData || p.fileData)) {
                    hasImage = true;
                    break;
                }
            }
        }
    }

    if (hasImage) {
        context.useModel("multimodal-model");
    }
    return body;
}
```

Key points:

- Must be in the **before slot**: it needs to read the client's original-format image fields, and different protocols have different field names (OpenAI `image_url`, Anthropic `image`, Google `inlineData` / `fileData`)
- Branch by `context.sourceProtocol`, otherwise cross-protocol clients are misdetected
- `context.useModel` does a full routing switch (base_url, protocol, credentials all change together)

## Image/web-access routing: prefer native `switch-route` (not a script)

Example 1's before-slot script can detect images by protocol branch, suitable for **cross-field computation / complex decisions**. But if the decision is only a **structural check** like "does the request have an image block / is web search enabled", use the native `request_payload` `switch-route` rule instead of writing a script: it does not read or write body content, works same-protocol and cross-protocol, and its predicate does not dereference binary bytes — disk spooling of large bodies stays enabled, so it will not OOM on large multimodal bodies nor be silently skipped.

For the full condition-path syntax (including the Anthropic client's **easily-missed `tool_result` nested-image deep path**), JSON shape, and verification steps, see [Route image-bearing requests to a vision model](/en/howto/route-image-requests-to-vision-model.md) — that is the single authoritative page for `switch-route`. Only fall back to Example 1's script approach when the decision logic goes beyond what structural checks can express.

## Example 2: set parameters by access key group

Set different request parameters based on the access key group (`context.accessKeyGroup`):

```javascript
function transform(body, context) {
    if (context.accessKeyGroup === "premium") {
        body.max_tokens = 8192;
    } else {
        body.max_tokens = 4096;
    }
    return body;
}
```

Mount in the after slot (`request_transform_after`) to differentiate quota by group.

## Example 3: inject a System Prompt

```javascript
function transform(body, context) {
    if (body.messages.length === 0 || body.messages[0].role !== "system") {
        const systemMsg = { role: "system", content: "You are a helpful assistant." };
        body.messages = [systemMsg, ...body.messages];
    }
    return body;
}
```

Mount in the after slot. The simple "inject if missing" logic can also use the `append-if-missing` rule of `request_payload` (better performance); scripts are suitable for scenarios that need complex decisions.

## FAQ

**Q: After a script switches routing, does the original route's after slot still run?**
No. Once the before slot triggers an immediate `useModel` routing switch, the whole pipeline is replaced with the target route's, and the original route's after slot does not run. So the `request_payload` rules must be configured separately on both the original route and the target route.

**Q: Can `context.useModel` switch to a target model with a different protocol from the current model?**
Yes. The before slot supports cross-protocol switching. But the after slot's `useModel` can only switch same-protocol — the after slot receives the upstream-format body, and cross-protocol would error.

**Q: The script runs too slowly?**
The main cost of script execution comes from JSON serialization/deserialization. The bigger the body, the slower. For simple field operations prefer `request_payload` (no serialization cost), and avoid unnecessary `deep_clone` / `json_encode` + `json_parse`.

**Q: The script reports `TooManyOperations`?**
It exceeded the operation limit (default 2000). Optimize loops, changing O(n²) to O(n). `SCRIPT_MAX_OPERATIONS` can be adjusted (but optimize the script first).

**Next**: [Scripting API reference](/en/reference/scripting-api.md) for the full built-in function tables; [Scripting config](/en/reference/scripting-config.md) for runtime limits and configuration; [Switch model by Claude Code plan mode with a script](/en/howto/script-switch-model-by-plan-mode.md) for routing by session marker.
