@flame0510/project-aether 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +2 -1
  2. package/app/agents/ModelSection.tsx +313 -0
  3. package/app/agents/PageClient.tsx +83 -4
  4. package/app/agents/create/page.tsx +8 -21
  5. package/app/api/agents/[id]/model/route.ts +113 -0
  6. package/app/api/agents/[id]/recreate/route.ts +10 -34
  7. package/app/api/agents/[id]/route.ts +10 -29
  8. package/app/api/agents/create/route.ts +59 -57
  9. package/app/api/agents/models-summary/route.ts +163 -0
  10. package/app/api/assistant/route.ts +36 -15
  11. package/app/api/gateway/agent/route.ts +23 -6
  12. package/app/api/gateway/provider/keys.ts +13 -1
  13. package/app/api/gateway/provider/route.ts +43 -12
  14. package/app/api/gateway/sync.ts +248 -72
  15. package/app/api/models/route.ts +28 -34
  16. package/app/api/provider/auth.ts +65 -0
  17. package/app/api/provider/upstream.ts +9 -2
  18. package/app/api/provider/v1/chat/completions/route.ts +22 -16
  19. package/app/api/provider/v1/models/route.ts +26 -133
  20. package/app/components/PulseChat.tsx +25 -39
  21. package/app/components/ui/RemoveButton.tsx +46 -0
  22. package/app/components/ui/Select.tsx +3 -2
  23. package/app/components/ui/index.ts +1 -0
  24. package/app/credentials/PageClient.tsx +2 -2
  25. package/app/gateway/PageClient.tsx +257 -673
  26. package/app/globals.css +8 -0
  27. package/app/lib/models-context.tsx +43 -7
  28. package/app/wizard/useWizard.ts +6 -1
  29. package/bin/rev4a.js +73 -9
  30. package/docs/ARCHITECTURE.md +16 -4
  31. package/docs/FRONTEND-ARCHITECTURE.md +24 -2
  32. package/docs/REV4A.md +40 -17
  33. package/docs/dev/API-REFERENCE.md +170 -79
  34. package/docs/dev/GATEWAY.md +231 -89
  35. package/docs/dev/PROVIDERS.md +26 -13
  36. package/docs/rag/DATA-FRESHNESS.md +57 -28
  37. package/docs/rag/GLOSSARY.md +16 -14
  38. package/docs/rag/REV4A-OVERVIEW.md +23 -24
  39. package/docs/rag/WHAT-I-CAN-ANSWER.md +5 -7
  40. package/instrumentation.ts +9 -1
  41. package/lib/agent-readiness.ts +110 -0
  42. package/lib/channelManager.ts +64 -22
  43. package/lib/container-file.ts +27 -0
  44. package/lib/model-catalogue.ts +140 -27
  45. package/lib/rev4a-paths.ts +0 -21
  46. package/model-pricing.json +118 -110
  47. package/models.config.json +27 -12
  48. package/package.json +1 -1
  49. package/app/api/gateway/route.ts +0 -191
@@ -1,9 +1,10 @@
1
1
  # Gateway Page
2
2
 
3
- > **Last updated:** 2026-07-31
3
+ > **Last updated:** 2026-09-14
4
4
 
5
- The Gateway page (`/gateway`) is the central control panel for managing provider
6
- configurations and agent model assignments across all Docker containers.
5
+ The Gateway page (`/gateway`) is the control panel for provider configuration:
6
+ API keys and the model catalogue offered to every agent container. It does not
7
+ assign models to agents.
7
8
 
8
9
  The Gateway aggregates multiple upstream AI providers (DeepSeek, OpenAI, Anthropic,
9
10
  Kimi, GLM, Qwen, and others) behind a single Rev4a provider. Agent containers see
@@ -15,17 +16,21 @@ at `/api/provider/v1/`.
15
16
  ## Overview
16
17
 
17
18
  ```
18
- Gateway Page
19
- ├── Provider Tab → Manage provider API keys, enable/disable models, push sync
20
- └── Agents Tab → Change primary model per agent container
19
+ Gateway Page → Manage provider API keys, enable/disable models, push sync
20
+ Agent detail → Change the primary model and fallbacks of one agent
21
21
  ```
22
22
 
