dsh-local-models 0.0.0-stage → 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vmarcelo49
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README-es.md ADDED
@@ -0,0 +1,138 @@
1
+ # dsh-local-models
2
+
3
+ Un addon de `dsh` que añade una pestaña **Local Models** a la GUI web de dsh: elige un archivo `.gguf`, ajusta el contexto y la decodificación especulativa, observa una estimación de VRAM en vivo y cárgalo con `llama-server`; después registra el servidor en ejecución como proveedor de LLM en dsh con un solo clic.
4
+
5
+ Construido sobre el `llama.cpp` upstream sin modificaciones (`llama-server`). Sin fork, sin parches, sin paso de compilación: el bundle de cliente es `React.createElement` escrito a mano (sin toolchain de JSX) y la parte de node no tiene dependencias.
6
+
7
+ ## Features
8
+
9
+ - **Selector de modelo** — explorador de archivos dentro de la app (solo directorios + `.gguf`) con un parseo únicamente de la cabecera del GGUF (arquitectura, cuantización, capas, longitud de contexto, detección de MoE) detrás de `POST /local-models/gguf-meta`
10
+ - **Opciones de lanzamiento** — slider de contexto (pasos de 8K, limitado al contexto entrenado del modelo) + entrada de ajuste fino, selectores de cuantización de la caché KV (uno para K y otro para V — todos los tipos que acepta `llama-server`, con los bytes por elemento mostrados), profundidad fija del borrador MTP (0–7, upstream la limita a la profundidad nextn del modelo), nivel de razonamiento (`off`/`low`/`medium`/`xhigh`) + toggle de preservar el razonamiento (`--reasoning-preserve` vs `--no-reasoning-preserve`, desactivado por defecto), `mmproj` de visión opcional (offload a GPU o CPU), colocación de expertos MoE (`--cpu-moe` / `--n-cpu-moe` / override del top-k) con un asistente de ajuste a la VRAM
11
+ - **Estimación de VRAM en vivo** — pesos + los tipos de caché K/V seleccionados + estado recurrente + cómputo/grafo + overhead frente al total de GPU detectado (nvidia-smi / sysfs de amdgpu, sumados entre todas las GPUs, 16 GB asumidos cuando se desconoce), con filas de entra / margen de seguridad / ctx máximo que entra (ver [Problemas conocidos](./KNOWN_ISSUES.md) para la precisión en la familia Gemma)
12
+ - **Profiles** — guarda configuraciones de lanzamiento con nombre y recárgalas con un clic
13
+ - **Modo router** — sirve todos los perfiles guardados desde un único endpoint compatible con OpenAI (`--models-preset`); los modelos se cargan bajo demanda, uno residente a la vez por defecto. Arrancar el router registra (o vuelve a registrar) automáticamente sus modelos en dsh — sin pulsar Register a mano.
14
+ - **Register in dsh** — escribe el servidor listo como ruta de proveedor `llm-pi-ai` (con modalidad de visión + niveles de razonamiento, y salida máxima anunciada en 32K tokens (limitada a la mitad de la ventana para que la compactación conserve presupuesto de presión; sube maxTokens por request explícitamente para bloques largos de razonamiento xhigh))
15
+ - **Overlay de terminal** — tail en vivo del log de `llama-server` desde la pestaña
16
+
17
+ ## Requirements
18
+
19
+ - `dsh` con el perfil `web` (el plugin se compone dentro de él)
20
+ - Un binario `llama-server` (`llama.cpp` upstream, Vulkan/CUDA/CPU — lo que use tu máquina)
21
+ - El presupuesto de VRAM se detecta (`nvidia-smi` para NVIDIA, sysfs de amdgpu para AMD, sumando todas las GPUs visibles) y se puede fijar en la tarjeta Runtime de la pestaña; el fallback de 16 GB y el margen de seguridad están al principio de `lib/client.js` (`TOTAL_VRAM_BYTES`, `SAFE_MARGIN_BYTES`)
22
+
23
+ ## Install
24
+
25
+ Un plugin vive dentro de un **perfil** de dsh, que es un proyecto pnpm bajo
26
+ `$DSH_HOME/profiles/<name>`; `dsh plugin` reenvía sus argumentos a pnpm en
27
+ ese directorio.
28
+
29
+ ```bash
30
+ # from the npm registry
31
+ dsh plugin --profile web add dsh-local-models
32
+
33
+ # straight from git (plain ESM, no build step)
34
+ dsh plugin --profile web add github:Vmarcelo49/dsh-local-models
35
+
36
+ # from a local clone, for development (symlinked: edits apply on reload)
37
+ dsh plugin --profile web add link:/path/to/dsh-local-models
38
+ ```
39
+
40
+ `dsh plugin add` escribe la dependencia **y** añade el paquete a
41
+ `dsh.profile.bundles` en `$DSH_HOME/profiles/web/package.json` — ese array es lo
42
+ que lo monta, así que no hay nada que editar a mano. Reinicia el proceso web de
43
+ dsh (la composición del bundle ocurre al arrancar), recarga el navegador y abre
44
+ Settings → **Local Models**.
45
+
46
+ Comprueba la composición sin arrancar y vuelve a quitarlo con:
47
+
48
+ ```bash
49
+ dsh --profile web --dump-config | grep -A 2 dsh-local-models
50
+ dsh plugin --profile web remove dsh-local-models
51
+ ```
52
+
53
+ - **pnpm debe estar en el `PATH`.** npm o bun pueden instalar el propio CLI de
54
+ `dsh`, pero la gestión de plugins dentro de un perfil es cosa de pnpm
55
+ (`dsh plugin` lo invoca por debajo e imprime `pnpm was not found` si no está).
56
+ - **Sin puerta de versión, sin exención.** El paquete no declara dependencias
57
+ peer de `@deepseek-ai/*` — solo usa servicios inyectados (`settings`,
58
+ `credentials`, `webServer`) y slots de cliente —, así que `dsh plugin` nunca lo
59
+ rechaza por un desajuste de dsh y no hace falta `dsh plugin allow-version`.
60
+ - **Sin paso de compilación.** No hay script `prepare`, así que la puerta `allowBuilds`
61
+ de pnpm en la que caen los plugins alojados en git nunca se activa.
62
+ - **Comprobación del manifiesto.** [`dsh-plugin-dev check`](https://www.npmjs.com/package/dsh-plugin-guide)
63
+ (de `dsh-plugin-guide`) valida el manifiesto del bundle: `cordis.patch.yml`,
64
+ el puntero `dsh.bundle.patch`, `engines` y la lista blanca `files`.
65
+
66
+ > Los cambios en la parte de node (rutas, lista de inyecciones) necesitan reiniciar dsh; los cambios en la parte de cliente solo necesitan recargar la página.
67
+
68
+ ## Usage
69
+
70
+ 1. **Choose GGUF…** — elige un archivo de modelo (atajos Home / Models, navegación con Up).
71
+ 2. Ajusta **context**, **KV cache K / V**, **Max MTP head** (borrador fijo, 0-7; 3 es el punto óptimo ajustado — más profundidad colapsa con ctx grandes), **thinking level** + casilla **preserve thinking**, y los ajustes opcionales de **mmproj** y **MoE**.
72
+ 3. **Load model**, observa la tarjeta de estado, inspecciona la salida con **Open terminal**.
73
+ 4. **Register in dsh** — la ruta (por defecto `local-<alias>`) aparece en el selector de modelos.
74
+ 5. Como alternativa, guarda **perfiles** e **Start router (from profiles)** para tener un endpoint multimodelo.
75
+ 6. Marca **"Start the router automatically when dsh starts"** (tarjeta Router) para lanzar el router al inicio y registrar su ruta `local-router` en cuanto pase el chequeo de salud — los modelos siguen usándose sin abrir la pestaña. Requiere al menos un perfil guardado; el progreso aparece en `llama-server.log` (líneas `[autostart]`, visibles con Open terminal).
76
+ 7. **Expulsión por inactividad** (tarjeta Router, "Unload models after …", por defecto 30 min de inactividad) libera VRAM mediante el `--sleep-idle-seconds` de upstream tanto en las cargas individuales como en el router; el servidor dormido sigue respondiendo a `/health` y se recarga solo en la siguiente petición (una petición lenta). `0` la desactiva. Surte efecto en el siguiente arranque — la pestaña avisa cuando el servidor en ejecución usa un temporizador distinto.
77
+
78
+ ## Configuration
79
+
80
+ | Variable | Por defecto | Significado |
81
+ |---|---|---|
82
+ | `LOCAL_MODELS_PORT` | `8080` | puerto de `llama-server` |
83
+ | `LOCAL_MODELS_BIN` | — (autodetección) | binario del servidor o el directorio que lo contiene; el ajuste de la tarjeta Runtime tiene prioridad sobre él |
84
+ | `LOCAL_MODELS_SHORTCUTS` | — (ninguno) | directorios de atajo del explorador de archivos separados por dos puntos (`name=path` para etiquetas personalizadas); la lista de carpetas de la tarjeta Runtime toma el relevo una vez guardada |
85
+ | `LOCAL_MODELS_MMPROJ_CPU` | `1` | pesos del proyector de visión en RAM (`0` = offload a la GPU) |
86
+ | `LOCAL_MODELS_ROUTER_MAX` | `1` | máximo de modelos del router residentes a la vez |
87
+ | `LOCAL_MODELS_MAX_IMAGE_BYTES` | `10485760` | límite de imágenes de visión |
88
+ | `LOCAL_MODELS_IMAGE_PIXEL_BUDGET` | `4194304` | presupuesto de píxeles de visión |
89
+ | `DSH_HOME` | `~/.dsh` | directorio de datos (`local-models/profiles.json`, `local-models/settings.json`, `llama-server.log`) |
90
+
91
+ El presupuesto de VRAM de la pestaña se detecta, no está hardcodeado: NVIDIA a
92
+ través de `nvidia-smi`, AMD a través de sysfs (`mem_info_vram_total`, con el
93
+ nombre de producto resuelto desde `pci.ids` cuando existe), todas las GPUs
94
+ visibles sumadas, y `CUDA_VISIBLE_DEVICES` / `HIP_VISIBLE_DEVICES` respetadas.
95
+ El hardware que no se puede leer cae al histórico de 16 GiB, y el campo
96
+ **VRAM budget** de la tarjeta Runtime fija el número a mano (`settings.json` → `vramGb`, 0 = auto).
97
+
98
+ Los flags de lanzamiento están fijados a la configuración diaria validada: offload completo, `-b 2048 -ub 512 -t 4 -np 1`, `--flash-attn on --kv-unified`, razonamiento `--reasoning auto --reasoning-format deepseek --reasoning-effort <level>` más `--reasoning-preserve` cuando el toggle de preservar (campo `preserveThinking` del perfil) está activado y, si no, `--no-reasoning-preserve`, MTP `--spec-type draft-mtp --spec-draft-n-max N --spec-draft-p-min 0` (sin puerta — el propio valor por defecto de upstream; la puerta de confianza solo compensa en tarjetas con poco ancho de banda, en esta tarjeta de 16 GB cuesta ~32% de decode con n-max 3 mientras *sube* la aceptación del 63.5% → 91.1%, ver [bench/mtp_tuning.md](./bench/mtp_tuning.md); la pestaña ofrece profundidades 0-7, upstream limita la profundidad efectiva a la profundidad nextn del modelo, y el borrador es incondicional con cualquier ctx — la antigua casilla “ignore the MTP ctx softcap” ya no existe, así que un borrador profundo con un ctx grande todavía puede dar OOM o colapsar el decode), colocación multi-GPU `--split-mode` / `--tensor-split` cuando un perfil los define (por defecto: el reparto por capas del propio llama.cpp, sin flags — el control solo aparece cuando se detecta más de una GPU), y el par de caché KV de los selectores K/V de la pestaña (`--cache-type-k` / `--cache-type-v`, campos del perfil `kvTypeK` / `kvTypeV`). Se ofrecen todos los tipos que acepta este `llama-server` (`f32 f16 bf16 q8_0 q5_1 q5_0 q4_1 iq4_nl q4_0`, etiquetados con sus bytes/elemento); el `q5_0` K / `q4_1` V por defecto es el punto óptimo medido para 16 GB, y los perfiles heredados sin esos campos arrancan exactamente con ese par. La V cuantizada necesita flash-attn (aquí siempre activo) y la KV del borrador MTP se queda fijada a `q4_0`. Los modelos MLA (KV latente al estilo DeepSeek) rechazan tipos K/V mixtos en llama.cpp, así que la pestaña avisa y mantiene Load deshabilitado hasta que ambos coincidan, y la ruta `/run` rechaza ese lanzamiento con un error claro. Los presets del router llevan el mismo par KV por perfil y la misma elección `reasoning-preserve = 1/0`.
99
+
100
+ ## HTTP API (mounted under `/local-models`)
101
+
102
+ | Ruta | Significado |
103
+ |---|---|
104
+ | `GET /local-models/browse?dir=` | directorios + archivos `.gguf` |
105
+ | `POST /local-models/gguf-meta` | `{path}` → cabecera GGUF parseada (en caché) |
106
+ | `GET /local-models/status` | estado + sonda `/health` fresca |
107
+ | `GET /local-models/logs?offset=&max=` | tail incremental de `llama-server.log` |
108
+ | `POST /local-models/run` | lanza el servidor |
109
+ | `POST /local-models/stop` | detiene el hijo (o libera el puerto) |
110
+ | `POST /local-models/profiles` / `GET` | guarda (upsert) / lista perfiles |
111
+ | `POST /local-models/profiles/remove` | borra un perfil |
112
+ | `GET /local-models/settings` / `POST` | lee / actualiza los ajustes del plugin (`autostartRouter`, `autoUnloadMins`, `binPath`, `shortcuts`, `vramGb`) |
113
+ | `POST /local-models/runtime/check` | `{binPath}` → resuelve + `<bin> --version` (el Check de la tarjeta Runtime) |
114
+ | `POST /local-models/router/start` | construye los presets desde los perfiles + arranca el router |
115
+ | `POST /local-models/router/unload` | descarga un modelo del router |
116
+ | `POST /local-models/router/unload-all` | descarga todos los modelos del router |
117
+ | `POST /local-models/register` | añade el servidor listo como ruta `llm-pi-ai` |
118
+
119
+ ## Project layout
120
+
121
+ ```
122
+ lib/index.js node half: process manager, GGUF parser, routes, presets
123
+ lib/client.js browser half: settings tab (single build-free bundle)
124
+ skills/ operator skill: spawn-parity checklist, profile audits
125
+ docs/ UI mockup
126
+ ```
127
+
128
+ Los helpers puros y exportados (`normalizeEffort`, `moeArgsFor`, `generateRouterPresets`, `buildProviderProfile`, almacén de perfiles) están cubiertos por `npm test` (el runner integrado de node, `test/`); `node lib/index.js /path/to/model.gguf` vuelca una cabecera parseada a modo de autotest.
129
+
130
+ Módulos provistos por el host: `@deepseek-ai/dsh-client-runtime` y `@deepseek-ai/dsh-client-ui-settings` los inyecta el host de dsh en tiempo de bundle (ver la lista `dsh.client.inject` de `package.json`) y deliberadamente **no** están en `dependencies` — no existen en npm y no se deben instalar.
131
+
132
+ ## Known issues
133
+
134
+ Ver [KNOWN_ISSUES.md](./KNOWN_ISSUES.md) — en especial, la estimación de VRAM es aproximada para los layouts de la familia Gemma.
135
+
136
+ ## License
137
+
138
+ MIT — ver [LICENSE](./LICENSE).
package/README-hi.md ADDED
@@ -0,0 +1,119 @@
1
+ # dsh-local-models
2
+
3
+ एक `dsh` addon जो dsh Web GUI में **Local Models** tab जोड़ता है: कोई `.gguf` फ़ाइल चुनें, context और speculative decoding ट्यून करें, लाइव VRAM अनुमान देखें, और उसे `llama-server` के ज़रिए लोड करें — फिर चल रहे server को एक क्लिक में dsh में LLM provider के रूप में register कर दें।
4
+
5
+ stock upstream `llama.cpp` (`llama-server`) के विरुद्ध बनाया गया। कोई fork नहीं, कोई patch नहीं, कोई build step नहीं: client bundle हाथ से लिखा गया `React.createElement` है (कोई JSX toolchain नहीं) और node half dependency-free है।
6
+
7
+ ## Features
8
+
9
+ - **Model picker** — in-app file browser (सिर्फ़ directories + `.gguf`) के साथ header-only GGUF parse (architecture, quant, layers, context length, MoE detection), `POST /local-models/gguf-meta` के पीछे
10
+ - **Launch options** — context slider (8K steps, model के trained context पर capped) + fine-tune input, KV cache quantization selectors (एक K के लिए, एक V के लिए — हर वह type जो `llama-server` स्वीकार करता है, bytes-per-element दिखाया गया), fixed MTP draft depth (0–7, upstream model की nextn depth पर clamp करता है), thinking level (`off`/`low`/`medium`/`xhigh`) + preserve-thinking toggle (`--reasoning-preserve` बनाम `--no-reasoning-preserve`, default off), वैकल्पिक vision `mmproj` (GPU या CPU offload), MoE expert placement (`--cpu-moe` / `--n-cpu-moe` / top-k override) fit-to-VRAM helper के साथ
11
+ - **Live VRAM estimate** — weights + चुने गए K/V cache types + recurrent state + compute/graph + overhead, detected GPU total के विरुद्ध (nvidia-smi / amdgpu sysfs, सभी GPUs पर summed, अज्ञात होने पर 16 GB माना गया), fits / safe-margin / max-ctx-that-fits पंक्तियों के साथ (Gemma-family सटीकता के लिए [Known issues](./KNOWN_ISSUES.md) देखें)
12
+ - **Profiles** — नामित launch configurations सहेजें, एक क्लिक में फिर लोड करें
13
+ - **Router mode** — सभी सहेजे गए profiles को एक OpenAI-compatible endpoint (`--models-preset`) से serve करें; models माँग पर लोड होते हैं, default रूप से एक बार में एक ही resident रहता है। router शुरू करने पर dsh में उसके models अपने आप (फिर से) register हो जाते हैं — manual Register दबाने की ज़रूरत नहीं।
14
+ - **Register in dsh** — तैयार server को `llm-pi-ai` provider route के रूप में लिखता है (vision modality + thinking levels शामिल, max output 32K tokens बताया गया (window के आधे तक सीमित ताकि compaction का pressure budget बचा रहे; लंबे xhigh thinking blocks के लिए per-request maxTokens स्पष्ट रूप से बढ़ाएं))
15
+ - **Terminal overlay** — tab से ही `llama-server` log का live tail
16
+
17
+ ## Requirements
18
+
19
+ - `web` profile वाला `dsh` (plugin उसी में compose होता है)
20
+ - एक `llama-server` binary (upstream `llama.cpp`, Vulkan/CUDA/CPU — जो भी आपकी मशीन इस्तेमाल करती हो)
21
+ - VRAM budget detect होता है (NVIDIA के लिए `nvidia-smi`, AMD के लिए amdgpu sysfs, सभी दिखने वाले GPUs summed) और tab के Runtime card में pin किया जा सकता है; 16 GB fallback और safety margin `lib/client.js` के शीर्ष पर रहते हैं (`TOTAL_VRAM_BYTES`, `SAFE_MARGIN_BYTES`)
22
+
23
+ ## Install
24
+
25
+ एक plugin dsh **profile** के अंदर रहता है, जो `$DSH_HOME/profiles/<name>` के नीचे एक pnpm project है; `dsh plugin` अपने arguments उसी directory में pnpm को अग्रेषित करता है।
26
+
27
+ ```bash
28
+ # from the npm registry
29
+ dsh plugin --profile web add dsh-local-models
30
+
31
+ # straight from git (plain ESM, no build step)
32
+ dsh plugin --profile web add github:Vmarcelo49/dsh-local-models
33
+
34
+ # from a local clone, for development (symlinked: edits apply on reload)
35
+ dsh plugin --profile web add link:/path/to/dsh-local-models
36
+ ```
37
+
38
+ `dsh plugin add` dependency लिखता है **और** package को `$DSH_HOME/profiles/web/package.json` में `dsh.profile.bundles` के आगे जोड़ देता है — यही array इसे mount करता है, इसलिए हाथ से संपादित करने को कुछ नहीं है। dsh web process को restart करें (bundle composition boot पर होता है), browser refresh करें और Settings → **Local Models** खोलें।
39
+
40
+ boot किए बिना composition जाँचें, और इसे हटाएँ:
41
+
42
+ ```bash
43
+ dsh --profile web --dump-config | grep -A 2 dsh-local-models
44
+ dsh plugin --profile web remove dsh-local-models
45
+ ```
46
+
47
+ - **pnpm का `PATH` पर होना ज़रूरी है।** `dsh` CLI खुद npm या bun से install हो सकता है, लेकिन profile के अंदर plugin management pnpm का है (`dsh plugin` उसी को shell out करता है और अन्यथा `pnpm was not found` छापता है)।
48
+ - **कोई version gate नहीं, कोई exemption नहीं।** package कोई `@deepseek-ai/*` peer dependency declare नहीं करता — यह सिर्फ़ injected services (`settings`, `credentials`, `webServer`) और client slots इस्तेमाल करता है — इसलिए `dsh plugin` dsh mismatch पर इसे कभी अस्वीकार नहीं करता और किसी `dsh plugin allow-version` की ज़रूरत नहीं पड़ती।
49
+ - **कोई build step नहीं।** कोई `prepare` script नहीं है, इसलिए वह pnpm `allowBuilds` gate, जिससे git-hosted plugins टकराते हैं, कभी trigger ही नहीं होता।
50
+ - **Manifest check.** [`dsh-plugin-dev check`](https://www.npmjs.com/package/dsh-plugin-guide) (`dsh-plugin-guide` से) bundle manifest validate करता है: `cordis.patch.yml`, `dsh.bundle.patch` pointer, `engines` और `files` whitelist।
51
+
52
+ > Node-half changes (routes, inject list) के लिए dsh restart चाहिए; client-half changes के लिए सिर्फ़ page refresh काफ़ी है।
53
+
54
+ ## Usage
55
+
56
+ 1. **Choose GGUF…** — कोई model फ़ाइल चुनें (Home / Models shortcuts, Up navigation)।
57
+ 2. **context**, **KV cache K / V**, **Max MTP head** (fixed draft, 0-7; 3 ही ट्यून किया गया sweet spot है — इससे गहरा draft बड़े ctx पर collapse हो जाता है), **thinking level** + **preserve thinking** checkbox, वैकल्पिक **mmproj** और **MoE** settings ट्यून करें।
58
+ 3. **Load model**, status card देखें, **Open terminal** से output जाँचें।
59
+ 4. **Register in dsh** — route (default `local-<alias>`) Models picker में दिखने लगता है।
60
+ 5. वैकल्पिक रूप से **profiles** सहेजें और multi-model endpoint के लिए **Start router (from profiles)** करें।
61
+ 6. **"Start the router automatically when dsh starts"** (Router card) tick करें ताकि boot पर router शुरू हो और healthy होते ही उसका `local-router` route register हो जाए — tab खोले बिना भी models इस्तेमाल में रहते हैं। कम से कम एक सहेजा हुआ profile चाहिए; प्रगति `llama-server.log` में आती है (`[autostart]` पंक्तियाँ, Open terminal से दिखती हैं)।
62
+ 7. **Idle eviction** (Router card, "Unload models after …", default 30 min idle) single loads और router दोनों पर upstream `--sleep-idle-seconds` के ज़रिए VRAM मुक्त करता है; सोया हुआ server `/health` का जवाब देता रहता है और अगली request पर अपने आप फिर लोड हो जाता है (एक धीमी request)। `0` इसे बंद कर देता है। अगले start पर लागू होता है — चल रहा server अलग timer इस्तेमाल कर रहा हो तो tab चेतावनी देता है।
63
+
64
+ ## Configuration
65
+
66
+ | Variable | Default | Meaning |
67
+ |---|---|---|
68
+ | `LOCAL_MODELS_PORT` | `8080` | `llama-server` port |
69
+ | `LOCAL_MODELS_BIN` | — (auto-detect) | server binary या उसे रखने वाली dir; Runtime card की setting इस पर भारी पड़ती है |
70
+ | `LOCAL_MODELS_SHORTCUTS` | — (none) | colon-separated file-browser shortcut dirs (custom labels के लिए `name=path`); सहेजे जाने के बाद Runtime card की folder list कार्यभार संभाल लेती है |
71
+ | `LOCAL_MODELS_MMPROJ_CPU` | `1` | vision projector weights RAM में (`0` = GPU पर offload) |
72
+ | `LOCAL_MODELS_ROUTER_MAX` | `1` | एक साथ resident router models की अधिकतम संख्या |
73
+ | `LOCAL_MODELS_MAX_IMAGE_BYTES` | `10485760` | vision image guard |
74
+ | `LOCAL_MODELS_IMAGE_PIXEL_BUDGET` | `4194304` | vision pixel budget |
75
+ | `DSH_HOME` | `~/.dsh` | data dir (`local-models/profiles.json`, `local-models/settings.json`, `llama-server.log`) |
76
+
77
+ tab का VRAM budget hardcoded नहीं, detect होता है: NVIDIA `nvidia-smi` से, AMD sysfs से (`mem_info_vram_total`, मौजूद होने पर product name `pci.ids` से resolve किया जाता है), सभी दिखने वाले GPUs summed, और `CUDA_VISIBLE_DEVICES` / `HIP_VISIBLE_DEVICES` का सम्मान किया जाता है। जो hardware पढ़ा नहीं जा सकता वह ऐतिहासिक 16 GiB पर लौट आता है, और Runtime card का **VRAM budget** field संख्या हाथ से pin करता है (`settings.json` → `vramGb`, 0 = auto)।
78
+
79
+ Launch flags सत्यापित daily config पर स्थिर हैं: full offload, `-b 2048 -ub 512 -t 4 -np 1`, `--flash-attn on --kv-unified`, reasoning `--reasoning auto --reasoning-format deepseek --reasoning-effort <level>` तथा preserve toggle (profile `preserveThinking`) चालू होने पर `--reasoning-preserve`, वरना `--no-reasoning-preserve`, MTP `--spec-type draft-mtp --spec-draft-n-max N --spec-draft-p-min 0` (ungated — upstream का अपना default; confidence gate सिर्फ़ bandwidth-starved cards पर फ़ायदा देता है, इस 16 GB card पर यह n-max 3 पर decode का ~32% लेता है जबकि acceptance 63.5% → 91.1% *बढ़ाता* है, देखें [bench/mtp_tuning.md](./bench/mtp_tuning.md); tab 0-7 depths देता है, upstream प्रभावी depth को model की nextn depth पर clamp करता है, और draft किसी भी ctx पर बिना शर्त है — पुराना “ignore the MTP ctx softcap” checkbox हट चुका है, इसलिए बड़े ctx पर गहरा draft अब भी OOM कर सकता है या decode collapse कर सकता है), multi-GPU placement `--split-mode` / `--tensor-split` जब कोई profile उन्हें set करता हो (default: llama.cpp का अपना layer split, कोई flags नहीं — यह control सिर्फ़ तब दिखता है जब एक से ज़्यादा GPU detect हों), और tab के K/V selectors से KV cache pair (`--cache-type-k` / `--cache-type-v`, profile fields `kvTypeK` / `kvTypeV`)। यह `llama-server` जो भी type स्वीकार करता है वह सब दिया जाता है (`f32 f16 bf16 q8_0 q5_1 q5_0 q4_1 iq4_nl q4_0`, उसके bytes/element के साथ labeled); default `q5_0` K / `q4_1` V मापा गया 16 GB sweet spot है, और बिना इन fields वाले legacy profiles ठीक उसी pair के साथ launch होते हैं। Quantized V के लिए flash-attn चाहिए (यहाँ हमेशा on) और MTP draft KV `q4_0` पर pinned रहता है। MLA models (DeepSeek-style latent KV) llama.cpp में मिले-जुले K/V types अस्वीकार करते हैं, इसलिए tab चेतावनी देता है और दोनों के मेल खाने तक Load disabled रखता है, और `/run` route ऐसा launch स्पष्ट error के साथ मना कर देता है। Router presets वही per-profile KV pair और `reasoning-preserve = 1/0` चुनाव साथ ले जाते हैं।
80
+
81
+ ## HTTP API (mounted under `/local-models`)
82
+
83
+ | Route | Meaning |
84
+ |---|---|
85
+ | `GET /local-models/browse?dir=` | dirs + `.gguf` files |
86
+ | `POST /local-models/gguf-meta` | `{path}` → parsed GGUF header (cached) |
87
+ | `GET /local-models/status` | state + fresh `/health` probe |
88
+ | `GET /local-models/logs?offset=&max=` | incremental tail of `llama-server.log` |
89
+ | `POST /local-models/run` | spawn the server |
90
+ | `POST /local-models/stop` | stop the child (or reap the port) |
91
+ | `POST /local-models/profiles` / `GET` | save (upsert) / list profiles |
92
+ | `POST /local-models/profiles/remove` | delete a profile |
93
+ | `GET /local-models/settings` / `POST` | read / update plugin settings (`autostartRouter`, `autoUnloadMins`, `binPath`, `shortcuts`, `vramGb`) |
94
+ | `POST /local-models/runtime/check` | `{binPath}` → resolve + `<bin> --version` (the Runtime card's Check) |
95
+ | `POST /local-models/router/start` | build presets from profiles + start router |
96
+ | `POST /local-models/router/unload` | unload one router model |
97
+ | `POST /local-models/router/unload-all` | unload all router models |
98
+ | `POST /local-models/register` | add the ready server as an `llm-pi-ai` route |
99
+
100
+ ## Project layout
101
+
102
+ ```
103
+ lib/index.js node half: process manager, GGUF parser, routes, presets
104
+ lib/client.js browser half: settings tab (single build-free bundle)
105
+ skills/ operator skill: spawn-parity checklist, profile audits
106
+ docs/ UI mockup
107
+ ```
108
+
109
+ शुद्ध, exported helpers (`normalizeEffort`, `moeArgsFor`, `generateRouterPresets`, `buildProviderProfile`, profiles store) `npm test` से covered हैं (node का built-in runner, `test/`); `node lib/index.js /path/to/model.gguf` self-test के रूप में parsed header dump करता है।
110
+
111
+ Host-provided modules: `@deepseek-ai/dsh-client-runtime` और `@deepseek-ai/dsh-client-ui-settings` को dsh host bundle time पर inject करता है (देखें `package.json` में `dsh.client.inject` list) और ये जान-बूझकर `dependencies` में **नहीं** हैं — ये npm पर मौजूद नहीं हैं और इन्हें install नहीं करना चाहिए।
112
+
113
+ ## Known issues
114
+
115
+ देखें [KNOWN_ISSUES.md](./KNOWN_ISSUES.md) — सबसे उल्लेखनीय यह कि Gemma-family layouts के लिए VRAM अनुमान अनुमानित है।
116
+
117
+ ## License
118
+
119
+ MIT — देखें [LICENSE](./LICENSE).
package/README-pt.md ADDED
@@ -0,0 +1,138 @@
1
+ # dsh-local-models
2
+
3
+ Um addon do `dsh` que adiciona uma aba **Local Models** à Web GUI do dsh: escolha um arquivo `.gguf`, ajuste o contexto e a decodificação especulativa, acompanhe uma estimativa de VRAM ao vivo e carregue o modelo pelo `llama-server` — depois registre o servidor em execução como um provedor de LLM no dsh com um clique.
4
+
5
+ Construído sobre o `llama.cpp` upstream sem modificações (`llama-server`). Sem fork, sem patches, sem etapa de build: o bundle do cliente é `React.createElement` escrito à mão (sem toolchain JSX) e a metade node não tem dependências.
6
+
7
+ ## Features
8
+
9
+ - **Seletor de modelo** — navegador de arquivos dentro do app (apenas diretórios + `.gguf`) com leitura só do cabeçalho do GGUF (arquitetura, quant, camadas, comprimento de contexto, detecção de MoE) por trás de `POST /local-models/gguf-meta`
10
+ - **Opções de inicialização** — slider de contexto (passos de 8K, limitado ao contexto treinado do modelo) + campo de ajuste fino, seletores de quantização do KV cache (um para K, um para V — todos os tipos que o `llama-server` aceita, com bytes por elemento exibidos), profundidade fixa do draft MTP (0–7, o upstream limita à profundidade nextn do modelo), thinking level (`off`/`low`/`medium`/`xhigh`) + toggle de preservação do thinking (`--reasoning-preserve` vs `--no-reasoning-preserve`, padrão desativado), `mmproj` de visão opcional (offload para GPU ou CPU), posicionamento dos experts MoE (`--cpu-moe` / `--n-cpu-moe` / override de top-k) com um auxiliar de ajuste à VRAM
11
+ - **Estimativa de VRAM ao vivo** — pesos + os tipos de cache K/V selecionados + estado recorrente + compute/grafo + overhead contra o total detectado de GPU (nvidia-smi / sysfs do amdgpu, somados entre as GPUs, 16 GB presumidos quando desconhecido), com linhas de cabe / margem de segurança / ctx máximo que cabe (veja [Known issues](./KNOWN_ISSUES.md) para a precisão na família Gemma)
12
+ - **Profiles** — salve configurações de inicialização nomeadas e recarregue com um clique
13
+ - **Modo Router** — serve todos os profiles salvos a partir de um único endpoint compatível com OpenAI (`--models-preset`); os modelos carregam sob demanda, um residente por vez por padrão. Iniciar o router registra (ou re-registra) automaticamente seus modelos no dsh — sem precisar clicar em Register manualmente.
14
+ - **Register in dsh** — grava o servidor pronto como uma rota de provedor `llm-pi-ai` (modalidade de visão + thinking levels incluídos, saída máxima anunciada em 32K tokens (limitada à metade da janela para que a compactação mantenha budget de pressão; suba o maxTokens por request explicitamente para blocos longos de thinking xhigh))
15
+ - **Overlay de terminal** — tail ao vivo do log do `llama-server` direto da aba
16
+
17
+ ## Requirements
18
+
19
+ - `dsh` com o profile `web` (o plugin se compõe nele)
20
+ - Um binário `llama-server` (`llama.cpp` upstream, Vulkan/CUDA/CPU — o que a sua máquina usar)
21
+ - O VRAM budget é detectado (`nvidia-smi` para NVIDIA, sysfs do amdgpu para AMD, todas as GPUs visíveis somadas) e pode ser fixado no card Runtime da aba; o fallback de 16 GB e a margem de segurança ficam no topo de `lib/client.js` (`TOTAL_VRAM_BYTES`, `SAFE_MARGIN_BYTES`)
22
+
23
+ ## Install
24
+
25
+ Um plugin vive dentro de um **profile** do dsh, que é um projeto pnpm em
26
+ `$DSH_HOME/profiles/<name>`; o `dsh plugin` repassa seus argumentos para o pnpm
27
+ nesse diretório.
28
+
29
+ ```bash
30
+ # from the npm registry
31
+ dsh plugin --profile web add dsh-local-models
32
+
33
+ # straight from git (plain ESM, no build step)
34
+ dsh plugin --profile web add github:Vmarcelo49/dsh-local-models
35
+
36
+ # from a local clone, for development (symlinked: edits apply on reload)
37
+ dsh plugin --profile web add link:/path/to/dsh-local-models
38
+ ```
39
+
40
+ O `dsh plugin add` grava a dependência **e** acrescenta o pacote a
41
+ `dsh.profile.bundles` em `$DSH_HOME/profiles/web/package.json` — é esse array
42
+ que o monta, então não há nada para editar à mão. Reinicie o processo web do
43
+ dsh (a composição do bundle acontece no boot), atualize o navegador e abra
44
+ Settings → **Local Models**.
45
+
46
+ Verifique a composição sem inicializar e remova-o de novo com:
47
+
48
+ ```bash
49
+ dsh --profile web --dump-config | grep -A 2 dsh-local-models
50
+ dsh plugin --profile web remove dsh-local-models
51
+ ```
52
+
53
+ - **O pnpm precisa estar no `PATH`.** npm ou bun conseguem instalar a própria
54
+ CLI do `dsh`, mas o gerenciamento de plugins dentro de um profile é do pnpm
55
+ (o `dsh plugin` invoca o pnpm por baixo e imprime `pnpm was not found` caso contrário).
56
+ - **Sem gate de versão, sem exceção.** O pacote não declara nenhuma peer
57
+ dependency `@deepseek-ai/*` — ele usa apenas serviços injetados (`settings`,
58
+ `credentials`, `webServer`) e slots de cliente — então o `dsh plugin` nunca o
59
+ recusa por incompatibilidade de dsh e nenhum `dsh plugin allow-version` é necessário.
60
+ - **Sem etapa de build.** Não existe script `prepare`, então o gate
61
+ `allowBuilds` do pnpm que plugins hospedados no git encontram nunca é acionado.
62
+ - **Checagem de manifest.** O [`dsh-plugin-dev check`](https://www.npmjs.com/package/dsh-plugin-guide)
63
+ (do `dsh-plugin-guide`) valida o manifest do bundle: `cordis.patch.yml`,
64
+ o ponteiro `dsh.bundle.patch`, `engines` e a whitelist de `files`.
65
+
66
+ > Mudanças na metade node (rotas, lista de inject) exigem reiniciar o dsh; mudanças na metade cliente exigem apenas atualizar a página.
67
+
68
+ ## Usage
69
+
70
+ 1. **Choose GGUF…** — escolha um arquivo de modelo (atalhos Home / Models, navegação Up).
71
+ 2. Ajuste **context**, **KV cache K / V**, **Max MTP head** (draft fixo, 0-7; 3 é o ponto ideal ajustado — profundidades maiores colapsam em ctx grande), **thinking level** + checkbox **preserve thinking**, e as configurações opcionais de **mmproj** e **MoE**.
72
+ 3. **Load model**, acompanhe o card de status e inspecione a saída via **Open terminal**.
73
+ 4. **Register in dsh** — a rota (padrão `local-<alias>`) aparece no seletor de modelos.
74
+ 5. Como alternativa, salve **profiles** e use **Start router (from profiles)** para um endpoint com vários modelos.
75
+ 6. Marque **"Start the router automatically when dsh starts"** (card Router) para iniciar o router no boot e registrar sua rota `local-router` assim que ele estiver saudável — os modelos continuam utilizáveis sem abrir a aba. Requer pelo menos um profile salvo; o progresso vai para `llama-server.log` (linhas `[autostart]`, visíveis via Open terminal).
76
+ 7. **Despejo por ociosidade** (card Router, "Unload models after …", padrão 30 min de ociosidade) libera VRAM via `--sleep-idle-seconds` do upstream tanto em cargas avulsas quanto no router; o servidor dormindo continua respondendo a `/health` e recarrega automaticamente na próxima requisição (uma requisição lenta). `0` desativa. Passa a valer na próxima inicialização — a aba avisa quando o servidor em execução usa um timer diferente.
77
+
78
+ ## Configuration
79
+
80
+ | Variável | Padrão | Significado |
81
+ |---|---|---|
82
+ | `LOCAL_MODELS_PORT` | `8080` | porta do `llama-server` |
83
+ | `LOCAL_MODELS_BIN` | — (detecção automática) | binário do servidor ou o diretório que o contém; a configuração do card Runtime tem precedência |
84
+ | `LOCAL_MODELS_SHORTCUTS` | — (nenhum) | diretórios de atalho do navegador de arquivos separados por dois-pontos (`name=path` para rótulos personalizados); a lista de pastas do card Runtime assume o controle depois de salva |
85
+ | `LOCAL_MODELS_MMPROJ_CPU` | `1` | pesos do projetor de visão na RAM (`0` = offload para a GPU) |
86
+ | `LOCAL_MODELS_ROUTER_MAX` | `1` | máximo de modelos do router residentes ao mesmo tempo |
87
+ | `LOCAL_MODELS_MAX_IMAGE_BYTES` | `10485760` | limite de imagem da visão |
88
+ | `LOCAL_MODELS_IMAGE_PIXEL_BUDGET` | `4194304` | orçamento de pixels da visão |
89
+ | `DSH_HOME` | `~/.dsh` | diretório de dados (`local-models/profiles.json`, `local-models/settings.json`, `llama-server.log`) |
90
+
91
+ O VRAM budget da aba é detectado, não hardcoded: NVIDIA via `nvidia-smi`,
92
+ AMD via sysfs (`mem_info_vram_total`, com o nome do produto resolvido a partir de
93
+ `pci.ids` quando presente), todas as GPUs visíveis somadas, e `CUDA_VISIBLE_DEVICES` /
94
+ `HIP_VISIBLE_DEVICES` respeitadas. Hardware que não pode ser lido cai no valor
95
+ histórico de 16 GiB, e o campo **VRAM budget** do card Runtime fixa o número
96
+ manualmente (`settings.json` → `vramGb`, 0 = automático).
97
+
98
+ As flags de inicialização são fixas na configuração diária validada: offload total, `-b 2048 -ub 512 -t 4 -np 1`, `--flash-attn on --kv-unified`, reasoning `--reasoning auto --reasoning-format deepseek --reasoning-effort <level>` mais `--reasoning-preserve` quando o toggle de preservação (campo de profile `preserveThinking`) está ligado, senão `--no-reasoning-preserve`, MTP `--spec-type draft-mtp --spec-draft-n-max N --spec-draft-p-min 0` (sem gate — o próprio padrão do upstream; o gate de confiança só compensa em placas com banda de memória escassa, nesta placa de 16 GB ele custa ~32% de decode em n-max 3 enquanto *aumenta* a aceitação de 63.5% → 91.1%, veja [bench/mtp_tuning.md](./bench/mtp_tuning.md); a aba oferece profundidades 0-7, o upstream limita a profundidade efetiva à profundidade nextn do modelo, e o draft é incondicional em qualquer ctx — o antigo checkbox “ignore the MTP ctx softcap” não existe mais, então um draft profundo em ctx grande ainda pode estourar a memória (OOM) ou colapsar o decode), posicionamento multi-GPU `--split-mode` / `--tensor-split` quando um profile os define (padrão: o próprio layer split do llama.cpp, sem flags — o controle só aparece quando há mais de uma GPU detectada), e o par de KV cache dos seletores K/V da aba (`--cache-type-k` / `--cache-type-v`, campos de profile `kvTypeK` / `kvTypeV`). Todos os tipos que este `llama-server` aceita são oferecidos (`f32 f16 bf16 q8_0 q5_1 q5_0 q4_1 iq4_nl q4_0`, rotulados com seus bytes/elemento); o padrão `q5_0` K / `q4_1` V é o ponto ideal medido para 16 GB, e profiles legados sem esses campos iniciam exatamente com esse par. V quantizado exige flash-attn (sempre ligado aqui) e o KV do draft MTP fica fixo em `q4_0`. Modelos MLA (KV latente no estilo DeepSeek) rejeitam tipos K/V mistos no llama.cpp, então a aba avisa e mantém o Load desabilitado até que ambos coincidam, e a rota `/run` recusa esse tipo de inicialização com um erro claro. Os presets do Router carregam o mesmo par KV por profile e a mesma escolha `reasoning-preserve = 1/0`.
99
+
100
+ ## HTTP API (mounted under `/local-models`)
101
+
102
+ | Rota | Significado |
103
+ |---|---|
104
+ | `GET /local-models/browse?dir=` | diretórios + arquivos `.gguf` |
105
+ | `POST /local-models/gguf-meta` | `{path}` → cabeçalho GGUF parseado (em cache) |
106
+ | `GET /local-models/status` | estado + sonda `/health` recente |
107
+ | `GET /local-models/logs?offset=&max=` | tail incremental do `llama-server.log` |
108
+ | `POST /local-models/run` | inicia o servidor |
109
+ | `POST /local-models/stop` | para o processo filho (ou libera a porta) |
110
+ | `POST /local-models/profiles` / `GET` | salva (upsert) / lista profiles |
111
+ | `POST /local-models/profiles/remove` | exclui um profile |
112
+ | `GET /local-models/settings` / `POST` | lê / atualiza as configurações do plugin (`autostartRouter`, `autoUnloadMins`, `binPath`, `shortcuts`, `vramGb`) |
113
+ | `POST /local-models/runtime/check` | `{binPath}` → resolve + `<bin> --version` (o Check do card Runtime) |
114
+ | `POST /local-models/router/start` | monta os presets a partir dos profiles + inicia o router |
115
+ | `POST /local-models/router/unload` | descarrega um modelo do router |
116
+ | `POST /local-models/router/unload-all` | descarrega todos os modelos do router |
117
+ | `POST /local-models/register` | adiciona o servidor pronto como uma rota `llm-pi-ai` |
118
+
119
+ ## Project layout
120
+
121
+ ```
122
+ lib/index.js node half: process manager, GGUF parser, routes, presets
123
+ lib/client.js browser half: settings tab (single build-free bundle)
124
+ skills/ operator skill: spawn-parity checklist, profile audits
125
+ docs/ UI mockup
126
+ ```
127
+
128
+ Os helpers puros e exportados (`normalizeEffort`, `moeArgsFor`, `generateRouterPresets`, `buildProviderProfile`, store de profiles) são cobertos por `npm test` (runner embutido do node, `test/`); `node lib/index.js /path/to/model.gguf` despeja um cabeçalho parseado como autoteste.
129
+
130
+ Módulos fornecidos pelo host: `@deepseek-ai/dsh-client-runtime` e `@deepseek-ai/dsh-client-ui-settings` são injetados pelo host do dsh no momento da composição do bundle (veja a lista `dsh.client.inject` em `package.json`) e deliberadamente **não** estão em `dependencies` — eles não existem no npm e não devem ser instalados.
131
+
132
+ ## Known issues
133
+
134
+ Veja [KNOWN_ISSUES.md](./KNOWN_ISSUES.md) — principalmente, a estimativa de VRAM é aproximada para layouts da família Gemma.
135
+
136
+ ## License
137
+
138
+ MIT — veja [LICENSE](./LICENSE).
package/README-zh.md ADDED
@@ -0,0 +1,138 @@
1
+ # dsh-local-models
2
+
3
+ 一个 `dsh` 插件,为 dsh Web GUI 增加一个 **Local Models** 标签页:选择 `.gguf` 文件,调节上下文与推测解码,实时查看显存估算,并通过 `llama-server` 加载它 —— 随后一键把运行中的服务器注册为 dsh 里的 LLM 提供方。
4
+
5
+ 基于上游原版 `llama.cpp`(`llama-server`)构建。没有 fork,没有补丁,也没有构建步骤:客户端 bundle 是手写的 `React.createElement`(没有 JSX 工具链),node 半边零依赖。
6
+
7
+ ## Features
8
+
9
+ - **模型选择器** —— 应用内的文件浏览器(仅目录 + `.gguf`),由 `POST /local-models/gguf-meta` 提供仅解析头部的 GGUF 解析(架构、量化、层数、上下文长度、MoE 检测)
10
+ - **启动选项** —— 上下文滑块(8K 步进,上限为模型训练时的上下文)+ 微调输入框,KV cache 量化选择器(K 一个、V 一个 —— 覆盖 `llama-server` 接受的每一种类型,并显示每元素字节数),固定的 MTP 草稿深度(0–7,上游会截断到模型的 nextn 深度),thinking level(`off`/`low`/`medium`/`xhigh`)+ preserve-thinking 开关(`--reasoning-preserve` 与 `--no-reasoning-preserve`,默认关闭),可选的视觉 `mmproj`(GPU 或 CPU offload),MoE 专家放置(`--cpu-moe` / `--n-cpu-moe` / top-k 覆盖)以及 fit-to-VRAM 辅助工具
11
+ - **实时显存估算** —— 权重 + 所选的 K/V cache 类型 + 循环状态 + 计算/图 + 额外开销,与检测到的 GPU 总显存对比(nvidia-smi / amdgpu sysfs,多卡求和,未知时按 16 GB 计),并给出 fits / safe-margin / max-ctx-that-fits 三行结果(Gemma 系列的准确性见 [Known issues](./KNOWN_ISSUES.md))
12
+ - **Profiles** —— 保存具名的启动配置,一键重新加载
13
+ - **路由模式** —— 用一个 OpenAI 兼容端点(`--models-preset`)服务所有已保存的 profile;模型按需加载,默认同一时刻只驻留一个。启动路由会自动在 dsh 中(重新)注册它的模型 —— 无需手动点 Register。
14
+ - **Register in dsh** —— 把已就绪的服务器写成一个 `llm-pi-ai` 提供方路由(包含视觉模态 + thinking levels,最大输出声明为 32K tokens(上限为窗口的一半,以便 compact 保留压力预算;较长的 xhigh thinking 块请显式提高单次请求的 maxTokens))
15
+ - **终端浮层** —— 在标签页里实时跟踪 `llama-server` 日志
16
+
17
+ ## Requirements
18
+
19
+ - 带有 `web` profile 的 `dsh`(插件会组合进该 profile)
20
+ - 一个 `llama-server` 可执行文件(上游 `llama.cpp`,Vulkan/CUDA/CPU —— 取决于你的机器用哪种)
21
+ - 显存预算由检测得出(NVIDIA 走 `nvidia-smi`,AMD 走 amdgpu sysfs,所有可见 GPU 求和),也可以在标签页的 Runtime 卡片里手动固定;16 GB 回退值与安全余量位于 `lib/client.js` 顶部(`TOTAL_VRAM_BYTES`、`SAFE_MARGIN_BYTES`)
22
+
23
+ ## Install
24
+
25
+ 插件位于 dsh 的某个 **profile** 内,而 profile 是
26
+ `$DSH_HOME/profiles/<name>` 下的一个 pnpm 项目;`dsh plugin` 会把它的参数
27
+ 转发给该目录下的 pnpm。
28
+
29
+ ```bash
30
+ # from the npm registry
31
+ dsh plugin --profile web add dsh-local-models
32
+
33
+ # straight from git (plain ESM, no build step)
34
+ dsh plugin --profile web add github:Vmarcelo49/dsh-local-models
35
+
36
+ # from a local clone, for development (symlinked: edits apply on reload)
37
+ dsh plugin --profile web add link:/path/to/dsh-local-models
38
+ ```
39
+
40
+ `dsh plugin add` 会写入依赖,**并且**把该包追加到
41
+ `$DSH_HOME/profiles/web/package.json` 里的 `dsh.profile.bundles` —— 正是这个
42
+ 数组完成挂载,所以没有任何需要手工编辑的地方。重启 dsh web 进程(bundle
43
+ 组合发生在启动时),刷新浏览器,然后打开
44
+ Settings → **Local Models**。
45
+
46
+ 不启动也能检查组合结果,移除时用:
47
+
48
+ ```bash
49
+ dsh --profile web --dump-config | grep -A 2 dsh-local-models
50
+ dsh plugin --profile web remove dsh-local-models
51
+ ```
52
+
53
+ - **pnpm 必须在 `PATH` 上。** 安装 `dsh` CLI 本身可以用 npm 或 bun,但 profile
54
+ 内的插件管理归 pnpm 管(`dsh plugin` 会调用它,
55
+ 否则会打印 `pnpm was not found`)。
56
+ - **没有版本门槛,也不需要豁免。** 该包不声明任何 `@deepseek-ai/*` peer 依赖 ——
57
+ 它只使用注入的服务(`settings`、`credentials`、`webServer`)和客户端 slot ——
58
+ 所以 `dsh plugin` 不会因 dsh 版本不匹配而拒绝它,
59
+ 也不需要 `dsh plugin allow-version`。
60
+ - **没有构建步骤。** 该包没有 `prepare` 脚本,因此 git 托管的插件会撞上的
61
+ pnpm `allowBuilds` 门槛永远不会触发。
62
+ - **清单校验。** [`dsh-plugin-dev check`](https://www.npmjs.com/package/dsh-plugin-guide)
63
+ (来自 `dsh-plugin-guide`)会校验 bundle 清单:`cordis.patch.yml`、
64
+ `dsh.bundle.patch` 指针、`engines` 以及 `files` 白名单。
65
+
66
+ > node 半边的改动(路由、inject 列表)需要重启 dsh;client 半边的改动只需刷新页面。
67
+
68
+ ## Usage
69
+
70
+ 1. **Choose GGUF…** —— 选择模型文件(Home / Models 快捷入口,Up 向上导航)。
71
+ 2. 调整 **context**、**KV cache K / V**、**Max MTP head**(固定草稿,0-7;3 是调校好的甜点值 —— 更深的草稿在大 ctx 下会崩溃)、**thinking level** + **preserve thinking** 复选框,以及可选的 **mmproj** 和 **MoE** 设置。
72
+ 3. **Load model**,观察状态卡片,通过 **Open terminal** 查看输出。
73
+ 4. **Register in dsh** —— 该路由(默认 `local-<alias>`)会出现在 Models 选择器中。
74
+ 5. 或者保存 **profiles**,再用 **Start router (from profiles)** 得到一个多模型端点。
75
+ 6. 勾选 **“Start the router automatically when dsh starts”**(Router 卡片),即可在 dsh 启动时拉起路由,并在它健康后注册 `local-router` 路由 —— 不打开标签页也能继续使用模型。至少需要一个已保存的 profile;进度落在 `llama-server.log`(`[autostart]` 行,可通过 Open terminal 查看)。
76
+ 7. **Idle eviction**(Router 卡片,“Unload models after …”,默认空闲 30 分钟)会通过上游的 `--sleep-idle-seconds` 释放显存,对单次加载和路由都生效;休眠中的服务器仍会响应 `/health`,并在下一个请求时自动重新加载(该请求会慢一次)。`0` 表示关闭。下次启动时生效 —— 若正在运行的服务器使用了不同的计时器,标签页会给出警告。
77
+
78
+ ## Configuration
79
+
80
+ | 变量 | 默认值 | 含义 |
81
+ |---|---|---|
82
+ | `LOCAL_MODELS_PORT` | `8080` | `llama-server` 端口 |
83
+ | `LOCAL_MODELS_BIN` | ——(自动检测) | 服务器可执行文件或它所在的目录;Runtime 卡片里的设置优先级更高 |
84
+ | `LOCAL_MODELS_SHORTCUTS` | ——(无) | 以冒号分隔的文件浏览器快捷目录(`name=path` 可自定义标签);Runtime 卡片的文件夹列表一旦保存即接管 |
85
+ | `LOCAL_MODELS_MMPROJ_CPU` | `1` | 视觉投影权重放在内存中(`0` = offload 到 GPU) |
86
+ | `LOCAL_MODELS_ROUTER_MAX` | `1` | 路由中同时常驻的模型数量上限 |
87
+ | `LOCAL_MODELS_MAX_IMAGE_BYTES` | `10485760` | 视觉图像防护上限 |
88
+ | `LOCAL_MODELS_IMAGE_PIXEL_BUDGET` | `4194304` | 视觉像素预算 |
89
+ | `DSH_HOME` | `~/.dsh` | 数据目录(`local-models/profiles.json`、`local-models/settings.json`、`llama-server.log`) |
90
+
91
+ 标签页的显存预算靠检测而非硬编码:NVIDIA 通过 `nvidia-smi`,
92
+ AMD 通过 sysfs(`mem_info_vram_total`,存在 `pci.ids` 时会解析出产品名),
93
+ 所有可见 GPU 求和,并遵循 `CUDA_VISIBLE_DEVICES` /
94
+ `HIP_VISIBLE_DEVICES`。读不到硬件时回退到历史值 16 GiB,
95
+ Runtime 卡片的 **VRAM budget** 字段可以手动固定这个数字
96
+ (`settings.json` → `vramGb`,0 = 自动)。
97
+
98
+ 启动参数固定为经过验证的日常配置:全量 offload,`-b 2048 -ub 512 -t 4 -np 1`,`--flash-attn on --kv-unified`,推理用 `--reasoning auto --reasoning-format deepseek --reasoning-effort <level>`,并在 preserve 开关(profile 字段 `preserveThinking`)打开时追加 `--reasoning-preserve`,否则用 `--no-reasoning-preserve`;MTP 用 `--spec-type draft-mtp --spec-draft-n-max N --spec-draft-p-min 0`(不加门控 —— 这也是上游自己的默认值;置信度门控只在带宽吃紧的卡上才划算,而在这块 16 GB 卡上,n-max 为 3 时会损失约 32% 的解码速度,却把接受率从 63.5% *提高到* 91.1%,见 [bench/mtp_tuning.md](./bench/mtp_tuning.md);标签页提供 0-7 的深度,上游会把有效深度截断到模型的 nextn 深度,而且草稿在任何 ctx 下都是无条件的 —— 旧的“忽略 MTP ctx 软上限”复选框已经移除,所以在较大 ctx 下用很深的草稿仍可能 OOM 或让解码崩溃),profile 设置了多 GPU 放置时用 `--split-mode` / `--tensor-split`(默认:llama.cpp 自己的层划分,不带任何参数 —— 只有检测到多于一块 GPU 时才会出现该控件),以及标签页 K/V 选择器给出的 KV cache 组合(`--cache-type-k` / `--cache-type-v`,profile 字段 `kvTypeK` / `kvTypeV`)。该 `llama-server` 接受的每一种类型都会列出(`f32 f16 bf16 q8_0 q5_1 q5_0 q4_1 iq4_nl q4_0`,并标注它的字节/元素);默认的 `q5_0` K / `q4_1` V 是实测的 16 GB 甜点,缺少这些字段的旧 profile 也会以完全相同的组合启动。量化 V 需要 flash-attn(这里始终开启),而 MTP 草稿的 KV 固定为 `q4_0`。MLA 模型(DeepSeek 风格的 latent KV)在 llama.cpp 中不接受混合的 K/V 类型,因此标签页会警告,并在两者一致前保持 Load 不可用,`/run` 路由也会以明确的错误拒绝这类启动。路由 preset 会携带相同的按 profile 指定的 KV 组合和 `reasoning-preserve = 1/0` 选择。
99
+
100
+ ## HTTP API (mounted under `/local-models`)
101
+
102
+ | 路由 | 含义 |
103
+ |---|---|
104
+ | `GET /local-models/browse?dir=` | 目录 + `.gguf` 文件 |
105
+ | `POST /local-models/gguf-meta` | `{path}` → 解析出的 GGUF 头部(带缓存) |
106
+ | `GET /local-models/status` | 状态 + 实时的 `/health` 探测 |
107
+ | `GET /local-models/logs?offset=&max=` | `llama-server.log` 的增量尾部 |
108
+ | `POST /local-models/run` | 启动服务器 |
109
+ | `POST /local-models/stop` | 停止子进程(或回收端口) |
110
+ | `POST /local-models/profiles` / `GET` | 保存(upsert)/ 列出 profile |
111
+ | `POST /local-models/profiles/remove` | 删除一个 profile |
112
+ | `GET /local-models/settings` / `POST` | 读取 / 更新插件设置(`autostartRouter`、`autoUnloadMins`、`binPath`、`shortcuts`、`vramGb`) |
113
+ | `POST /local-models/runtime/check` | `{binPath}` → 解析 + `<bin> --version`(Runtime 卡片的 Check) |
114
+ | `POST /local-models/router/start` | 从 profile 构建 preset 并启动路由 |
115
+ | `POST /local-models/router/unload` | 卸载一个路由模型 |
116
+ | `POST /local-models/router/unload-all` | 卸载所有路由模型 |
117
+ | `POST /local-models/register` | 把已就绪的服务器添加为 `llm-pi-ai` 路由 |
118
+
119
+ ## Project layout
120
+
121
+ ```
122
+ lib/index.js node half: process manager, GGUF parser, routes, presets
123
+ lib/client.js browser half: settings tab (single build-free bundle)
124
+ skills/ operator skill: spawn-parity checklist, profile audits
125
+ docs/ UI mockup
126
+ ```
127
+
128
+ 纯函数、对外导出的辅助函数(`normalizeEffort`、`moeArgsFor`、`generateRouterPresets`、`buildProviderProfile`、profiles store)由 `npm test` 覆盖(node 内置测试运行器,`test/`);`node lib/index.js /path/to/model.gguf` 会把解析出的头部打印出来,作为自检。
129
+
130
+ 宿主提供的模块:`@deepseek-ai/dsh-client-runtime` 和 `@deepseek-ai/dsh-client-ui-settings` 由 dsh 宿主在打包 bundle 时注入(见 `package.json` 里的 `dsh.client.inject` 列表),并且刻意**不**放在 `dependencies` 中 —— 它们并不存在于 npm 上,也绝不能安装。
131
+
132
+ ## Known issues
133
+
134
+ 见 [KNOWN_ISSUES.md](./KNOWN_ISSUES.md) —— 其中最值得注意的是,显存估算对 Gemma 系列的结构只是近似值。
135
+
136
+ ## License
137
+
138
+ MIT —— 详见 [LICENSE](./LICENSE)。