Skip to content

Commit ee34f67

Browse files
author
gluck
committed
docs: providers.md, routing a model to a transport
One Tier-2 page owns the routed provider: transports, route order, the providers/<name>.toml format, the three presets, translation channels, and how to check a route. architecture.md's Copilot Responses section becomes the Responses dialect section and links to it. SKILL.md gains one row.
1 parent 9ab1e85 commit ee34f67

3 files changed

Lines changed: 81 additions & 8 deletions

File tree

‎skills/figaro/SKILL.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ about changing figaro rather than using it live under
9494
| [reference/turns.md](reference/turns.md) | Turn ids, the turn-shaped read wire, pagination. |
9595
| [reference/arias.md](reference/arias.md) | Reading an aria off disk, and the store layout. |
9696
| [reference/architecture.md](reference/architecture.md) | The three roles, the IR, the form, the RPC wire, the provider layer. |
97+
| [reference/providers.md](reference/providers.md) | Writing `providers/<name>.toml`, pointing a gateway at a new endpoint, or a model failing on one provider and working on another. |
9798
| [reference/ui-stream.md](reference/ui-stream.md) | How a conversation reaches a terminal: the read wire, inline freeze, the pager. |
9899
| [reference/quoting.md](reference/quoting.md) | Quoting a passage into a prompt (`<412.0:23-1180>!`), visual selection, and the pager's `:send` / `:fork` / `:listen` / `:attend`. |
99100
| [reference/cache-control.md](reference/cache-control.md) | Prompt caching, and overriding it. |

‎skills/figaro/reference/architecture.md‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ Translates IR ↔ Anthropic wire and caches the per-aria wire bytes
369369
thinking blocks rather than emit unsigned ones.
370370
- **A stream that closed cleanly is not yet a reply.** Both Anthropic-shaped
371371
providers (this one and the hand-rolled `internal/provider/anthropic`, which
372-
the copilot Messages route rides) run the drained message through
372+
the routed `messages` transport rides) run the drained message through
373373
`provider.CheckCompletion` before it lands. Two faults: no content blocks at
374374
all, whatever the stop reason; or a stop reason that is not a way of
375375
finishing. `provider.AnthropicStopReason` is the one table for the wire's
@@ -444,14 +444,16 @@ Translates IR ↔ Anthropic wire and caches the per-aria wire bytes
444444
flags. `anthropic-beta` does not need `interleaved-thinking` for adaptive
445445
models.
446446

447-
### Copilot Responses: per-request streams, local history
447+
### Responses dialect: `internal/provider/responses`
448448

449-
`internal/provider/copilot` routes by the Copilot model catalog's advertised
450-
endpoints. The Responses route sends the locally reconstructed conversation on
451-
one WebSocket per request. There is no remote continuation dependency,
452-
`previous_response_id`, or durable server-side session.
449+
Which provider and transport a model reaches is decided in
450+
`internal/provider/routed`; see [providers.md](providers.md). The Responses
451+
dialect sends the locally reconstructed conversation as one request per turn,
452+
over a WebSocket or an HTTP SSE stream as the route chose. There is no remote
453+
continuation dependency, `previous_response_id`, or durable server-side
454+
session.
453455

454-
- `responses_stream.go` forwards argument deltas immediately and correlates
456+
- `stream.go` forwards argument deltas immediately and correlates
455457
calls by call ID, announced item ID, then an **explicit** output index.
456458
Copilot may re-encrypt item IDs between events; an absent index must not
457459
accidentally match tool zero. Complete calls can execute while other output
@@ -474,7 +476,8 @@ one WebSocket per request. There is no remote continuation dependency,
474476
before credential resolution. Astra rejects sampling controls and supports
475477
`low`, `medium`, `high`, `xhigh`, `max` reasoning effort. Unknown model families
476478
remain unconstrained; settings are never silently discarded.
477-
- `wirelog.BeginStream` makes WebSocket attempts visible in `doctor provider`,
479+
- `wirelog.BeginStream` makes both transports visible in `doctor provider`
480+
(method `WS` or `SSE`),
478481
including while in flight. It records timing, byte/event counts, argument
479482
deltas, unmatched deltas and terminal status, not headers, argument text,
480483
opaque identifiers or free-form provider errors. An unknown event increments
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Providers: routing a model to a transport
2+
3+
`copilot`, `gateway` and `openrouter` are one provider, `internal/provider/routed`,
4+
given three different specs. A spec names a host, the headers to send, and how
5+
to choose a transport for each model. `anthropic` (native OAuth) is separate.
6+
7+
## Transports
8+
9+
| Name | Wire | Endpoint |
10+
|---|---|---|
11+
| `messages` | Anthropic Messages | `base_url` + `messages_path` (default `/v1/messages`) |
12+
| `chat` | OpenAI Chat Completions | `base_url` + `/chat/completions` |
13+
| `responses` | OpenAI Responses, HTTP SSE | `base_url` + `/responses` |
14+
| `responses-ws` | OpenAI Responses, WebSocket | `ws(s)://` `base_url` + `/responses` |
15+
16+
## Route order
17+
18+
1. The first `[[routing.rule]]` whose `model` glob matches.
19+
2. The catalog: `GET base_url + routing.catalog`, then the model's
20+
`supported_endpoints`. Preference: `responses-ws` > `responses` > `messages` >
21+
`chat`. A `ws:` prefix marks a socket.
22+
3. `routing.default`.
23+
4. Otherwise the send is refused, and the error names all three.
24+
25+
## The file: `providers/<name>.toml`
26+
27+
```toml
28+
base_url = "http://localhost:4000/v1"
29+
api_key = "..." # or the provider's env var
30+
cache_markers = "trusted" # chat only; see cache-control.md
31+
messages_path = "/messages" # when base_url already ends in /v1
32+
headers = { "X-Team" = "figaro" } # every request, every transport
33+
34+
[transport_headers.responses] # one transport only
35+
"X-Experiment" = "1"
36+
37+
[routing]
38+
catalog = "/models" # optional
39+
default = "chat"
40+
41+
[[routing.rule]] # file rules run before the preset's
42+
model = "grok-*"
43+
transport = "responses"
44+
```
45+
46+
The provider fails to build, rather than guess, on an unknown transport, a rule
47+
with no transport, a bad glob, or a spec with no way to route at all.
48+
49+
## Presets
50+
51+
| Provider | Built in | The file adds |
52+
|---|---|---|
53+
| `copilot` | catalog `/models`; rule `claude-*` → `messages`; Copilot and identity headers; host from the hush token | rules, headers. `base_url` stays Copilot's own override (`token_mode`, `enterprise_domain`). |
54+
| `gateway` | default `chat`; credential optional | everything. `base_url` is required; `FIGARO_GATEWAY_BASE_URL` wins over the file. |
55+
| `openrouter` | default `chat`; OpenRouter host; `anthropic/` model prefix | everything. |
56+
57+
## Translation channels
58+
59+
`chat` writes `<name>`, `messages` writes `<name>-messages`, and both Responses
60+
transports write `<name>-responses`. The names predate routing, so existing arias
61+
keep their caches. Entries are translated as they land only when routing is
62+
static (a default, no catalog, no rules); otherwise they are translated at send
63+
time, once the model's transport is known.
64+
65+
## Checking a route
66+
67+
`figaro doctor provider --id <id> -j` files Responses streams as `WS` or `SSE`
68+
and HTTP round-trips by path. `figaro models` lists catalog models that have a
69+
route.

0 commit comments

Comments
 (0)