23
+ The Gateway page is system configuration: which providers are wired up and which
24
+ models the fleet is offered. Assigning a model to one agent is that agent's
25
+ configuration, so it lives in the agent's detail panel (`app/agents/ModelSection.tsx`)
26
+ and not here.
27
+
23
28
  The Gateway talks to each agent container directly via `docker exec`, reading and
24
29
  writing to `/root/.openclaw/openclaw.json` inside the container.
25
30
 
26
31
  ---
27
32
 
28
- ## Provider Tab
33
+ ## Providers
29
34
 
30
35
  Lists all AI providers from `models.config.json` in the project root. Each provider
31
36
  row shows:
@@ -37,13 +42,25 @@ row shows:
37
42
 
38
43
  ### Provider Key Storage
39
44
 
45
+ > **Where these files actually live.** Both sit under the Rev4a data directory,
46
+ > `REV4A_DATA`, which defaults to `~/.config/rev4a/` and moves with
47
+ > `REV4A_DATA_DIR`. Neither is in the repository. They are nested differently and
48
+ > this catches people out, including while writing this document:
49
+ >
50
+ > | file | full path |
51
+ > |---|---|
52
+ > | provider keys | `REV4A_DATA/data/provider-keys.json` |
53
+ > | model overrides | `REV4A_DATA/model-overrides.json` |
54
+ >
55
+ > Paths written below as `data/provider-keys.json` mean the first row.
56
+
40
57
  Provider API keys are stored in `data/provider-keys.json`:
41
58
 
42
59
  ```json
43
60
  {
44
61
  "deepseek": "sk-...",
45
62
  "openrouter": "sk-...",
46
- "rev4a": "ciao"
63
+ "rev4a": "<gateway key>"
47
64
  }
48
65
  ```
49
66
 
@@ -52,19 +69,28 @@ against the Rev4a provider proxy using this token. See [`PROVIDERS.md`](PROVIDER
52
69
 
53
70
  ### Sync Flow
54
71
 
55
- When a provider key is saved or a model toggle is changed, the Gateway:
56
-
57
- 1. **Reads** `models.config.json` to get the full model catalogue
58
- 2. **Filters** enabled models for providers that have a configured API key
59
- 3. **Builds** a `models.providers.rev4a` config block (without `rev4a/` prefix
60
- on model IDs)
61
- 4. **Writes** the block into `/root/.openclaw/openclaw.json` on every agent container
62
- (containers with `AGENT_ID` Docker label)
63
- 5. **Cleans up** stale `models.json` and `auth-profiles.json` files inside each
64
- container
65
- 6. **Writes** the block into `/root/.openclaw/openclaw.json` on every agent container
66
- — **does NOT touch** `agents.defaults.model` or `agents.list[].model`
67
- 7. **Does NOT restart** the gateway — writes are live via file write
72
+ When a provider key is saved, a model toggle is changed, Sync All is pressed, or
73
+ Rev4a starts, the Gateway:
74
+
75
+ 1. **Reads** `models.config.json` and the overrides, and keeps the models that are
76
+ enabled and whose provider has a key. If the file cannot be read and no earlier
77
+ read succeeded, the sync stops here and reports `configError`
78
+ 2. **Builds** a `models.providers.rev4a` config block (without `rev4a/` prefix on
79
+ model IDs)
80
+ 3. **Lists** every container with the `AGENT_ID` Docker label, **running or
81
+ not**. Stopped agents are included so that starting one later does not bring
82
+ back an old catalogue
83
+ 4. **Reads** each container's `/root/.openclaw/openclaw.json` — through
84
+ `docker exec` when running, through `docker cp` otherwise (stopped, paused, created), since `exec`
85
+ cannot reach them. A read that fails, or returns an empty object, stops
86
+ there: **nothing is written** and the container is reported in `sync.failed`
87
+ 5. **Cleans up** stale `models.json` and `auth-profiles.json` — running containers
88
+ only, since it needs `exec`
89
+ 6. **Writes** the file back with only `models.providers.rev4a` replaced — **does NOT
90
+ touch** `agents.defaults.model` or `agents.list[].model`. Running containers get
91
+ it through `docker exec` and hot-apply it; the others through `docker cp`, with
92
+ the file's original mode, and apply it when started or resumed
93
+ 7. **Does NOT restart** the gateway
68
94
 
69
95
  ### Model ID Convention
70
96
 
@@ -80,7 +106,7 @@ Inside `models.providers.rev4a.models`, model IDs are stored **without** the
80
106
  "api": "openai-completions",
81
107
  "apiKey": "***",
82
108
  "models": [
83
- { "id": "deepseek/deepseek-v4-flash", "name": "DeepSeek V4 Flash" },
109
+ { "id": "deepseek/deepseek-flash", "name": "DeepSeek Flash" },
84
110
  { "id": "deepseek/deepseek-v4-pro", "name": "DeepSeek V4 Pro" }
85
111
  ]
86
112
  }
