@fanchaozz/provider-manager 1.0.0 → 1.1.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.
- package/README.md +267 -257
- package/README_EN.md +249 -249
- package/commands.ts +3 -17
- package/components.ts +1193 -866
- package/forms.ts +19 -1
- package/package.json +1 -1
- package/ui.ts +1245 -761
package/README_EN.md
CHANGED
|
@@ -1,249 +1,249 @@
|
|
|
1
|
-
# provider-manager
|
|
2
|
-
|
|
3
|
-
[English](./README_EN.md) | [简体中文](./README.md)
|
|
4
|
-
|
|
5
|
-
A pi extension that manages custom providers and models in `~/.pi/agent/models.json` through a TUI dashboard, a `/providers` slash command, and a remote model sync.
|
|
6
|
-
|
|
7
|
-
> **Scope**: only `models.json` is covered — the extension does **not** manage built-in providers, does **not** switch models, and does **not** provide a login UI. Use pi's built-in `/model` and provider auth flow for those.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Install
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
pi install npm:@fanchaozz/provider-manager
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
The extension depends on `@earendil-works/pi-coding-agent` (shipped with pi). jiti walks up `node_modules`, so **no `npm install` is needed** inside the extension directory.
|
|
18
|
-
|
|
19
|
-
After install, `~/.pi/agent/provider-manager.json` is auto-created on first load (see [User config](#user-config--provider-managerjson)). Delete it to fall back to the code default.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Recent changes
|
|
24
|
-
|
|
25
|
-
Version history in [CHANGELOG.md](./CHANGELOG.md).
|
|
26
|
-
|
|
27
|
-
## Quick start
|
|
28
|
-
|
|
29
|
-
| Want to… | Do this |
|
|
30
|
-
|---|---|
|
|
31
|
-
| Open the dashboard | `/providers` |
|
|
32
|
-
| List providers + their models | `/providers ls` (filter: `/providers ls kdapi`) |
|
|
33
|
-
| Add a provider | Dashboard, `n` on the Providers pane, or `/providers add [<id>]` |
|
|
34
|
-
| Add a model | Dashboard, switch to Models pane with `Tab`, `n`, or `/providers model <pid> add` |
|
|
35
|
-
| Edit provider / model | Dashboard, `e` |
|
|
36
|
-
| Delete | Dashboard, `d` (confirm dialog) |
|
|
37
|
-
| Pull new model list from a provider's API | Dashboard, `y`, or `/providers sync [<pid>]` |
|
|
38
|
-
| Probe auth + reachability + a 1-token test call | Dashboard, `t` (current model) or `T` (all in provider) |
|
|
39
|
-
| Restore last `.bak` | `/providers reset` |
|
|
40
|
-
| Print command help | `/providers help` |
|
|
41
|
-
| Close dashboard | `q` or `Esc` |
|
|
42
|
-
|
|
43
|
-
The sync command is the fastest way to populate a fresh provider: it fetches the remote model list, shows a checklist, and writes back the ones you select.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Dashboard
|
|
48
|
-
|
|
49
|
-
`/providers` opens a two-pane TUI:
|
|
50
|
-
|
|
51
|
-
- **Left pane** — providers (id + model count)
|
|
52
|
-
- **Right pane** — models of the selected provider (id + `[R]` reasoning / `[I]` image flags + ctx / max)
|
|
53
|
-
- **Detail strip** — raw JSON of the selected row
|
|
54
|
-
- **Footer** — current key bindings
|
|
55
|
-
|
|
56
|
-
### Key bindings
|
|
57
|
-
|
|
58
|
-
| Key | Action |
|
|
59
|
-
|---|---|
|
|
60
|
-
| `↑↓` / `j k` | Navigate in current pane |
|
|
61
|
-
| `g` / `G` | Jump to top / bottom |
|
|
62
|
-
| `Tab` | Switch between Providers ↔ Models pane |
|
|
63
|
-
| `n` | New: add provider on Providers pane, add model on Models pane |
|
|
64
|
-
| `e` | Edit selected provider / model |
|
|
65
|
-
| `d` | Delete (with confirm dialog) |
|
|
66
|
-
| `y` | Sync (fetch remote model list for selected provider) |
|
|
67
|
-
| `t` / `T` | Probe current model / all models in selected provider |
|
|
68
|
-
| `?` | Toggle help overlay |
|
|
69
|
-
| `q` / `Esc` | Close dashboard |
|
|
70
|
-
|
|
71
|
-
When the provider list is empty, pressing `n` on the Models pane switches to the Providers pane and tells you to create one first.
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## Sync flow
|
|
76
|
-
|
|
77
|
-
`sync` is the **only** way to add a batch of models. It fetches the remote model list for the selected provider and shows a checklist.
|
|
78
|
-
|
|
79
|
-
The checklist **shows every model for that provider — both existing and remote new**:
|
|
80
|
-
|
|
81
|
-
- Existing models are labelled `<id> (existing)`, default checked. Uncheck to delete.
|
|
82
|
-
- Remote new models are labelled `<id>` only, default unchecked. Check to add.
|
|
83
|
-
|
|
84
|
-
Press `Enter` to write the result, `Esc` to cancel. On save, the final `models.json` is the union of (checked existing) + (checked new); remote new is preferred over local (so you pick up the fresh `reasoning` / `input` / `ctx` / `maxTokens` / `thinkingLevelMap`).
|
|
85
|
-
|
|
86
|
-
**The checklist mirrors pi's `/models` selector**, so 100+ models stay manageable:
|
|
87
|
-
|
|
88
|
-
- **Always-visible search input** at the top with a `> ` prompt. Every printable character (a / i / g / G, etc.) is appended to the search query — no shortcut clashes.
|
|
89
|
-
- **Viewport scrolling**: 8 rows by default. `⋮ N more below` / `⋮ N hidden` mark scroll boundaries.
|
|
90
|
-
- **Pinned first item**: when the cursor scrolls past the first page, the first item stays at the top with a `(top)` label.
|
|
91
|
-
- **Wrap-around navigation**: `↑` / `↓` cycle at the top / bottom; `j` / `k` are aliases.
|
|
92
|
-
- **Space toggles** the current row's selection, also inside a filter.
|
|
93
|
-
- **`selected / total selected` summary** at the top.
|
|
94
|
-
- **`(current/total)` position indicator** at the bottom (filter context when active).
|
|
95
|
-
|
|
96
|
-
Viewport size is overridable via `~/.pi/agent/provider-manager.json#syncViewportSize` (5–200, falls back to default 8 if out of range).
|
|
97
|
-
|
|
98
|
-
**Newly added models' fields come from `~/.pi/agent/provider-manager.json`'s `defaultModel` block** (not the code default) — this is the `loadDefaultModelConfig()` behavior. Edit that file before sync to customise the model template.
|
|
99
|
-
|
|
100
|
-
**proxy field**: a provider's edit form accepts a `proxy` URL (e.g. `http://127.0.0.1:7890`). On sync it's set as `HTTPS_PROXY` / `HTTP_PROXY` env vars and restored after the request. Other concurrent fetches in the same process will see the same proxy while it is set (env is process-global; sync runs at most one fetch at a time).
|
|
101
|
-
|
|
102
|
-
**apiKey character constraint**: sync rejects apiKeys with any character whose code > 255 (`•`, CJK, emoji, etc., typical from copy-paste). The error is actionable: `apiKey contains non-Latin-1 character at position 7 (U+2022). Re-enter the key in the provider form.`
|
|
103
|
-
|
|
104
|
-
**Noise filter**: embedding / tts / image-gen models are filtered by default (`embed*`, `tts`, `whisper`, `dall-e`, `clip`, `moderation`, `image-*`).
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## User config — `provider-manager.json`
|
|
109
|
-
|
|
110
|
-
`~/.pi/agent/provider-manager.json` controls the defaults used when:
|
|
111
|
-
- you answer "yes" to "Use default config?" in the new-model form
|
|
112
|
-
- you sync new models from a remote API
|
|
113
|
-
|
|
114
|
-
Auto-created on first launch. Delete to revert to code defaults.
|
|
115
|
-
|
|
116
|
-
### Schema
|
|
117
|
-
|
|
118
|
-
```jsonc
|
|
119
|
-
{
|
|
120
|
-
"_defaultModel": "free-form comment, ignored at runtime",
|
|
121
|
-
"defaultModel": {
|
|
122
|
-
"reasoning": true,
|
|
123
|
-
"input": ["text", "image"],
|
|
124
|
-
"contextWindow": 128000,
|
|
125
|
-
"maxTokens": 16384,
|
|
126
|
-
"thinkingLevelMap": {
|
|
127
|
-
"off": null,
|
|
128
|
-
"minimal": null,
|
|
129
|
-
"low": null,
|
|
130
|
-
"medium": "medium",
|
|
131
|
-
"high": null,
|
|
132
|
-
"xhigh": null,
|
|
133
|
-
"max": null
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### Field rules
|
|
140
|
-
|
|
141
|
-
- **`reasoning`** — boolean. If `true`, the model supports extended thinking and `thinkingLevelMap` applies.
|
|
142
|
-
- **`input`** — non-empty array of `"text"` and/or `"image"`. `"text" | "image"` means the model accepts that modality.
|
|
143
|
-
- **`contextWindow`** / **`maxTokens`** — positive integers (tokens).
|
|
144
|
-
- **`thinkingLevelMap`** — object. Any subset of the 7 keys (`off` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max`). A `string` value (e.g. `"medium"`) means that thinking level is enabled and the string is sent to the provider; `null` means disabled. Missing keys are treated as `null`.
|
|
145
|
-
|
|
146
|
-
If the file is missing, malformed JSON, or fails any check, the extension silently falls back to the built-in defaults shown above.
|
|
147
|
-
|
|
148
|
-
### Why `medium` is highlighted by default
|
|
149
|
-
|
|
150
|
-
When a synced model has `reasoning: true` and `thinkingLevelMap.medium = "medium"`, pi's Shift+Tab thinking-level cycle lands on `medium` by default. Pick whichever level your upstream actually supports — `null` is fine for providers with no extended-thinking knob.
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## Form editor (new model / edit model / new provider)
|
|
155
|
-
|
|
156
|
-
`addProviderFlow` / `editProviderFlow` / `addModelFlow` / `editModelFlow` / `deleteProviderFlow` / `deleteModelFlow` all share one TUI form (`components.ts:FormEditor`).
|
|
157
|
-
|
|
158
|
-
### Field types
|
|
159
|
-
|
|
160
|
-
| Type | Behavior |
|
|
161
|
-
|---|---|
|
|
162
|
-
| `text` | Free text input |
|
|
163
|
-
| `secret` | Free text, rendered masked in the TUI |
|
|
164
|
-
| `number` | Free numeric input, validated on commit |
|
|
165
|
-
| `select` | Options list; press `e` to enter edit mode, `Space` to pick, `↑↓`/`jk` to navigate, `Enter` to commit |
|
|
166
|
-
| `multiselect` | Like `select` but multiple values; `Space` toggles each |
|
|
167
|
-
| `levelmap` | 7 rows (`off` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max`); `Space` toggles each; commit normalizes missing keys to `null` |
|
|
168
|
-
| `readonly` | Display only, cannot edit |
|
|
169
|
-
|
|
170
|
-
### Key bindings
|
|
171
|
-
|
|
172
|
-
| Key | Behavior |
|
|
173
|
-
|---|---|
|
|
174
|
-
| `e` / `E` | Enter edit mode (only on `select` / `levelmap` / `multiselect`) |
|
|
175
|
-
| `Esc` / `q` | If editing: exit edit (commit current value). Otherwise: cancel the form |
|
|
176
|
-
| `s` | Save the whole form. Only on non-typeable fields — `s` is a literal char inside `text` / `secret` / `number` / `json` |
|
|
177
|
-
| `Enter` | If editing on a non-input field: commit and exit edit. On a typeable field: commit + move to next field. On a readonly field: save the form |
|
|
178
|
-
| `Space` | If editing on a non-input field: toggle current option |
|
|
179
|
-
| `↑↓` / `j k` | Navigate fields; inside edit mode of a non-input field: navigate options |
|
|
180
|
-
| `Backspace` | Delete last char (typeable fields) |
|
|
181
|
-
|
|
182
|
-
### New-model flow: "Use default config?"
|
|
183
|
-
|
|
184
|
-
`addModelFlow` asks once after the name:
|
|
185
|
-
|
|
186
|
-
- **Yes** — apply `DEFAULT_MODEL_CONFIG` (see [User config](#user-config--provider-managerjson)) and skip the remaining questions
|
|
187
|
-
- **No** — ask reasoning / input / ctx / max / thinking-level-map one by one
|
|
188
|
-
|
|
189
|
-
`Esc` at any prompt cancels the whole flow (the dashboard is restored automatically).
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## Test a model (`t` / `T`)
|
|
194
|
-
|
|
195
|
-
`t` probes the current model; `T` probes all models in the current provider. Three checks per model:
|
|
196
|
-
|
|
197
|
-
| Check | What it does | Cost |
|
|
198
|
-
|---|---|---|
|
|
199
|
-
| `auth` | Looks up `~/.pi/agent/auth.json` (or env) for the provider's API key | free |
|
|
200
|
-
| `reachable` | `GET {baseUrl}/models` with a 10 s timeout | free |
|
|
201
|
-
| `generated` | Sends a 4-token prompt (`"Reply with the single word: ok"`) and checks `stopReason ∈ {stop, length}` | ~4 tokens |
|
|
202
|
-
|
|
203
|
-
Hard caps: `maxTokens` is clamped to 16, timeout 10 s (override with `PI_PROVIDER_TEST_TIMEOUT` env var in seconds). Result is cached in-process until you restart pi.
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## File layout
|
|
208
|
-
|
|
209
|
-
```
|
|
210
|
-
~/.pi/agent/
|
|
211
|
-
├── models.json ← the file this extension edits
|
|
212
|
-
├── models.json.bak ← automatic backup taken before every write
|
|
213
|
-
└── extensions/
|
|
214
|
-
└── provider-manager/ ← this extension (installed via pi install / git clone)
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
The extension does not touch anything outside `models.json` and `models.json.bak`. To roll back, restore from `.bak` with `/providers reset` or manually:
|
|
218
|
-
|
|
219
|
-
```bash
|
|
220
|
-
cp ~/.pi/agent/models.json.bak ~/.pi/agent/models.json
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Troubleshooting
|
|
226
|
-
|
|
227
|
-
**`pi install` fails with `E404` or "no such package".** The npm registry doesn't have this package. First run `npm view @fanchaozz/provider-manager` to confirm the publish succeeded; if it has, check `npm config get registry` is not `npm.pkg.github.com`.
|
|
228
|
-
|
|
229
|
-
**Dashboard opens but is empty.** Your `models.json` has no custom providers. The extension only manages `models.json` — built-in pi providers (anthropic / openai / google / …) are not shown. Use pi's built-in `/model` for those.
|
|
230
|
-
|
|
231
|
-
**Sync errors with `ECONNREFUSED` / `ENOTFOUND`.** The selected provider's `baseUrl` is unreachable. Edit it with `/providers edit <pid>` (or dashboard `e`).
|
|
232
|
-
|
|
233
|
-
**Sync errors with `HTTP 500` / `HTTP 401`.** Wrong `baseUrl` or missing / wrong `apiKey`. Verify in the provider edit form.
|
|
234
|
-
|
|
235
|
-
**A thinking level I set keeps disappearing.** pi may not support that level on the underlying model — try a different level, or set it to `null` to disable.
|
|
236
|
-
|
|
237
|
-
**All my customizations in `provider-manager.json` are ignored.** The file is malformed or fails validation (see [Schema](#schema)). The extension falls back to defaults silently. Validate with `node -e "JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pi/agent/provider-manager.json','utf8'))"`.
|
|
238
|
-
|
|
239
|
-
**Models added by sync show wrong fields (`ctx=128000` regardless).** The model is using defaults, not the file. The file isn't being read. Check file path: should be exactly `~/.pi/agent/provider-manager.json` (not `~/.pi/agent/providers.json` or similar).
|
|
240
|
-
|
|
241
|
-
**Dashboard disappears after pressing `n` / `e` / `d` / `y` and Esc.** Should not happen in the current version — the dashboard is restored automatically. If it does, please report with `~/.pi/agent/provider-manager.log` output.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Related
|
|
246
|
-
|
|
247
|
-
- pi's built-in `/model` — switch the active model
|
|
248
|
-
- pi's built-in provider auth (`/login` or env vars) — set up API keys
|
|
249
|
-
- Backup flow: `/providers reset` or `cp ~/.pi/agent/models.json.bak ~/.pi/agent/models.json`
|
|
1
|
+
# provider-manager
|
|
2
|
+
|
|
3
|
+
[English](./README_EN.md) | [简体中文](./README.md)
|
|
4
|
+
|
|
5
|
+
A pi extension that manages custom providers and models in `~/.pi/agent/models.json` through a TUI dashboard, a `/providers` slash command, and a remote model sync.
|
|
6
|
+
|
|
7
|
+
> **Scope**: only `models.json` is covered — the extension does **not** manage built-in providers, does **not** switch models, and does **not** provide a login UI. Use pi's built-in `/model` and provider auth flow for those.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pi install npm:@fanchaozz/provider-manager
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The extension depends on `@earendil-works/pi-coding-agent` (shipped with pi). jiti walks up `node_modules`, so **no `npm install` is needed** inside the extension directory.
|
|
18
|
+
|
|
19
|
+
After install, `~/.pi/agent/provider-manager.json` is auto-created on first load (see [User config](#user-config--provider-managerjson)). Delete it to fall back to the code default.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Recent changes
|
|
24
|
+
|
|
25
|
+
Version history in [CHANGELOG.md](./CHANGELOG.md).
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
| Want to… | Do this |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Open the dashboard | `/providers` |
|
|
32
|
+
| List providers + their models | `/providers ls` (filter: `/providers ls kdapi`) |
|
|
33
|
+
| Add a provider | Dashboard, `n` on the Providers pane, or `/providers add [<id>]` |
|
|
34
|
+
| Add a model | Dashboard, switch to Models pane with `Tab`, `n`, or `/providers model <pid> add` |
|
|
35
|
+
| Edit provider / model | Dashboard, `e` |
|
|
36
|
+
| Delete | Dashboard, `d` (confirm dialog) |
|
|
37
|
+
| Pull new model list from a provider's API | Dashboard, `y`, or `/providers sync [<pid>]` |
|
|
38
|
+
| Probe auth + reachability + a 1-token test call | Dashboard, `t` (current model) or `T` (all in provider) |
|
|
39
|
+
| Restore last `.bak` | `/providers reset` |
|
|
40
|
+
| Print command help | `/providers help` |
|
|
41
|
+
| Close dashboard | `q` or `Esc` |
|
|
42
|
+
|
|
43
|
+
The sync command is the fastest way to populate a fresh provider: it fetches the remote model list, shows a checklist, and writes back the ones you select.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Dashboard
|
|
48
|
+
|
|
49
|
+
`/providers` opens a two-pane TUI:
|
|
50
|
+
|
|
51
|
+
- **Left pane** — providers (id + model count)
|
|
52
|
+
- **Right pane** — models of the selected provider (id + `[R]` reasoning / `[I]` image flags + ctx / max)
|
|
53
|
+
- **Detail strip** — raw JSON of the selected row
|
|
54
|
+
- **Footer** — current key bindings
|
|
55
|
+
|
|
56
|
+
### Key bindings
|
|
57
|
+
|
|
58
|
+
| Key | Action |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `↑↓` / `j k` | Navigate in current pane |
|
|
61
|
+
| `g` / `G` | Jump to top / bottom |
|
|
62
|
+
| `Tab` | Switch between Providers ↔ Models pane |
|
|
63
|
+
| `n` | New: add provider on Providers pane, add model on Models pane |
|
|
64
|
+
| `e` | Edit selected provider / model |
|
|
65
|
+
| `d` | Delete (with confirm dialog) |
|
|
66
|
+
| `y` | Sync (fetch remote model list for selected provider) |
|
|
67
|
+
| `t` / `T` | Probe current model / all models in selected provider |
|
|
68
|
+
| `?` | Toggle help overlay |
|
|
69
|
+
| `q` / `Esc` | Close dashboard |
|
|
70
|
+
|
|
71
|
+
When the provider list is empty, pressing `n` on the Models pane switches to the Providers pane and tells you to create one first.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Sync flow
|
|
76
|
+
|
|
77
|
+
`sync` is the **only** way to add a batch of models. It fetches the remote model list for the selected provider and shows a checklist.
|
|
78
|
+
|
|
79
|
+
The checklist **shows every model for that provider — both existing and remote new**:
|
|
80
|
+
|
|
81
|
+
- Existing models are labelled `<id> (existing)`, default checked. Uncheck to delete.
|
|
82
|
+
- Remote new models are labelled `<id>` only, default unchecked. Check to add.
|
|
83
|
+
|
|
84
|
+
Press `Enter` to write the result, `Esc` to cancel. On save, the final `models.json` is the union of (checked existing) + (checked new); remote new is preferred over local (so you pick up the fresh `reasoning` / `input` / `ctx` / `maxTokens` / `thinkingLevelMap`).
|
|
85
|
+
|
|
86
|
+
**The checklist mirrors pi's `/models` selector**, so 100+ models stay manageable:
|
|
87
|
+
|
|
88
|
+
- **Always-visible search input** at the top with a `> ` prompt. Every printable character (a / i / g / G, etc.) is appended to the search query — no shortcut clashes.
|
|
89
|
+
- **Viewport scrolling**: 8 rows by default. `⋮ N more below` / `⋮ N hidden` mark scroll boundaries.
|
|
90
|
+
- **Pinned first item**: when the cursor scrolls past the first page, the first item stays at the top with a `(top)` label.
|
|
91
|
+
- **Wrap-around navigation**: `↑` / `↓` cycle at the top / bottom; `j` / `k` are aliases.
|
|
92
|
+
- **Space toggles** the current row's selection, also inside a filter.
|
|
93
|
+
- **`selected / total selected` summary** at the top.
|
|
94
|
+
- **`(current/total)` position indicator** at the bottom (filter context when active).
|
|
95
|
+
|
|
96
|
+
Viewport size is overridable via `~/.pi/agent/provider-manager.json#syncViewportSize` (5–200, falls back to default 8 if out of range).
|
|
97
|
+
|
|
98
|
+
**Newly added models' fields come from `~/.pi/agent/provider-manager.json`'s `defaultModel` block** (not the code default) — this is the `loadDefaultModelConfig()` behavior. Edit that file before sync to customise the model template.
|
|
99
|
+
|
|
100
|
+
**proxy field**: a provider's edit form accepts a `proxy` URL (e.g. `http://127.0.0.1:7890`). On sync it's set as `HTTPS_PROXY` / `HTTP_PROXY` env vars and restored after the request. Other concurrent fetches in the same process will see the same proxy while it is set (env is process-global; sync runs at most one fetch at a time).
|
|
101
|
+
|
|
102
|
+
**apiKey character constraint**: sync rejects apiKeys with any character whose code > 255 (`•`, CJK, emoji, etc., typical from copy-paste). The error is actionable: `apiKey contains non-Latin-1 character at position 7 (U+2022). Re-enter the key in the provider form.`
|
|
103
|
+
|
|
104
|
+
**Noise filter**: embedding / tts / image-gen models are filtered by default (`embed*`, `tts`, `whisper`, `dall-e`, `clip`, `moderation`, `image-*`).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## User config — `provider-manager.json`
|
|
109
|
+
|
|
110
|
+
`~/.pi/agent/provider-manager.json` controls the defaults used when:
|
|
111
|
+
- you answer "yes" to "Use default config?" in the new-model form
|
|
112
|
+
- you sync new models from a remote API
|
|
113
|
+
|
|
114
|
+
Auto-created on first launch. Delete to revert to code defaults.
|
|
115
|
+
|
|
116
|
+
### Schema
|
|
117
|
+
|
|
118
|
+
```jsonc
|
|
119
|
+
{
|
|
120
|
+
"_defaultModel": "free-form comment, ignored at runtime",
|
|
121
|
+
"defaultModel": {
|
|
122
|
+
"reasoning": true,
|
|
123
|
+
"input": ["text", "image"],
|
|
124
|
+
"contextWindow": 128000,
|
|
125
|
+
"maxTokens": 16384,
|
|
126
|
+
"thinkingLevelMap": {
|
|
127
|
+
"off": null,
|
|
128
|
+
"minimal": null,
|
|
129
|
+
"low": null,
|
|
130
|
+
"medium": "medium",
|
|
131
|
+
"high": null,
|
|
132
|
+
"xhigh": null,
|
|
133
|
+
"max": null
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Field rules
|
|
140
|
+
|
|
141
|
+
- **`reasoning`** — boolean. If `true`, the model supports extended thinking and `thinkingLevelMap` applies.
|
|
142
|
+
- **`input`** — non-empty array of `"text"` and/or `"image"`. `"text" | "image"` means the model accepts that modality.
|
|
143
|
+
- **`contextWindow`** / **`maxTokens`** — positive integers (tokens).
|
|
144
|
+
- **`thinkingLevelMap`** — object. Any subset of the 7 keys (`off` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max`). A `string` value (e.g. `"medium"`) means that thinking level is enabled and the string is sent to the provider; `null` means disabled. Missing keys are treated as `null`.
|
|
145
|
+
|
|
146
|
+
If the file is missing, malformed JSON, or fails any check, the extension silently falls back to the built-in defaults shown above.
|
|
147
|
+
|
|
148
|
+
### Why `medium` is highlighted by default
|
|
149
|
+
|
|
150
|
+
When a synced model has `reasoning: true` and `thinkingLevelMap.medium = "medium"`, pi's Shift+Tab thinking-level cycle lands on `medium` by default. Pick whichever level your upstream actually supports — `null` is fine for providers with no extended-thinking knob.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Form editor (new model / edit model / new provider)
|
|
155
|
+
|
|
156
|
+
`addProviderFlow` / `editProviderFlow` / `addModelFlow` / `editModelFlow` / `deleteProviderFlow` / `deleteModelFlow` all share one TUI form (`components.ts:FormEditor`).
|
|
157
|
+
|
|
158
|
+
### Field types
|
|
159
|
+
|
|
160
|
+
| Type | Behavior |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `text` | Free text input |
|
|
163
|
+
| `secret` | Free text, rendered masked in the TUI |
|
|
164
|
+
| `number` | Free numeric input, validated on commit |
|
|
165
|
+
| `select` | Options list; press `e` to enter edit mode, `Space` to pick, `↑↓`/`jk` to navigate, `Enter` to commit |
|
|
166
|
+
| `multiselect` | Like `select` but multiple values; `Space` toggles each |
|
|
167
|
+
| `levelmap` | 7 rows (`off` / `minimal` / `low` / `medium` / `high` / `xhigh` / `max`); `Space` toggles each; commit normalizes missing keys to `null` |
|
|
168
|
+
| `readonly` | Display only, cannot edit |
|
|
169
|
+
|
|
170
|
+
### Key bindings
|
|
171
|
+
|
|
172
|
+
| Key | Behavior |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `e` / `E` | Enter edit mode (only on `select` / `levelmap` / `multiselect`) |
|
|
175
|
+
| `Esc` / `q` | If editing: exit edit (commit current value). Otherwise: cancel the form |
|
|
176
|
+
| `s` | Save the whole form. Only on non-typeable fields — `s` is a literal char inside `text` / `secret` / `number` / `json` |
|
|
177
|
+
| `Enter` | If editing on a non-input field: commit and exit edit. On a typeable field: commit + move to next field. On a readonly field: save the form |
|
|
178
|
+
| `Space` | If editing on a non-input field: toggle current option |
|
|
179
|
+
| `↑↓` / `j k` | Navigate fields; inside edit mode of a non-input field: navigate options |
|
|
180
|
+
| `Backspace` | Delete last char (typeable fields) |
|
|
181
|
+
|
|
182
|
+
### New-model flow: "Use default config?"
|
|
183
|
+
|
|
184
|
+
`addModelFlow` asks once after the name:
|
|
185
|
+
|
|
186
|
+
- **Yes** — apply `DEFAULT_MODEL_CONFIG` (see [User config](#user-config--provider-managerjson)) and skip the remaining questions
|
|
187
|
+
- **No** — ask reasoning / input / ctx / max / thinking-level-map one by one
|
|
188
|
+
|
|
189
|
+
`Esc` at any prompt cancels the whole flow (the dashboard is restored automatically).
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Test a model (`t` / `T`)
|
|
194
|
+
|
|
195
|
+
`t` probes the current model; `T` probes all models in the current provider. Three checks per model:
|
|
196
|
+
|
|
197
|
+
| Check | What it does | Cost |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `auth` | Looks up `~/.pi/agent/auth.json` (or env) for the provider's API key | free |
|
|
200
|
+
| `reachable` | `GET {baseUrl}/models` with a 10 s timeout | free |
|
|
201
|
+
| `generated` | Sends a 4-token prompt (`"Reply with the single word: ok"`) and checks `stopReason ∈ {stop, length}` | ~4 tokens |
|
|
202
|
+
|
|
203
|
+
Hard caps: `maxTokens` is clamped to 16, timeout 10 s (override with `PI_PROVIDER_TEST_TIMEOUT` env var in seconds). Result is cached in-process until you restart pi.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## File layout
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
~/.pi/agent/
|
|
211
|
+
├── models.json ← the file this extension edits
|
|
212
|
+
├── models.json.bak ← automatic backup taken before every write
|
|
213
|
+
└── extensions/
|
|
214
|
+
└── provider-manager/ ← this extension (installed via pi install / git clone)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The extension does not touch anything outside `models.json` and `models.json.bak`. To roll back, restore from `.bak` with `/providers reset` or manually:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
cp ~/.pi/agent/models.json.bak ~/.pi/agent/models.json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Troubleshooting
|
|
226
|
+
|
|
227
|
+
**`pi install` fails with `E404` or "no such package".** The npm registry doesn't have this package. First run `npm view @fanchaozz/provider-manager` to confirm the publish succeeded; if it has, check `npm config get registry` is not `npm.pkg.github.com`.
|
|
228
|
+
|
|
229
|
+
**Dashboard opens but is empty.** Your `models.json` has no custom providers. The extension only manages `models.json` — built-in pi providers (anthropic / openai / google / …) are not shown. Use pi's built-in `/model` for those.
|
|
230
|
+
|
|
231
|
+
**Sync errors with `ECONNREFUSED` / `ENOTFOUND`.** The selected provider's `baseUrl` is unreachable. Edit it with `/providers edit <pid>` (or dashboard `e`).
|
|
232
|
+
|
|
233
|
+
**Sync errors with `HTTP 500` / `HTTP 401`.** Wrong `baseUrl` or missing / wrong `apiKey`. Verify in the provider edit form.
|
|
234
|
+
|
|
235
|
+
**A thinking level I set keeps disappearing.** pi may not support that level on the underlying model — try a different level, or set it to `null` to disable.
|
|
236
|
+
|
|
237
|
+
**All my customizations in `provider-manager.json` are ignored.** The file is malformed or fails validation (see [Schema](#schema)). The extension falls back to defaults silently. Validate with `node -e "JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pi/agent/provider-manager.json','utf8'))"`.
|
|
238
|
+
|
|
239
|
+
**Models added by sync show wrong fields (`ctx=128000` regardless).** The model is using defaults, not the file. The file isn't being read. Check file path: should be exactly `~/.pi/agent/provider-manager.json` (not `~/.pi/agent/providers.json` or similar).
|
|
240
|
+
|
|
241
|
+
**Dashboard disappears after pressing `n` / `e` / `d` / `y` and Esc.** Should not happen in the current version — the dashboard is restored automatically. If it does, please report with `~/.pi/agent/provider-manager.log` output.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Related
|
|
246
|
+
|
|
247
|
+
- pi's built-in `/model` — switch the active model
|
|
248
|
+
- pi's built-in provider auth (`/login` or env vars) — set up API keys
|
|
249
|
+
- Backup flow: `/providers reset` or `cp ~/.pi/agent/models.json.bak ~/.pi/agent/models.json`
|
package/commands.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { readModelsJson } from "./store.ts";
|
|
10
|
-
import { openDashboard } from "./ui.ts";
|
|
10
|
+
import { openDashboard, openTestPanel } from "./ui.ts";
|
|
11
11
|
import {
|
|
12
12
|
addProviderFlow,
|
|
13
13
|
deleteProviderFlow,
|
|
@@ -228,7 +228,6 @@ function cmdHelp(ctx: ExtensionCommandContext): void {
|
|
|
228
228
|
// /providers test <provider>/<model> | /providers test-all [provider]
|
|
229
229
|
// ---------------------------------------------------------------------------
|
|
230
230
|
|
|
231
|
-
import { testModel, testProvider, formatTestResult } from "./test.ts";
|
|
232
231
|
|
|
233
232
|
/** 测单个 model:arg 支持 "<provider>/<model>" 或 "<model>"(缺省走 models.json 里第一个 provider)。 */
|
|
234
233
|
async function testCommand(ctx: ExtensionCommandContext, arg: string): Promise<void> {
|
|
@@ -258,9 +257,7 @@ async function testCommand(ctx: ExtensionCommandContext, arg: string): Promise<v
|
|
|
258
257
|
return;
|
|
259
258
|
}
|
|
260
259
|
|
|
261
|
-
|
|
262
|
-
// 同步 dashboard:测试结果统一 info(showStatus 可覆盖),失败语义靠文本 ✗ fail 前缀表达
|
|
263
|
-
ctx.ui.notify(formatTestResult(result), "info");
|
|
260
|
+
await openTestPanel(ctx, { provider, modelIds: [model], mode: "full" });
|
|
264
261
|
}
|
|
265
262
|
|
|
266
263
|
/** 批量测某 provider 全部 model;无参数时取第一个 provider。 */
|
|
@@ -288,16 +285,5 @@ async function testAllCommand(ctx: ExtensionCommandContext, providerId: string |
|
|
|
288
285
|
return;
|
|
289
286
|
}
|
|
290
287
|
|
|
291
|
-
ctx
|
|
292
|
-
const results = await testProvider({
|
|
293
|
-
ctx: ctx as any,
|
|
294
|
-
provider,
|
|
295
|
-
modelIds,
|
|
296
|
-
mode: "full",
|
|
297
|
-
concurrency: 3,
|
|
298
|
-
});
|
|
299
|
-
// 批量结果拼成一条 notify:逐条 notify 会被 showStatus 原地覆盖,只残留最后一条
|
|
300
|
-
const okCount = results.filter((r) => r.ok).length;
|
|
301
|
-
const summary = results.map((r) => formatTestResult(r)).join("\n\n") + `\n${provider}: ${okCount}/${results.length} ok`;
|
|
302
|
-
ctx.ui.notify(summary, "info");
|
|
288
|
+
await openTestPanel(ctx, { provider, modelIds, mode: "full", concurrency: 3 });
|
|
303
289
|
}
|