@maheidem/model-discovery 0.1.0 → 0.6.1
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 +197 -22
- package/index.ts +1465 -86
- package/package.json +9 -3
- package/profiles.ts +619 -0
package/README.md
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
# @maheidem/model-discovery
|
|
2
2
|
|
|
3
|
-
Interactive TUI for discovering and managing local AI model endpoints. Works with llama.cpp, oMLX, Ollama, vLLM, SGLang, LM Studio, and
|
|
3
|
+
Interactive TUI for discovering and managing local AI model endpoints. Works with llama.cpp, oMLX, Ollama, vLLM, SGLang, LM Studio, and other OpenAI-compatible servers.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Auto-detect
|
|
8
|
-
- **Read
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
7
|
+
- **Auto-detect server type** from headers and model data
|
|
8
|
+
- **Read server-reported configuration** — context window, max tokens, reasoning, and vision, with per-model overrides on top
|
|
9
|
+
- **Auto-detect vision-capable models (VLMs)** — from architecture metadata, llama.cpp `--mmproj` args, or oMLX capabilities
|
|
10
|
+
- **Auto-detect reasoning capability** — from `capabilities`, explicit `reasoning` fields, `--reasoning-budget`, and Qwen model names on oMLX
|
|
11
|
+
- **Auto-detect reasoning format** — oMLX servers get `chat_template_kwargs` thinking support automatically
|
|
12
|
+
- **Per-model compatibility** — `supportsDeveloperRole: false` for llama.cpp, oMLX, and Ollama; Qwen thinking format for oMLX
|
|
13
|
+
- **Fine-tune per-model overrides** — context window, max output, reasoning, and vision support
|
|
14
|
+
- **Profile-routed native thinking levels** — Shift-Tab can select complete thinking and sampling presets
|
|
15
|
+
- **Named model presets** — reuse complete thinking/sampling bundles as fixed aliases or adaptive routes
|
|
16
|
+
- **Offline management** — retain the last successful server catalogue for configuration and startup fallback
|
|
17
|
+
- **Authenticated endpoints** — enroll, replace, validate, or clear bearer API keys through masked TUI input
|
|
18
|
+
- **Multi-endpoint management** — add, rename, scan, and remove local model sources
|
|
19
|
+
- **LLM-callable tool** — the `discover_models` tool can register endpoints on the agent's behalf
|
|
20
|
+
- **Persistent storage** in `~/.pi/agent/model-discovery.json`
|
|
13
21
|
|
|
14
22
|
## Installation
|
|
15
23
|
|
|
@@ -18,42 +26,209 @@ Interactive TUI for discovering and managing local AI model endpoints. Works wit
|
|
|
18
26
|
pi install npm:@maheidem/model-discovery
|
|
19
27
|
|
|
20
28
|
# Via git
|
|
21
|
-
pi install git:github.com/Maheidem/model-discovery@v0.1
|
|
29
|
+
pi install git:github.com/Maheidem/model-discovery@v0.6.1
|
|
22
30
|
```
|
|
23
31
|
|
|
24
32
|
## Usage
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
Run `/discover` in Pi to open the management TUI:
|
|
27
35
|
|
|
28
|
-
|
|
36
|
+
- **Add endpoint** — enter a URL, choose anonymous or API-key authentication, probe it, review models, and register
|
|
37
|
+
- **Re-scan** — refresh models reported by an existing endpoint
|
|
38
|
+
- **Edit model** — override context, max output, reasoning, or vision support
|
|
39
|
+
- **Manage profiles** — create, edit, rename, or delete named variants
|
|
40
|
+
- **Rename source** — change the provider name shown by `/model`
|
|
41
|
+
- **Remove** — unregister and delete an endpoint
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
- **Scan existing** — re-scan registered endpoints for fresh model lists
|
|
32
|
-
- **Edit per-model** — override context window, max tokens, reasoning flags
|
|
33
|
-
- **Remove** — unregister and delete saved endpoints
|
|
43
|
+
You can jump directly into adding an endpoint:
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
```text
|
|
46
|
+
/discover http://192.168.1.100:8080
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Enrollment explicitly asks whether the endpoint is anonymous or requires an API key. API-key input is masked and is sent as `Authorization: Bearer <key>` for both `/v1/models` discovery and inference. The key is stored unencrypted in `~/.pi/agent/model-discovery.json`; the extension writes that file atomically with owner-only (`0600`) permissions. Use **Authentication** on an existing endpoint to replace, validate, or clear its key without losing the cached catalogue.
|
|
50
|
+
|
|
51
|
+
The LLM-callable tool remains available:
|
|
36
52
|
|
|
53
|
+
```text
|
|
54
|
+
discover_models(url="http://192.168.1.100:8080", providerName="my-llama")
|
|
37
55
|
```
|
|
38
|
-
|
|
56
|
+
|
|
57
|
+
The tool also accepts `apiKey`, but literal tool arguments may be retained in the agent session. Prefer the masked `/discover` flow for secrets.
|
|
58
|
+
|
|
59
|
+
## How model settings are detected
|
|
60
|
+
|
|
61
|
+
Every field is read from what the server actually reports, first value found wins:
|
|
62
|
+
|
|
63
|
+
- **Context window** — `context_length` → `context_window` → `max_model_len` → `max_context_len` → `max_context_length` → llama.cpp `--ctx-size` (args or preset) → `meta.n_ctx` for loaded models → the source's default context window → `128000`
|
|
64
|
+
- **Max output tokens** — `max_tokens` → `max_output_tokens` → `max_completion_tokens` → llama.cpp `--n-predict` → the source's default → `16384`
|
|
65
|
+
- **Reasoning** — `capabilities` containing `reasoning` → an explicit `reasoning` field → llama.cpp `--reasoning-budget` ≠ 0 → Qwen model names on oMLX (when the server reports nothing)
|
|
66
|
+
- **Vision** — `architecture.input_modalities` (vLLM, SGLang), vision-specific architecture keys (`vision_config`, `vision_model`, `mm_proj`, `multi_modal_projector`), llama.cpp `--mmproj`/`--vision` args or a preset name mentioning mmproj/vision, and oMLX `capabilities` containing `vision`, `image`, or `multimodal`
|
|
67
|
+
|
|
68
|
+
Detected vision-capable models get `input: ["text", "image"]`, so Pi accepts image input for them. The source defaults and every detection can be corrected per model with **Edit model**.
|
|
69
|
+
|
|
70
|
+
### Compatibility settings
|
|
71
|
+
|
|
72
|
+
The extension attaches `compat` to each registered model (Pi does not merge provider-level compat into individual models):
|
|
73
|
+
|
|
74
|
+
- llama.cpp, oMLX, Ollama: `supportsDeveloperRole: false`
|
|
75
|
+
- oMLX: `thinkingFormat: "qwen-chat-template"` and `supportsReasoningEffort: true` for base models; fixed and adaptive profile aliases carry their own complete `chat_template_kwargs` independently
|
|
76
|
+
|
|
77
|
+
### Model list display
|
|
78
|
+
|
|
79
|
+
Each model shows `ctx <window> · max <tokens> · <source>`, where source is `server args` for a live llama.cpp process and `api` for other backends. Flags: `[vision]`, `[reasoning]`, `[reasoning?]` (undetermined and not overridden), `[loaded]` (llama.cpp, currently in memory), and `(edited)` when overrides are present.
|
|
80
|
+
|
|
81
|
+
## Native thinking levels
|
|
82
|
+
|
|
83
|
+
For reasoning-capable Qwen models on oMLX, the base model and sampling-only profiles translate Pi's native Shift-Tab level into request-scoped `chat_template_kwargs`:
|
|
84
|
+
|
|
85
|
+
| Pi level | `enable_thinking` | Qwen `reasoning_effort` |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `off` | `false` | omitted |
|
|
88
|
+
| `minimal`, `low` | `true` | `low` |
|
|
89
|
+
| `medium` | `true` | `medium` |
|
|
90
|
+
| `high`, `xhigh`, `max` | `true` | `xhigh` |
|
|
91
|
+
|
|
92
|
+
`preserve_thinking` remains `true`. Fixed-thinking profiles intentionally stay locked to their configured state or effort, while profiles containing only sampling values inherit native Shift-Tab behavior.
|
|
93
|
+
|
|
94
|
+
### Explicit complete-profile routing
|
|
95
|
+
|
|
96
|
+
Adaptive routing is opt-in and separate from ordinary presets. Creating presets never changes the base model or another alias. In **Thinking & presets**, configure an adaptive alias and explicitly choose a preset for every Pi level. The conventional four-preset helper expands this layout:
|
|
97
|
+
|
|
98
|
+
| Pi level | Selected preset |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `off` | instruct/off preset |
|
|
101
|
+
| `minimal`, `low` | low preset |
|
|
102
|
+
| `medium` | medium preset |
|
|
103
|
+
| `high`, `xhigh`, `max` | xhigh preset |
|
|
104
|
+
|
|
105
|
+
The adaptive alias replaces all profile-controlled sampling fields and `chat_template_kwargs` on every request while retaining the same physical server model and conversation. Multiple presets may use the same reasoning effort because the mapping chooses by preset name rather than inference. The base model, sampling-only aliases, and fixed aliases retain their own behavior.
|
|
106
|
+
|
|
107
|
+
The TUI supports cloning presets, hiding preset aliases from `/model`, editing individual level mappings, previewing exact request payloads, disabling routing while preserving its map, and removing the adaptive alias without deleting presets. The footer shows `preset: <slug>` for an adaptive alias and `fixed preset: <slug>` for a fixed alias.
|
|
108
|
+
|
|
109
|
+
Example persisted routing:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"modelProfileRouting": {
|
|
114
|
+
"Qwen3.8-27B": {
|
|
115
|
+
"enabled": true,
|
|
116
|
+
"aliasSlug": "thinking",
|
|
117
|
+
"levels": {
|
|
118
|
+
"off": "instruct",
|
|
119
|
+
"minimal": "thinking-low",
|
|
120
|
+
"low": "thinking-low",
|
|
121
|
+
"medium": "thinking-medium",
|
|
122
|
+
"high": "thinking-xhigh",
|
|
123
|
+
"xhigh": "thinking-xhigh",
|
|
124
|
+
"max": "thinking-xhigh"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
39
129
|
```
|
|
40
130
|
|
|
41
|
-
|
|
131
|
+
## Named profiles
|
|
42
132
|
|
|
43
|
-
|
|
133
|
+
Presets with `exposeAsModel` omitted or `true` appear as fixed models under the same provider. Set `exposeAsModel: false` to keep a preset available to adaptive routing without cluttering `/model`. Given a server model named `Qwen3.8-27B` and a visible preset named `xhigh`, `/model` shows both:
|
|
44
134
|
|
|
135
|
+
```text
|
|
136
|
+
Qwen3.8-27B
|
|
137
|
+
Qwen3.8-27B@xhigh
|
|
45
138
|
```
|
|
46
|
-
|
|
139
|
+
|
|
140
|
+
The profile request still targets the real server model. For example:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"modelProfiles": {
|
|
145
|
+
"Qwen3.8-27B": [
|
|
146
|
+
{
|
|
147
|
+
"slug": "xhigh",
|
|
148
|
+
"chatTemplateKwargs": {
|
|
149
|
+
"enable_thinking": true,
|
|
150
|
+
"reasoning_effort": "xhigh",
|
|
151
|
+
"preserve_thinking": false
|
|
152
|
+
},
|
|
153
|
+
"exposeAsModel": false,
|
|
154
|
+
"sampling": {
|
|
155
|
+
"temperature": 0.7,
|
|
156
|
+
"topP": 0.9,
|
|
157
|
+
"topK": 20,
|
|
158
|
+
"minP": 0.05,
|
|
159
|
+
"repetitionPenalty": 1.05,
|
|
160
|
+
"presencePenalty": 0,
|
|
161
|
+
"frequencyPenalty": 0
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
47
167
|
```
|
|
48
168
|
|
|
169
|
+
Supported thinking values are:
|
|
170
|
+
|
|
171
|
+
- `enable_thinking`: `true` or `false`
|
|
172
|
+
- `reasoning_effort`: `"low"`, `"medium"`, or `"xhigh"`
|
|
173
|
+
- `preserve_thinking`: `true` or `false`
|
|
174
|
+
|
|
175
|
+
Supported sampling values are:
|
|
176
|
+
|
|
177
|
+
- `temperature`: `0–2`; `0` is greedy
|
|
178
|
+
- `topP`: `0–1`; `1` disables top-p filtering
|
|
179
|
+
- `topK`: integer `>= 0`; omit it to keep the backend-specific default
|
|
180
|
+
- `minP`: `0–1`; `0` disables min-p filtering
|
|
181
|
+
- `repetitionPenalty`: `> 0`; `1` disables it
|
|
182
|
+
- `presencePenalty`: `-2–2`; `0` disables it
|
|
183
|
+
- `frequencyPenalty`: `-2–2`; `0` disables it
|
|
184
|
+
|
|
185
|
+
The extension translates these backend-neutral storage names to top-level request fields such as `top_p`, `top_k`, `min_p`, `presence_penalty`, and `frequency_penalty`. Repetition penalty is sent as:
|
|
186
|
+
|
|
187
|
+
- `repeat_penalty` for llama.cpp and LM Studio
|
|
188
|
+
- `repetition_penalty` for oMLX, vLLM, and SGLang
|
|
189
|
+
- `repetition_penalty` as a best-effort fallback for Ollama and unknown OpenAI-compatible servers
|
|
190
|
+
|
|
191
|
+
A configured value is sent exactly. Fixed thinking values take precedence over Pi's current `/think` level, while fixed sampling values take precedence over Pi/request defaults. When every thinking value is omitted, the profile inherits the base model's native Shift-Tab behavior; when any thinking value is configured, only that profile's configured thinking keys are sent. Omitted sampling keys are not sent, leaving them to the server/model default. Profiles may contain thinking values, sampling values, or both. The base model remains independently selectable.
|
|
192
|
+
|
|
193
|
+
Sampling support still depends on the target server. In particular, Ollama's current OpenAI-compatible chat endpoint does not expose `top_k`, `min_p`, or a dedicated repetition-penalty field and may ignore those fallback keys. Omit unsupported controls to retain that backend's defaults.
|
|
194
|
+
|
|
195
|
+
If Pi has an `enabledModels` scope, press **Tab** in `/model` to switch from scoped models to all models, or add the profile alias to `enabledModels`.
|
|
196
|
+
|
|
197
|
+
Profiles are retained if a model temporarily disappears during a re-scan.
|
|
198
|
+
|
|
199
|
+
## Offline resilience
|
|
200
|
+
|
|
201
|
+
Every successful live scan atomically persists the raw model catalogue as the source's last known-good cache. Saved sources are scanned independently and concurrently at startup. If one source is offline, times out, rejects its credentials, or returns a malformed response:
|
|
202
|
+
|
|
203
|
+
- its cached base models, fixed aliases, and adaptive aliases remain registered;
|
|
204
|
+
- its overrides, presets, and routing remain editable through `/discover`;
|
|
205
|
+
- a failed or empty response never replaces the last known-good cache;
|
|
206
|
+
- the TUI shows the latest failure and time of the last successful scan; and
|
|
207
|
+
- other healthy sources continue loading normally.
|
|
208
|
+
|
|
209
|
+
`Re-scan all` does not unregister a provider before a replacement catalogue has been validated. A source with no previous successful scan is reported as unavailable without affecting any other source. The cache preserves discovery and configuration during an outage; actual inference still requires the model source to become reachable again.
|
|
210
|
+
|
|
211
|
+
Legacy implicit routing created by early v0.6 development builds is migrated once: its sampling-only adaptive alias becomes an explicit router with the same model ID, and its concrete presets are retained. Future presets never activate routing implicitly.
|
|
212
|
+
|
|
49
213
|
## Storage
|
|
50
214
|
|
|
51
|
-
Discovered providers are persisted in
|
|
215
|
+
Discovered providers, cached catalogues, model overrides, presets, and explicit routing maps are persisted in:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
~/.pi/agent/model-discovery.json
|
|
219
|
+
```
|
|
52
220
|
|
|
53
221
|
## Requirements
|
|
54
222
|
|
|
55
|
-
-
|
|
56
|
-
-
|
|
223
|
+
- Pi coding agent **0.84.0 or newer** (`samplingParams` support is required for model aliases)
|
|
224
|
+
- TUI support
|
|
225
|
+
- Network access to an OpenAI-compatible model server
|
|
226
|
+
|
|
227
|
+
## Development
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npm test
|
|
231
|
+
```
|
|
57
232
|
|
|
58
233
|
## License
|
|
59
234
|
|