# Fusion

Fusion sends one prompt to several models at once. A judge model then picks the best answer or
writes one answer from all of them. Call `POST /v1/chat/completions` with the model
`routerplus/fusion`.

## Request

```bash
curl https://demo-api.routerplus.com/v1/chat/completions \
  -H "Authorization: Bearer $TM_API_KEY" -H "content-type: application/json" \
  -d '{
    "model": "routerplus/fusion",
    "messages": [{ "role": "user", "content": "Explain what a mutex is." }],
    "fusion": {
      "models": ["claude-sonnet-5-5", "gpt-6-luna", "moonshotai/kimi-k3"],
      "judge": "claude-opus-5-5",
      "mode": "synthesize",
      "max_tokens_each": 1024
    }
  }'
```

| Field | Rule |
|---|---|
| `fusion.models` | 2 to 5 different model ids. These are the candidates. |
| `fusion.judge` | Optional. The model that judges. The default is the first candidate. |
| `fusion.mode` | `"synthesize"` (default): the judge writes one answer from the candidates. `"judge"`: the judge picks the best candidate, and you get that answer word for word. |
| `fusion.max_tokens_each` | Optional. The output limit for each candidate. Without it, each candidate gets your `max_tokens`. |

- Each candidate gets your messages and parameters. Candidates do not stream.
- The judge sees the candidate answers as A, B, C and so on, never the model names.
- `stream: true` streams the answer. In `synthesize` mode the judge's answer streams as the
  judge writes it. In `judge` mode the picked answer streams after the judge picks.

## Response

The response is an OpenAI chat completion with `model: "routerplus/fusion"`. `usage` is the sum
over every candidate and the judge. `usage.cost` is present when the calls report a cost. A
streamed response carries `usage` and `fusion` on its last chunk before `[DONE]`.

```json
{
  "model": "routerplus/fusion",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" }],
  "usage": { "prompt_tokens": 1830, "completion_tokens": 892, "total_tokens": 2722, "cost": 0.0213 },
  "fusion": {
    "mode": "synthesize",
    "candidates": [
      { "model": "claude-sonnet-5-5", "finish_reason": "stop", "usage": { "prompt_tokens": 20, "completion_tokens": 300, "total_tokens": 320 } },
      { "model": "gpt-6-luna", "finish_reason": null, "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 }, "error": "the provider is unavailable" },
      { "model": "moonshotai/kimi-k3", "finish_reason": "stop", "usage": { "prompt_tokens": 20, "completion_tokens": 280, "total_tokens": 300 } }
    ],
    "judge": { "model": "claude-opus-5-5", "usage": { "prompt_tokens": 1790, "completion_tokens": 312, "total_tokens": 2102 } }
  }
}
```

In `judge` mode, `fusion.judge` also has `pick` (the letter) and `selected` (the model whose
answer you got).

## When a call fails

- RouterPlus leaves out a candidate that fails. Its entry in `fusion.candidates` has `error`.
- When only one candidate answers, you get its answer with no judge call.
  `fusion.judge.skipped` says so.
- When every candidate fails, you get the first candidate's error.
- When the judge fails, you get the longest candidate answer. `fusion.judge.error` says why
  and `fusion.judge.fallback` names the candidate.

## Billing

RouterPlus bills each candidate call and the judge call as a direct call to its model. A fusion
request costs the sum of those calls.

## Errors

| Status | Cause |
|---|---|
| 400 `invalid_request` | The `fusion` object is missing or invalid. The message names the field. |
| 400 | `n` is above 1. Fusion returns one choice. |

Fusion does not work with [server tools](server-tools.md).