@@ -90,7 +116,7 @@ Inside `models.providers.rev4a.models`, model IDs are stored **without** the
90
116
  ```
91
117
 
92
118
  OpenClaw automatically prefixes model IDs with the provider name at runtime,
93
- producing `rev4a/deepseek/deepseek-v4-flash`. The `mode` field is **not** set —
119
+ producing `rev4a/deepseek/deepseek-flash`. The `mode` field is **not** set —
94
120
  OpenClaw defaults to merging config models with auto-discovered models.
95
121
 
96
122
  ### Model Catalogue
@@ -124,107 +150,113 @@ containers.
124
150
 
125
151
  ---
126
152
 
127
- ## Agents Tab
128
-
129
- Lists all agent containers discovered from Docker (containers with `AGENT_ID`
130
- label). Each agent row shows:
131
-
132
- - Agent name and container name
133
- - Current primary model
134
- - "Change Model" button
135
-
136
- ### Change Model Flow
137
-
138
- Clicking "Change Model" opens an inline form with:
139
-
140
- 1. **Primary model select** — dropdown of all enabled models (filtered to
141
- configured providers only)
142
-
143
- On save:
144
-
145
- 1. **PUT /api/gateway/agent** writes the model config to the container's
146
- `openclaw.json`:
147
- - `agents.defaults.model.primary` and `agents.list[0].model.primary` written
148
- with format `rev4a/<provider>/<model>`
149
- - No fallbacks are set unless explicitly provided
150
- 2. **No restart** — writes are live via `docker exec node -e`
151
-
152
- ### Model Select Filtering
153
-
154
- The model select in the Agents tab only shows models whose provider has a
155
- configured API key in `data/provider-keys.json`. This ensures users can only
156
- select models that are actually available.
153
+ ## Per-agent model assignment
154
+
155
+ Lives in the agent detail panel, not on this page. `app/agents/ModelSection.tsx`
156
+ reads `GET /api/agents/[id]/model` for the current primary and fallbacks, and the
157
+ enabled catalogue from `GET /api/models`, which returns only models whose provider
158
+ has a configured API key in `<data dir>/provider-keys.json`. Note this page does
159
+ **not** apply that filter — `GET /api/gateway/provider` returns the whole catalogue
160
+ so you can add a key and enable its models in one pass. The filter is applied by
161
+ `/api/models` and by the sync.
162
+
163
+ Saving calls `PUT /api/gateway/agent`, which writes the container's
164
+ `openclaw.json`:
165
+
166
+ - `agents.defaults.model`, and the entry in `agents.list` whose `id` is `main` —
167
+ found by id, not by position, and skipped entirely if no such entry exists. The
168
+ primary is stored as `rev4a/<provider>/<model>`
169
+ - `fallbacks` omitted and `fallbacks: []` are different requests: omitted leaves the
170
+ container's existing list untouched, `[]` clears it. The Model panel is the only
171
+ UI that sets them; the create wizard does not
172
+ - **no restart** — `agents` is in OpenClaw's hot-reload column, so the write
173
+ applies on the agent's next turn
174
+
175
+ The agent list shows each agent's current model as a chip, fed by
176
+ `GET /api/agents/models-summary`. The red states are split by remedy, because they
177
+ look alike and need different actions:
178
+
179
+ - `NOT IN CATALOGUE` — the id is gone from the catalogue: pick another model;
180
+ - `NOT ENABLED` — it exists but is not offered, and the proxy refuses it: pick another model;
181
+ - `OUT OF SYNC` — the gateway offers the agent's primary, but this container's synced
182
+ copy does not have it: run Sync All.
183
+
184
+ Amber `DEPRECATED` marks a working model on a retired name. All of these describe the
185
+ primary model; a neutral `NO FALLBACK` is the one chip about the fallback list, shown
186
+ when it is empty. Every agent gets them, stopped ones included (read from the volume
187
+ with `docker cp`). The agent's Model panel repeats the red and amber distinction in
188
+ its warning.
157
189
 
158
190
  ---
159
191
 
160
192
  ## API Endpoints
161
193
 
162
- ### `GET /api/gateway`
163
-
164
- Returns live Gateway status from all agent containers. Reads agent model configs
165
- via `openclaw models status --json` inside each container.
166
-
167
- **Response:**
168
- ```json
169
- {
170
- "agents": {
171
- "total": 1,
172
- "list": [
173
- {
174
- "containerName": "openclaw-atlas",
175
- "agentId": "atlas",
176
- "agentName": "Atlas",
177
- "defaultModel": "rev4a/deepseek/deepseek-v4-flash",
178
- "configured": true
179
- }
180
- ]
181
- }
182
- }
183
- ```
184
-
185
194
  ### `PUT /api/gateway/provider`
186
195
 
187
196
  Enables or disables a single model in the catalogue, then syncs every agent.
188
197
  Only writes `models.providers.rev4a` — does NOT touch model references.
189
198
 
190
- **Body:** `{ "modelId": "deepseek/deepseek-chat", "enabled": true }` — both
199
+ **Body:** `{ "modelId": "deepseek/deepseek-flash", "enabled": true }` — both
191
200
  required. Missing either returns `400`; an unknown `modelId` returns `404`.
201
+ If Docker cannot be reached the toggle is refused with `503` and nothing is saved.
192
202
 
193
- **Response:**
203
+ **Response:** `status` reports the toggle, saved before the sync runs; `sync`
204
+ reports whether every agent received it.
194
205
  ```json
