diff --git a/README.md b/README.md index af457d1..d04aa73 100644 --- a/README.md +++ b/README.md @@ -125,10 +125,11 @@ routstrd start --host 0.0.0.0 Only expose the daemon behind appropriate network controls. -With specific provider: +Pin all requests to one provider (no cross-provider failover): ```sh routstrd start --provider https://your-provider.com ``` +See [Provider pinning](#provider-pinning) for the request-level header/query. ### CLI Commands @@ -233,6 +234,23 @@ Any unmatched `POST` path is proxied to the selected provider with the incoming path preserved, so `POST /v1/messages` (Anthropic Messages API) and `POST /v1/responses` (OpenAI Responses API) work in their own formats too. +#### Provider pinning + +Pin a request to a single provider with either the `x-routstr-provider` header +or the `?provider=` query parameter (the query takes precedence): + +```sh +curl -H 'x-routstr-provider: https://your-provider.com' ... \ + http://127.0.0.1:8008/v1/chat/completions +``` + +A pin is strict: the request is sent only to that provider. On an upstream +error — including a 400/422 the node itself rejects — the request is **not** +failed over to another node; the error is returned to the client. Retries +against the same provider (top-up, mint fallback) still happen. The config +`provider` and `routstrd start --provider` set the same strict pin as a +default for every request. + Request body: ```json { @@ -292,6 +310,10 @@ choose and pin an advertised model path for `deepseek-v4.1-flash` requests. Explicit `x-routstr-model-path` request headers work independently of this setting and take precedence. Restart the daemon after changing `autoModelPath`. +`provider` pins every request to one node (same strict behavior as the +`x-routstr-provider` header / `?provider=` query). Leave it `null` to let the +router pick the cheapest eligible provider and fail over between nodes. + ### Environment Variables - `ROUTSTRD_DIR` - Config directory (default: `~/.routstrd`) diff --git a/SKILL.md b/SKILL.md index 84a135b..69f5175 100644 --- a/SKILL.md +++ b/SKILL.md @@ -49,7 +49,7 @@ Start the background daemon process. |--------|-------------| | `--port ` | Port to listen on (default: 8008) | | `--host ` | Bind address (default: 127.0.0.1) | -| `-p, --provider ` | Default provider to use | +| `-p, --provider ` | Pin all requests to this provider (no cross-provider failover) | ### `routstrd daemon` @@ -479,6 +479,20 @@ The incoming request path is forwarded to the provider, so the Anthropic Messages API (`POST /v1/messages`) and the OpenAI Responses API (`POST /v1/responses`) are proxied in their own formats as well. +#### Provider pinning + +A provider can be pinned with any of: + +- request header `x-routstr-provider: ` +- query parameter `?provider=` (takes precedence over the header) +- config `provider` / `routstrd start --provider ` + +All three are **strict pins**: the request is routed only to that provider. If +it answers with an error (including a 400/422 upstream rejection), the request +is **not** failed over to another node — the error is returned to the client. +Retries against the same provider (top-up, mint fallback) still happen. Without +a pin, the router falls over to the next-cheapest eligible provider as usual. + ## Configuration Config file: `~/.routstrd/config.json` @@ -487,7 +501,7 @@ Config file: `~/.routstrd/config.json` |-------|------|---------|-------------| | `port` | number | 8008 | Daemon HTTP port | | `host` | string | `"127.0.0.1"` | Bind address | -| `provider` | string\|null | null | Default provider URL | +| `provider` | string\|null | null | Pinned provider URL; all requests go only to this node (no cross-provider failover) | | `mode` | string | `"apikeys"` | Client mode (`apikeys` or `xcashu`) | | `maxTokens` | number | 64000 | Completion budget applied when a client sets no output-token limit | | `daemonUrl` | string | — | Remote daemon URL (set by `routstrd remote`) |