pi-llamacpp-infra 1.1.0 → 1.2.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 +62 -23
- package/package.json +4 -3
- package/src/core.ts +200 -0
- package/src/index.ts +320 -2369
- package/src/metrics.ts +243 -0
- package/src/prompt-warmup.ts +37 -150
- package/src/registration.ts +239 -0
- package/src/scan.ts +504 -0
- package/src/speed.ts +320 -0
- package/src/types.ts +242 -0
- package/src/ui.ts +812 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# llamacpp-infra — Discovery, Metrics & Control for llama.cpp-family Servers
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
**llamacpp-infra** turns pi into a first-class citizen of local llama.cpp infrastructure. It probes any number of machines — localhost, LAN or Tailscale — discovers every model served by llama.cpp and its variants (including LM Studio), registers them into pi's native `/model` list, and gives you live Prometheus metrics, per-model thinking budgets, vision detection and a full configuration UI — all without leaving the pi prompt.
|
|
4
6
|
|
|
5
7
|
---
|
|
@@ -21,9 +23,10 @@ Anything else (vLLM, Ollama, cloud APIs…) is out of scope — use pi's built-i
|
|
|
21
23
|
## Features
|
|
22
24
|
|
|
23
25
|
- **Multi-machine discovery** — configurable list of servers (host, ports, API key, options); probes all of them at startup and on demand
|
|
26
|
+
- **Compact model ids** — models appear as `Name (host:port)` in pi's `/model` picker, like a native provider; the raw GGUF path/alias is sent to the server automatically on every request
|
|
24
27
|
- **Single-model & router modes** — llama.cpp single-model mode (one GGUF per instance) and router mode (multiple models per server, with per-model status and args)
|
|
25
28
|
- **Per-model metadata badges** — 👁️ vision (mmproj / modalities), 🚀 drafter (speculative decoding), 🗜️ quant tag from GGUF filename, 🧠 KV cache quantization (from server args or `/proc`)
|
|
26
|
-
- **Live
|
|
29
|
+
- **Live speed & metrics** — a constantly updating footer reading of the active model's prefill (⚡) and generation (🔥) token speed, measured straight from the stream (per token, ~10 updates/s); when pi is idle it also mirrors other clients the server's `/metrics` endpoint reports. Lives in the footer's status line, so no extra terminal row is taken. Works even without `--metrics`
|
|
27
30
|
- **Thinking budgets** — llama.cpp accepts `thinking_budget_tokens` per request; configure budgets per thinking level (minimal/low/medium/high/xhigh/max) per model; models with budgets are registered with reasoning enabled
|
|
28
31
|
- **Header warmup** — pre-caches the system prompt KV on llama.cpp-family servers so the first real request is faster
|
|
29
32
|
- **LM Studio support** — uses LM Studio's OpenAI-compatible `/v1` API, enriches names/context/quant/vision from `/api/v1/models` (or legacy `/api/v0/models`), and avoids llama.cpp-only request fields
|
|
@@ -34,14 +37,14 @@ Anything else (vLLM, Ollama, cloud APIs…) is out of scope — use pi's built-i
|
|
|
34
37
|
|
|
35
38
|
## Install
|
|
36
39
|
|
|
37
|
-
llamacpp-infra is a [pi package](https://pi.dev/packages):
|
|
40
|
+
llamacpp-infra is a [pi package](https://pi.dev/packages): a small entrypoint (`src/index.ts`) backed by several focused modules. Heavier pieces (UI, scan engine, metrics, header warmup) are loaded lazily on first use so the extension stays light at startup. The package is declared in `package.json`.
|
|
38
41
|
|
|
39
42
|
```bash
|
|
40
43
|
# From GitHub
|
|
41
44
|
pi install git:github.com/noguerol/llamacpp-infra
|
|
42
45
|
|
|
43
46
|
# Pin a tag/commit
|
|
44
|
-
pi install git:github.com/noguerol/llamacpp-infra@v1.
|
|
47
|
+
pi install git:github.com/noguerol/llamacpp-infra@v1.2.0
|
|
45
48
|
|
|
46
49
|
# From npm
|
|
47
50
|
pi install npm:pi-llamacpp-infra
|
|
@@ -96,7 +99,7 @@ No special payload workaround is required: LM Studio accepts standard OpenAI cha
|
|
|
96
99
|
| `/llamacpp-infra scan` | Rescan all servers now |
|
|
97
100
|
| `/llamacpp-infra status` | Detailed per-endpoint report |
|
|
98
101
|
| `/llamacpp-infra list` | List discovered models with metadata badges |
|
|
99
|
-
| `/llamacpp-infra metrics` | Toggle
|
|
102
|
+
| `/llamacpp-infra metrics` | Toggle live speed & metrics in the footer |
|
|
100
103
|
| `/llamacpp-infra help` | Command help |
|
|
101
104
|
|
|
102
105
|
### `/llamacpp-infra config`
|
|
@@ -108,7 +111,7 @@ The main config menu branches into submenus:
|
|
|
108
111
|
- **📋 Models** — per-model options (thinking budgets, replace/remove)
|
|
109
112
|
- **🧪 Test** — connectivity test of all configured servers
|
|
110
113
|
- **🧠 Thinking budgets** — configure per-model thinking_budget_tokens per level
|
|
111
|
-
- **📈 Metrics** — enable/disable
|
|
114
|
+
- **📈 Metrics** — enable/disable footer metrics, server poll interval
|
|
112
115
|
- **⚙️ Settings** — discovery timeout, poll interval/budget, startup grace, fail limit, vision detection, prefix model IDs, name badges, unloaded router models, header warmup
|
|
113
116
|
- **ℹ️ About** — extension info
|
|
114
117
|
|
|
@@ -119,12 +122,14 @@ Shows every discovered model with metadata badges:
|
|
|
119
122
|
```
|
|
120
123
|
📋 Discovered models (8)
|
|
121
124
|
|
|
122
|
-
1.
|
|
123
|
-
2.
|
|
124
|
-
3.
|
|
125
|
-
4.
|
|
125
|
+
1. Qwen3.6-27B-UD-Q3_K_XL (local:8080) 👁️ 🗜️ UD-Q3_K_XL
|
|
126
|
+
2. DeepSeek-V4-Flash-ROCMFP2 (local:8081) 🗜️ ROCMFP2
|
|
127
|
+
3. Meta-Llama-3.1-8B (myserver:8080) 🚀 draft-model 🗜️ Q4_K_M
|
|
128
|
+
4. gemma-3-4b-it (myserver:8081) 👁️ 🗜️ Q4_K_M
|
|
126
129
|
```
|
|
127
130
|
|
|
131
|
+
The same compact id is what pi's `/model` picker shows, with the serving machine in parentheses.
|
|
132
|
+
|
|
128
133
|
### `/llamacpp-infra status`
|
|
129
134
|
|
|
130
135
|
Detailed per-endpoint report:
|
|
@@ -181,7 +186,7 @@ Everything is configurable through the UI, but the persisted file is `~/.pi/agen
|
|
|
181
186
|
"metricsPollMs": 5000
|
|
182
187
|
},
|
|
183
188
|
"modelOptions": {
|
|
184
|
-
"
|
|
189
|
+
"Qwen3.6-27B (myserver:8080)": {
|
|
185
190
|
"thinkingBudgets": {
|
|
186
191
|
"minimal": 256,
|
|
187
192
|
"low": 1024,
|
|
@@ -215,11 +220,11 @@ Everything is configurable through the UI, but the persisted file is `~/.pi/agen
|
|
|
215
220
|
| `startupGraceMs` | `40000` | Keep trying at startup while nothing has answered |
|
|
216
221
|
| `knownGoodFailLimit` | `3` | Consecutive failures before a live endpoint is dropped |
|
|
217
222
|
| `detectVision` | `true` | Scan `/proc` for `--mmproj` + read server-reported modalities |
|
|
218
|
-
| `prefixModelIds` | `true` | `host:port
|
|
223
|
+
| `prefixModelIds` | `true` | Append the machine tag `(host:port)` to model ids; OFF keeps bare names and only disambiguates collisions |
|
|
219
224
|
| `showBadgesInNames` | `true` | Append 👁️🚀💤 badges to model display names |
|
|
220
225
|
| `includeUnloadedRouterModels` | `false` | Router mode: list models that are not currently loaded |
|
|
221
226
|
| `warmup` | `true` | Pre-cache system prompt KV on llama.cpp servers |
|
|
222
|
-
| `metricsEnabled` | `true` |
|
|
227
|
+
| `metricsEnabled` | `true` | Show live speed & metrics in the footer for llamacpp-infra models |
|
|
223
228
|
| `metricsPollMs` | `5000` | How often `/metrics` is fetched |
|
|
224
229
|
|
|
225
230
|
### Thinking budgets
|
|
@@ -230,35 +235,69 @@ Levels: `minimal`, `low`, `medium`, `high`, `xhigh`, `max`.
|
|
|
230
235
|
|
|
231
236
|
## Model ID Format
|
|
232
237
|
|
|
233
|
-
|
|
238
|
+
Models are registered with compact display ids: `ModelName (host:port)`, e.g. `Qwen3.6-27B-UD-Q3_K_XL (myserver:8080)` — the compact model name plus the machine serving it in parentheses, matching how pi shows native provider models. Localhost servers (`127.0.0.1`, `localhost`) use `local:port` in the tag.
|
|
239
|
+
|
|
240
|
+
pi sends the compact id to the extension's request hook, which transparently rewrites it to the raw server-side id (the GGUF path, alias or router id the server advertised in `/v1/models`) before the request leaves pi. Config keys under `modelOptions` use the compact id; legacy `host:port/model` keys are migrated automatically on the first scan.
|
|
241
|
+
|
|
242
|
+
With `prefixModelIds: false` the machine tag is omitted (`ModelName`); it is re-added automatically only when two models would otherwise collide.
|
|
243
|
+
|
|
244
|
+
### Thinking budgets in the UI
|
|
234
245
|
|
|
235
|
-
|
|
246
|
+
llama.cpp-family models are registered as reasoning models, exactly like a native pi provider: the footer shows `ModelName (host:port) • <level>`, the thinking selector offers levels with token estimates, and pi sends the configured `thinking_budget_tokens` budget on each request. Per-model budgets configured in the extension override pi's global per-level budgets.
|
|
236
247
|
|
|
237
|
-
|
|
248
|
+
## Live Speed & Metrics (footer)
|
|
249
|
+
|
|
250
|
+
When enabled, the speed reading appears in the footer's status line (no extra terminal row) whenever the active model is from llamacpp-infra, updating constantly while tokens flow:
|
|
238
251
|
|
|
239
252
|
```
|
|
240
|
-
|
|
253
|
+
🦙 12 models · 3/3 ✓ 📊 · ⚡ prefill… (before the first token)
|
|
254
|
+
🦙 12 models · 3/3 ✓ 📊 · 🔥 38.1 t/s · ⚡ 420 · 1.2k tok (while streaming)
|
|
255
|
+
🦙 12 models · 3/3 ✓ 📊 · 🔥 38.1 t/s · ⚡ 420 · 1.2k tok (just after the answer ends)
|
|
256
|
+
🦙 12 models · 3/3 ✓ 📊 · ⏸ idle (between turns)
|
|
257
|
+
🦙 12 models · 3/3 ✓ 📊 · ▶ 2 · ⚡ 150 · 🔥 18.0 · server (pi idle, server busy for other clients)
|
|
241
258
|
```
|
|
242
259
|
|
|
243
|
-
|
|
260
|
+
(The `🦙 …` prefix is the extension's model-count status; both live on the same footer line, so no extra row is consumed.)
|
|
261
|
+
|
|
262
|
+
- **Client measurement (always, no `--metrics` needed)** — prefill speed = `prompt tokens ÷ (request → first token)` (pi's `usage.input`, OpenAI-style `prompt_tokens` as fallback); generation speed = a moving 1.5 s window over per-token arrival samples. Updated ~every 100 ms while a stream is live (throttled, and unchanged text is skipped, so the footer never churns).
|
|
263
|
+
- **Server supplement (only when pi is idle)** — the poller fetches the server's Prometheus `/metrics` endpoint (or JSON `/stats`) every `metricsPollMs` (default 5 s). If the server reports other clients processing, their ⚡/🔥 rates are shown; when the server is idle, the plain `⏸ idle` reading returns.
|
|
244
264
|
|
|
245
265
|
## Architecture
|
|
246
266
|
|
|
247
267
|
```
|
|
248
268
|
llamacpp-infra/
|
|
249
|
-
├── package.json
|
|
250
|
-
├── LICENSE
|
|
269
|
+
├── package.json # pi package manifest (pi-package)
|
|
270
|
+
├── LICENSE # MIT
|
|
251
271
|
├── README.md
|
|
252
272
|
└── src/
|
|
253
|
-
├── index.ts
|
|
254
|
-
|
|
273
|
+
├── index.ts # Entrypoint: hooks, command, lifecycle. Statically imports core + types.
|
|
274
|
+
├── core.ts # Config persistence, shared state, id helpers, compat profile (loaded at startup).
|
|
275
|
+
├── types.ts # Shared interfaces (type-only; erased at runtime).
|
|
276
|
+
├── scan.ts # Discovery engine (lazy: HTTP probing, /props, LM Studio catalog, /proc, kind detection).
|
|
277
|
+
├── registration.ts # Scan → pi-model mapping + provider registration (lazy).
|
|
278
|
+
├── metrics.ts # Server /metrics poller → ServerMetricsState (lazy; only if `metricsEnabled`).
|
|
279
|
+
├── speed.ts # Client-side speed tracker + footer status line (lazy; only if `metricsEnabled`).
|
|
280
|
+
├── ui.ts # /llamacpp-infra subcommands, menus, status, help (lazy).
|
|
281
|
+
└── prompt-warmup.ts # Header warmup: capture + cache system prompt KV (lazy; only if `warmup`).
|
|
255
282
|
```
|
|
256
283
|
|
|
257
|
-
|
|
284
|
+
Module load profile:
|
|
285
|
+
|
|
286
|
+
| Module | Loaded when | Approx. size |
|
|
287
|
+
|---|---|---|
|
|
288
|
+
| `index.ts` + `core.ts` (+ `types.ts`) | Startup (static) | ~25 KB |
|
|
289
|
+
| `scan.ts` + `registration.ts` | First discovery (dynamic) | ~27 KB |
|
|
290
|
+
| `prompt-warmup.ts` | Primed at load if `warmup` enabled; not loaded when disabled | ~15 KB; skipped entirely when `warmup` is OFF |
|
|
291
|
+
| `metrics.ts` + `speed.ts` | Primed at load if `metricsEnabled`; not loaded when disabled | ~18 KB; skipped entirely when `metricsEnabled` is OFF |
|
|
292
|
+
| `ui.ts` | First `/llamacpp-infra …` command (dynamic) | ~32 KB |
|
|
293
|
+
|
|
294
|
+
Zero external npm dependencies (only pi's bundled `@earendil-works/pi-coding-agent` + Node built-ins).
|
|
295
|
+
|
|
296
|
+
Subsystems:
|
|
258
297
|
|
|
259
298
|
- **Discovery engine** — multi-server probing with timeouts, retry budgets, and per-server kind detection (llama.cpp, ZINC, DwarfStar, lucebox, LM Studio)
|
|
260
299
|
- **Router support** — single-model and multi-model llama.cpp modes with per-model status, args parsing and metadata extraction
|
|
261
|
-
- **
|
|
300
|
+
- **Speed & metrics subsystem** — client-side per-token speed measurement (prefill + moving-window generation), throttled footer status updates, and server `/metrics` polling that supplements the footer while the client is idle
|
|
262
301
|
- **Thinking budgets** — per-model per-level configuration with automatic `reasoning` registration
|
|
263
302
|
- **Config persistence** — `~/.pi/agent/llamacpp-infra.json` with one-time migration from `local-models.json`
|
|
264
303
|
- **/proc scanner** — local llama-server process detection for vision, KV cache quant, and drafter flags
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-llamacpp-infra",
|
|
3
|
-
"version": "1.1
|
|
4
|
-
"description": "Discovery, metrics and control of llama.cpp-family servers for pi
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Discovery, metrics and control of llama.cpp-family servers for pi (llama.cpp, ZINC, DwarfStar/ds4, lucebox, LM Studio): scan machines (localhost, LAN, Tailscale), register models, live metrics, per-model thinking budgets, vision detection, native config UI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"llamacpp",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"pi": {
|
|
29
29
|
"extensions": [
|
|
30
30
|
"./src/index.ts"
|
|
31
|
-
]
|
|
31
|
+
],
|
|
32
|
+
"image": "https://raw.githubusercontent.com/noguerol/llamacpp-infra/main/docs/preview.jpeg"
|
|
32
33
|
},
|
|
33
34
|
"files": [
|
|
34
35
|
"src"
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Core: config persistence, constants, id helpers, shared mutable state.
|
|
2
|
+
// Statically imported by the entrypoint → must stay small.
|
|
3
|
+
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import type {
|
|
8
|
+
CompatProfile,
|
|
9
|
+
InfraConfig,
|
|
10
|
+
ModelOptions,
|
|
11
|
+
ServerConfig,
|
|
12
|
+
ServerKind,
|
|
13
|
+
SettingsConfig,
|
|
14
|
+
} from "./types.ts";
|
|
15
|
+
|
|
16
|
+
// ── Provider identity ───────────────────────────────────────────────────────
|
|
17
|
+
export const PROVIDER_NAME = "llamacpp-infra";
|
|
18
|
+
export const STATUS_KEY = "llamacpp-infra";
|
|
19
|
+
export const CONFIG_FILE = "llamacpp-infra.json";
|
|
20
|
+
export const LEGACY_CONFIG_FILE = "local-models.json";
|
|
21
|
+
export const METRICS_STATUS_KEY = "llamacpp-infra-speed";
|
|
22
|
+
export const DEFAULT_API_KEY = "no-auth";
|
|
23
|
+
export const THINKING_BUDGET_FIELD = "thinking_budget_tokens";
|
|
24
|
+
|
|
25
|
+
// ── Settings defaults ───────────────────────────────────────────────────────
|
|
26
|
+
export const DEFAULT_SETTINGS: SettingsConfig = {
|
|
27
|
+
discoveryTimeoutMs: 2000,
|
|
28
|
+
pollIntervalMs: 4000,
|
|
29
|
+
pollMaxMs: 90_000,
|
|
30
|
+
startupGraceMs: 40_000,
|
|
31
|
+
knownGoodFailLimit: 3,
|
|
32
|
+
detectVision: true,
|
|
33
|
+
prefixModelIds: true,
|
|
34
|
+
warmup: true,
|
|
35
|
+
metricsEnabled: true,
|
|
36
|
+
metricsPollMs: 5000,
|
|
37
|
+
includeUnloadedRouterModels: false,
|
|
38
|
+
showBadgesInNames: true,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const DEFAULT_SERVERS: ServerConfig[] = [
|
|
42
|
+
{
|
|
43
|
+
id: "local",
|
|
44
|
+
host: "127.0.0.1",
|
|
45
|
+
label: "Local",
|
|
46
|
+
ports: [8000, 8001, 8002, 8080, 8081, 8082, 1234],
|
|
47
|
+
enabled: true,
|
|
48
|
+
probeDs4: false,
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// ── Debug logging ──────────────────────────────────────────────────────────
|
|
53
|
+
export const DEBUG = process.env.PI_LLAMACPP_INFRA_DEBUG === "1" || process.env.PI_LLAMACPP_INFRA_DEBUG === "true";
|
|
54
|
+
export function debugLog(...args: unknown[]): void {
|
|
55
|
+
if (DEBUG) console.debug(`[llamacpp-infra]`, ...args);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Config persistence ─────────────────────────────────────────────────────
|
|
59
|
+
export function getConfigPath(): string {
|
|
60
|
+
return join(getAgentDir(), CONFIG_FILE);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function loadConfig(): InfraConfig {
|
|
64
|
+
const defaults: InfraConfig = {
|
|
65
|
+
servers: DEFAULT_SERVERS,
|
|
66
|
+
settings: { ...DEFAULT_SETTINGS },
|
|
67
|
+
modelOptions: {},
|
|
68
|
+
};
|
|
69
|
+
const path = getConfigPath();
|
|
70
|
+
if (existsSync(path)) {
|
|
71
|
+
try {
|
|
72
|
+
const raw = JSON.parse(readFileSync(path, "utf-8")) as Partial<InfraConfig>;
|
|
73
|
+
return {
|
|
74
|
+
servers: Array.isArray(raw.servers) ? raw.servers : defaults.servers,
|
|
75
|
+
settings: { ...DEFAULT_SETTINGS, ...(raw.settings ?? {}) },
|
|
76
|
+
modelOptions: raw.modelOptions ?? {},
|
|
77
|
+
};
|
|
78
|
+
} catch (err) {
|
|
79
|
+
console.error(`[llamacpp-infra] Config load error: ${err}`);
|
|
80
|
+
}
|
|
81
|
+
return defaults;
|
|
82
|
+
}
|
|
83
|
+
const legacyPath = join(getAgentDir(), LEGACY_CONFIG_FILE);
|
|
84
|
+
if (existsSync(legacyPath)) {
|
|
85
|
+
try {
|
|
86
|
+
const raw = JSON.parse(readFileSync(legacyPath, "utf-8")) as Partial<InfraConfig>;
|
|
87
|
+
const migrated: InfraConfig = {
|
|
88
|
+
servers: Array.isArray(raw.servers) ? raw.servers : defaults.servers,
|
|
89
|
+
settings: { ...DEFAULT_SETTINGS, ...(raw.settings ?? {}) },
|
|
90
|
+
modelOptions: raw.modelOptions ?? {},
|
|
91
|
+
};
|
|
92
|
+
debugLog(`migrated legacy config from ${legacyPath}`);
|
|
93
|
+
return migrated;
|
|
94
|
+
} catch {
|
|
95
|
+
// fall through to defaults
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return defaults;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function saveConfig(config: InfraConfig): void {
|
|
102
|
+
try {
|
|
103
|
+
const dir = getAgentDir();
|
|
104
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
105
|
+
writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), "utf-8");
|
|
106
|
+
debugLog(`config saved to ${getConfigPath()}`);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error(`[llamacpp-infra] Config save error: ${err}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Compat profile ─────────────────────────────────────────────────────────
|
|
113
|
+
export function supportsThinkingBudget(kind: ServerKind | "unknown" | "auto" | undefined): boolean {
|
|
114
|
+
return kind === "llamacpp" || kind === "lucebox";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function makeCompat(kind: ServerKind | "unknown" | "auto"): CompatProfile {
|
|
118
|
+
const usageInStreaming = kind !== "zinc";
|
|
119
|
+
return {
|
|
120
|
+
supportsDeveloperRole: false,
|
|
121
|
+
supportsReasoningEffort: false,
|
|
122
|
+
maxTokensField: "max_tokens" as const,
|
|
123
|
+
supportsUsageInStreaming: usageInStreaming,
|
|
124
|
+
supportsStrictMode: false,
|
|
125
|
+
...(supportsThinkingBudget(kind) ? { thinkingTokenBudgetField: THINKING_BUDGET_FIELD } : {}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ── Shared mutable state ───────────────────────────────────────────────────
|
|
130
|
+
// Maps used by hooks + lazy modules. Kept in one place so lazy imports share
|
|
131
|
+
// them via static import of core.ts.
|
|
132
|
+
export const shared = {
|
|
133
|
+
activeConfig: undefined as InfraConfig | undefined,
|
|
134
|
+
zincModelIds: new Set<string>(),
|
|
135
|
+
modelBaseUrls: new Map<string, string>(),
|
|
136
|
+
endpointKinds: new Map<string, string>(),
|
|
137
|
+
serverModelIds: new Map<string, string>(),
|
|
138
|
+
compactModelIds: new Map<string, string>(),
|
|
139
|
+
lastScan: undefined as import("./types.ts").ScanResult | undefined,
|
|
140
|
+
lastModels: [] as import("./types.ts").PiModel[],
|
|
141
|
+
registeredCount: 0,
|
|
142
|
+
lastError: undefined as string | undefined,
|
|
143
|
+
rIncludeUnloaded: false,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export function setActiveConfig(c: InfraConfig | undefined): void {
|
|
147
|
+
shared.activeConfig = c;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function modelOptions(): Record<string, ModelOptions> {
|
|
151
|
+
return shared.activeConfig?.modelOptions ?? {};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Compact id registered in pi for a raw server model id (or the input). */
|
|
155
|
+
export function compactIdFor(modelId: string | undefined): string | undefined {
|
|
156
|
+
if (!modelId) return undefined;
|
|
157
|
+
return shared.compactModelIds.get(modelId) ?? modelId;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Raw server-side id to send in requests for a registered model id (or the input). */
|
|
161
|
+
export function rawIdFor(modelId: string | undefined): string | undefined {
|
|
162
|
+
if (!modelId) return undefined;
|
|
163
|
+
return shared.serverModelIds.get(modelId) ?? modelId;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── Thinking-level normalization (used by the per-model budget hook) ──────
|
|
167
|
+
export function normalizeLevel(level: string | undefined): "minimal" | "low" | "medium" | "high" | undefined {
|
|
168
|
+
switch (level) {
|
|
169
|
+
case "minimal":
|
|
170
|
+
case "low":
|
|
171
|
+
case "medium":
|
|
172
|
+
case "high":
|
|
173
|
+
return level;
|
|
174
|
+
case "xhigh":
|
|
175
|
+
case "max":
|
|
176
|
+
return "high";
|
|
177
|
+
default:
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Server-label / id helpers ──────────────────────────────────────────────
|
|
183
|
+
export function serverLabel(srv: ServerConfig): string {
|
|
184
|
+
return srv.label?.trim() || srv.host;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function idSafeHost(host: string): string {
|
|
188
|
+
return host.trim().toLowerCase() || "host";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function isLocalHost(host: string): boolean {
|
|
192
|
+
const h = host.trim().toLowerCase();
|
|
193
|
+
return h === "127.0.0.1" || h === "localhost" || h === "::1";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function baseName(p: string): string {
|
|
197
|
+
const cleaned = p.replace(/\\/g, "/");
|
|
198
|
+
const last = cleaned.split("/").pop() || cleaned;
|
|
199
|
+
return last;
|
|
200
|
+
}
|