195
206
  {
196
207
  "status": "ok",
197
- "modelId": "deepseek/deepseek-chat",
198
- "enabled": true,
208
+ "modelId": "deepseek/deepseek-flash",
209
+ "enabled": false,
199
210
  "provider": "deepseek",
200
- "sync": ["Active models: 2 across 1 agent(s)"]
211
+ "sync": { "ok": true, "activeModels": 2, "total": 2,
212
+ "synced": ["agent_2a3c3a07", "agent_9253eee3"], "stopped": [], "failed": [],
213
+ "summary": "Synced 2 of 2 agent(s)." }
201
214
  }
202
215
  ```
203
216
 
217
+ Two failures, handled differently on purpose:
218
+
219
+ - **Docker unreachable** — refused before anything is written: `503`, nothing saved,
220
+ the checkbox does not move, so the checkboxes never disagree with every agent.
221
+ - **Some containers fail once Docker answers** — the toggle stands and `sync.failed`
222
+ lists them; the page shows it in amber, and an agent whose primary is affected shows
223
+ `OUT OF SYNC`. Rolling
224
+ the override back would not restore consistency, because the containers that did
225
+ receive the change would keep it.
226
+
227
+ Full shape in
228
+ [API-REFERENCE.md](API-REFERENCE.md).
229
+
204
230
  ### `PUT /api/gateway/agent`
205
231
 
206
- Updates model config for a single agent container (primary model only).
232
+ Updates the primary model and fallbacks of a single agent container.
207
233
 
208
234
  **Body:**
209
235
  ```json
210
236
  {
211
237
  "containerName": "openclaw-atlas",
212
- "model": "deepseek/deepseek-v4-flash"
238
+ "model": "deepseek/deepseek-flash",
239
+ "fallbacks": ["deepseek/deepseek-v4-pro"]
213
240
  }
214
241
  ```
215
242
 
216
- **Response:**
243
+ Omit `fallbacks` to keep the container's current list; send `[]` to clear it.
244
+
245
+ **Response:** what was written, prefixed.
217
246
  ```json
218
247
  {
219
248
  "status": "ok",
220
249
  "agent": "openclaw-atlas",
221
250
  "model": {
222
- "primary": "rev4a/deepseek/deepseek-v4-flash"
251
+ "primary": "rev4a/deepseek/deepseek-flash",
252
+ "fallbacks": ["rev4a/deepseek/deepseek-v4-pro"]
223
253
  },
224
254
  "verify": { "ok": true, "bytes": 2058 }
225
255
  }
