Skip to navigation

Pi

Add Gateway as a provider in Pi and let a routing policy pick the model

Pi builds its model picker only from ~/.pi/agent/models.json, never querying Gateway, so new catalog models appear only once you add them or regenerate the file. Use an alias entry to let a routing policy choose, or list models by hand or from the catalog. Shared setup: Building with coding agents.

Configure Pi

Export MERGE_GATEWAY_API_KEY, then add a provider with an alias entry and a fixed model:

~/.pi/agent/models.json
{
"providers": {
"merge-gateway": {
"name": "Merge Gateway",
"baseUrl": "https://api-gateway.merge.dev/v1/openai",
"api": "openai-completions",
"apiKey": "$MERGE_GATEWAY_API_KEY",
"compat": { "supportsReasoningEffort": false, "sendSessionAffinityHeaders": true },
"models": [
{
"id": "@alias/coding-heavy",
"name": "Gateway coding policy",
"contextWindow": 200000,
"maxTokens": 32000,
"input": ["text", "image"]
},
{
"id": "anthropic/claude-sonnet-5",
"name": "Claude Sonnet 5",
"reasoning": true,
"contextWindow": 1000000,
"maxTokens": 64000,
"input": ["text", "image"]
}
]
}
}
}

apiKey reads your exported key with Pi’s $VAR syntax. Pi reloads the file whenever you open the picker: run pi, then /model, type merge, and select an entry.

Gotchas

  • Set contextWindow, maxTokens, and input on every entry, or Pi assumes 128K context, 16K output, and text only
  • For an alias or default_routing entry, set contextWindow to the smallest context window among its routable models. Pi compacts against it, so a larger value can overflow the serving model.
  • Gateway clamps maxTokens to the serving model’s output limit rather than letting the vendor reject it, so one value works across a policy’s models
  • supportsReasoningEffort: false stops Pi sending reasoning_effort, which some vendors reject. "reasoning": true labels reasoning models in the picker.
  • sendSessionAffinityHeaders: true groups each Pi session into one thread in Logs

Generate the model list from the catalog

This writes every chat model Gateway offers you, with limits, modalities, and prices from each model’s first listed vendor, alias entry first. It merges into your models.json, keeping other providers and replacing the merge-gateway list.

mkdir -p ~/.pi/agent
[ -f ~/.pi/agent/models.json ] || echo '{"providers":{}}' > ~/.pi/agent/models.json
curl -s "https://api-gateway.merge.dev/v1/models?limit=500" \
-H "Authorization: Bearer $MERGE_GATEWAY_API_KEY" \
| jq '{
providers: {
"merge-gateway": {
name: "Merge Gateway",
baseUrl: "https://api-gateway.merge.dev/v1/openai",
api: "openai-completions",
apiKey: "$MERGE_GATEWAY_API_KEY",
compat: { supportsReasoningEffort: false, sendSessionAffinityHeaders: true },
models: ([
{ id: "@alias/coding-heavy", name: "Gateway coding policy",
contextWindow: 200000, maxTokens: 32000, input: ["text", "image"] }
] + [
.data[]
| . as $m
| (.vendors | to_entries[0].value) as $v
| select($v.capabilities.output | index("text"))
| {
id: $m.model,
name: $m.display_name,
contextWindow: $v.context_window,
maxTokens: $v.max_output_tokens,
input: [$v.capabilities.input[] | select(. == "text" or . == "image")],
reasoning: $v.capabilities.supports_reasoning,
cost: { input: $v.pricing.input_per_million, output: $v.pricing.output_per_million }
}
])
}
}
}' > /tmp/merge-gateway-models.json
jq -s '.[0] * .[1]' ~/.pi/agent/models.json /tmp/merge-gateway-models.json \
> /tmp/pi-models-merged.json && mv /tmp/pi-models-merged.json ~/.pi/agent/models.json

limit=500 is the largest page /v1/models returns. If the response has "has_more": true, pass its next_cursor as ?cursor= for the rest.

Next steps