226
256
  ```
227
257
 
258
+ Full semantics in [API-REFERENCE.md](API-REFERENCE.md).
259
+
228
260
  ### `GET /api/gateway/provider`
229
261
 
230
262
  Returns current provider configuration state.
@@ -245,8 +277,8 @@ OpenRouter to find out.
245
277
  "baseUrl": "https://api.deepseek.com",
246
278
  "models": [
247
279
  {
248
- "id": "deepseek/deepseek-v4-flash",
249
- "name": "DeepSeek V4 Flash",
280
+ "id": "deepseek/deepseek-flash",
281
+ "name": "DeepSeek Flash",
250
282
  "enabled": true,
251
283
  "pricing": { "input": 0.05, "output": 0.10 },
252
284
  "pricingLive": true,
@@ -337,7 +369,117 @@ Restart the Rev4a server (via systemd).
337
369
  (tracked in `/root/.openclaw/.last-version`) — safe migrations only, no restart.
338
370
 
339
371
  6. **Model ref written after gateway readiness** — When the wizard creates an agent,
340
- the create route waits for gateway health check, then writes
372
+ the create route waits for the gateway to finish starting (`waitForGatewayReady`), then writes
341
373
  `agents.defaults.model.primary` + fallbacks and `gateway.controlUi.allowedOrigins`,
342
- then calls `syncAgent(name)`. The Gateway sync (`PUT /api/gateway/provider`)
374
+ then writes the provider block from `buildRev4aProviderConfig()` with `openclaw config patch --stdin`. The Gateway sync (`PUT /api/gateway/provider`)
343
375
  never touches model references — only `models.providers` is synced.
376
+
377
+ ---
378
+
379
+ ## Maintaining the model catalogue
380
+
381
+ Two files, both tracked in the repo, both maintained by hand:
382
+
383
+ | File | Holds |
384
+ |---|---|
385
+ | `models.config.json` | the catalogue: id, name, provider, `enabled`, `modality`, deprecation flags |
386
+ | `model-pricing.json` | price per 1M tokens, keyed by the same ids |
387
+
388
+ They drift, because upstream catalogues change without telling anyone. Refresh
389
+ them on a schedule that suits you, and always before shipping a release.
390
+
391
+ ### Where the truth lives
392
+
393
+ Do not copy figures from blog posts or search results — they go stale and
394
+ contradict each other. Two sources answer authoritatively:
395
+
396
+ ```bash
397
+ # OpenRouter: ids, prices and input modalities for every model, no auth
398
+ curl -s https://openrouter.ai/api/v1/models
399
+
400
+ # DeepSeek: the definitive list of callable model names
401
+ curl -s https://api.deepseek.com/models -H "Authorization: Bearer $DEEPSEEK_KEY"
402
+ ```
403
+
404
+ The OpenRouter payload carries `pricing.prompt`, `pricing.completion`,
405
+ `pricing.input_cache_read` (multiply by 1e6 for per-million figures) and
406
+ `architecture.input_modalities`, so prices and modalities can be checked in the
407
+ same pass.
408
+
409
+ For direct-provider prices, use the provider's own pricing page. DeepSeek's is
410
+ at `api-docs.deepseek.com/quick_start/pricing`, and note it charges **peak and
411
+ off-peak rates** (peak: 01:00–04:00 and 06:00–10:00 UTC, Mon–Fri; off-peak is
412
+ half). `model-pricing.json` holds one figure per model and cannot express that,
413
+ nor cache-hit rates — **record the peak rate**, so the dashboard overstates
414
+ spend rather than understating it.
415
+
416
+ ### What to check, in order
417
+
418
+ 1. **Models that no longer exist upstream.** Set `enabled: false`; do not delete
419
+ an id an agent might still be configured with — see below.
420
+ 2. **Prices that drifted.** Compare every `openrouter/*` entry against the live
421
+ API; direct providers against their pricing page.
422
+ 3. **Modalities.** `architecture.input_modalities` is authoritative. Compare the
423
+ *set*, not the string: `text+image` and `image+text` are the same thing, and
424
+ rewriting one into the other produces a diff full of noise.
425
+ 4. **New models worth carrying.** Add with `enabled` reflecting policy, not
426
+ availability — see the DeepSeek rule below.
427
+ 5. **Retired names.** Mark them, do not remove them yet.
428
+
429
+ ### The deprecation lifecycle
430
+
431
+ Upstream sometimes retires a model *name* while keeping it working, redirected to
432
+ a successor and billed at the successor's rates.
433
+
434
+ Handle it in two releases:
435
+
436
+ ```json5
437
+ // release N — flag it. It still works, and it stays selectable.
438
+ { "id": "provider/retired-model", "deprecated": true }
439
+
440
+ // release N+1 — remove it. Agents still on it show NOT IN CATALOGUE, in red.
441
+ ```
442
+
443
+ The gap between the two releases is what gives anyone using it a chance to move.
444
+ Removing in one step turns a warning into an outage.
445
+
446
+ `deprecated` surfaces in two places: a `DEPRECATED` chip on the agent card, and a
447
+ `(deprecated)` tag on the option in the primary and fallback pickers of the
448
+ agent's Model panel. The tag rides on the option label, so a native select keeps
449
+ showing it once the model is chosen.
450
+
451
+ There is no field for the successor: the redirect is the provider's, and nothing here
452
+ acts on it. Where the successor matters, say so in prose.
453
+
454
+ Do **not** encode deprecation in `name`. The name is the model's name; the flag
455
+ renders itself, and putting it in both produces `DeepSeek V4 Flash (legacy →
456
+ Flash) (deprecated)`.
457
+
458
+ **Never remove an id in the same release you deprecate it**, and never remove one
459
+ without checking who uses it: an agent whose primary model leaves the catalogue
460
+ fails on its next turn, and with an empty fallback list it fails hard. The agent
461
+ cards surface this — `NOT IN CATALOGUE` in red — but only after the fact.
462
+
463
+ ### Policy vs availability
464
+
465
+ `enabled` expresses **what this deployment should offer**, not what exists. Both
466
+ DeepSeek direct and OpenRouter carry the DeepSeek models; this deployment uses
467
+ the direct provider, so the OpenRouter copies ship disabled.
468
+
469
+ User toggles live separately in `<data dir>/model-overrides.json` and win over
470
+ `enabled`. When a default changes to match an existing override,
471
+ `toggleModelOverride` drops the now-redundant key on the next interaction, so the
472
+ overrides file stays limited to genuine divergence.
473
+
474
+ ### After editing
475
+
476
+ - both files must stay valid JSON, and every `enabled` model needs a price entry
477
+ - remove pricing rows for ids you removed from the catalogue — orphans accumulate
478
+ - read the catalogue only through `lib/model-catalogue.ts`: `loadModelsConfig()` for
479
+ the catalogue with overrides, `loadOfferedModels()` for what the deployment offers,
480
+ `isModelOffered()` to decide whether to serve a request. Nothing else in the app
481
+ reads `models.config.json`; `scripts/refresh-model-pricing.mjs` rewrites it offline,
482
+ and the backup and restore scripts copy it
483
+ - if the file cannot be read, the last copy this process read successfully is used;
484
+ with no earlier copy the sync refuses to push. Orphan overrides are pruned only
485
+ when a toggle is saved against a freshly read catalogue
@@ -1,6 +1,6 @@
1
1
  # Provider Gateway & Key Management
2
2
 
3
- > **Last updated:** 2026-08-28
3
+ > **Last updated:** 2026-09-14
4
4
 
5
5
  This document covers the Rev4a Provider Gateway (proxy), the provider key
6
6
  endpoints, and how agent containers authenticate against the Rev4a proxy.
@@ -19,7 +19,7 @@ Agent Container Rev4a Gateway Upstream API
19
19
  │ │ │
20
20
  │ Authorization: Bearer <rev4a-key> │
21
21
  │ POST /api/provider/v1/chat/completions │
22
- │ model: rev4a/deepseek-deepseek-v4-flash │
22
+ │ model: rev4a/deepseek/deepseek-flash │
23
23
  │────────────────────────>│ │
24
24
  │ │ POST https://api.deepseek.com/v1/chat/completions
25
25
  │ │ Authorization: Bearer <deepseek-key>
@@ -67,7 +67,7 @@ not by auto-discovery.
67
67
  "total": 9,
68
68
  "data": [
69
69
  {
70
- "id": "deepseek/deepseek-v4-flash",
70
+ "id": "deepseek/deepseek-flash",
71
71
  "object": "model",
72
72
  "created": 1700000000,
73
73
  "owned_by": "deepseek"
@@ -95,11 +95,15 @@ name is parsed to extract the provider and model ID.
95
95
 
96
96
  | Model alias | Upstream provider | Upstream model |
97
97
  |---|---|---|
98
- | `rev4a/deepseek-v4-flash` | `deepseek` | `deepseek-v4-flash` |
98
+ | `rev4a/deepseek-flash` | `deepseek` | `deepseek-flash` |
99
99
  | `rev4a/deepseek-v4-pro` | `deepseek` | `deepseek-v4-pro` |
100
- | `rev4a/kimi-k3` | `kimi` | `kimi-k3` |
101
- | `rev4a/glm-5.2` | `glm` | `glm-5.2` |
102
- | `rev4a/qwen3.7-plus` | `qwen` | `qwen3.7-plus` |
100
+
101
+ This map is a short list of two-segment shortcuts, not the general mechanism.
102
+ Every other model resolves through step 3 below, from its three-segment id:
103
+ `rev4a/kimi/kimi-k3` resolves to provider `kimi`, model `kimi-k3`. A two-segment
104
+ `rev4a/kimi-k3` is **not** an alias and does not resolve — `rev4a` is not a
105
+ provider key, so it falls through to the `deepseek` default with an unusable
106
+ model string.
103
107
 
104
108
  **Model parsing fallback:**
105
109
  1. Check `request.provider` field
@@ -114,7 +118,7 @@ The upstream module (`app/api/provider/upstream.ts`) handles this via a per-prov
114
118
  **Request body** (OpenAI-compatible):
115
119
  ```json
116
120
  {
117
- "model": "rev4a/deepseek-v4-flash",
121
+ "model": "rev4a/deepseek-flash",
118
122
  "messages": [{"role": "user", "content": "Hello"}]
119
123
  }
120
124
  ```
@@ -145,8 +149,13 @@ Store a provider API key, or re-run the sync without changing keys.
145
149
  **Body:** `{ "provider": "deepseek", "apiKey": "sk-..." }`, or `{ "_syncOnly": true }`
146
150
  to push the current configuration to every agent container without touching any key.
147
151
 
148
- **Response:** `{ "status": "ok", ... }`. Unknown provider names are rejected with
149
- `400` and the list of known providers.
152
+ **Response:** `{ "status": "ok", "provider": "...", "configured": true, "sync": { ... } }`.
153
+ The key is saved before the sync runs, so `status` reports the save and `sync`
154
+ reports whether every agent received it; its shape is documented under
155
+ `PUT /api/gateway/provider` in [API-REFERENCE.md](API-REFERENCE.md). With
156
+ `_syncOnly` the sync *is* the operation: a failed or partial sync returns `502` with
157
+ `status: "error"` and `error` set to the summary. Unknown provider names are
158
+ rejected with `400` and the list of known providers.
150
159
 
151
160
  **Auth:** browser cookie or bearer token
152
161
 
@@ -154,9 +163,13 @@ to push the current configuration to every agent container without touching any
154
163
 
155
164
  Enable or disable a single model in the catalogue.
156
165
 
157
- **Body:** `{ "modelId": "deepseek/deepseek-chat", "enabled": true }` — both fields
166
+ **Body:** `{ "modelId": "deepseek/deepseek-flash", "enabled": true }` — both fields
158
167
  required. Unknown `modelId` returns `404`.
159
168
 
169
+ **Response:** as for the key save — `status` for the toggle, `sync` for whether
170
+ the agents received it. If Docker cannot be reached the toggle is refused with `503` and
171
+ nothing is saved; a key save in the same situation is saved and the failure reported.
172
+
160
173
  **Auth:** browser cookie or bearer token
161
174
 
162
175
  There is no `DELETE`: a provider key is cleared by storing an empty one.
@@ -177,7 +190,7 @@ Inside each agent container, the `openclaw.json` file has this structure for the
177
190
  "api": "openai-completions",
178
191
  "apiKey": "<gateway-token>",
179
192
  "models": [
180
- { "id": "deepseek/deepseek-v4-flash", "name": "DeepSeek V4 Flash" },
193
+ { "id": "deepseek/deepseek-flash", "name": "DeepSeek Flash" },
181
194
  { "id": "deepseek/deepseek-v4-pro", "name": "DeepSeek V4 Pro" }
182
195
  ]
183
196
  }
@@ -186,7 +199,7 @@ Inside each agent container, the `openclaw.json` file has this structure for the
186
199
  "agents": {
187
200
  "defaults": {
188
201
  "model": {
189
- "primary": "rev4a/deepseek/deepseek-v4-flash"
202
+ "primary": "rev4a/deepseek/deepseek-flash"
190
203
  }
191
204
  }
192
205
  }