turbollm 1.7.2 → 1.7.4
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 +23 -20
- package/dist/chunk-TPYU5ZAX.js +175 -0
- package/dist/cli.js +2125 -1948
- package/dist/distiller-UROKYEBN.js +9 -0
- package/dist/webdist/assets/AgentEditPage-BOnwuNEo.js +1 -0
- package/dist/webdist/assets/ChatScreen-C-etPxFP.js +65 -0
- package/dist/webdist/assets/CustomizeScreen-CBS-4CGF.js +4 -0
- package/dist/webdist/assets/DeveloperScreen-Dm9p8rsC.js +1 -0
- package/dist/webdist/assets/EnginesScreen-KF8kClsH.js +7 -0
- package/dist/webdist/assets/FsBrowser-Un46d4qe.js +1 -0
- package/dist/webdist/assets/{MessageBubble-COuZ8jTd.js → MessageBubble-5F87qAxY.js} +15 -16
- package/dist/webdist/assets/ModelsScreen-6VbHhlyf.js +2 -0
- package/dist/webdist/assets/SettingsScreen-DjoLkPpP.js +1 -0
- package/dist/webdist/assets/SkillEditPage-C5Evktgw.js +1 -0
- package/dist/webdist/assets/WorkspaceScreen-CiKb7f1I.js +1 -0
- package/dist/webdist/assets/agent-api-D3hMsrHk.js +1 -0
- package/dist/webdist/assets/{arc-BrQAHE5c.js → arc-BhnuNjta.js} +1 -1
- package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-DzOwDk0E.js → architectureDiagram-3BPJPVTR-c3DASz95.js} +1 -1
- package/dist/webdist/assets/{arrow-left-Dp4XRnbs.js → arrow-left-BZ7U2kS9.js} +1 -1
- package/dist/webdist/assets/{blockDiagram-GPEHLZMM-Cbs2lNCh.js → blockDiagram-GPEHLZMM-CkpfHyuv.js} +1 -1
- package/dist/webdist/assets/bot-B3GTFQ2J.js +1 -0
- package/dist/webdist/assets/{c4Diagram-AAUBKEIU-BAull9Yv.js → c4Diagram-AAUBKEIU-B5dn9Zuf.js} +1 -1
- package/dist/webdist/assets/channel-CJpy6WaL.js +1 -0
- package/dist/webdist/assets/chat-api-Dz6qvO1a.js +3 -0
- package/dist/webdist/assets/check-C39bHy3w.js +1 -0
- package/dist/webdist/assets/chevron-left-Bm2f38an.js +1 -0
- package/dist/webdist/assets/chevron-right-DGQJG4QN.js +1 -0
- package/dist/webdist/assets/{chunk-2J33WTMH-B38k4sJL.js → chunk-2J33WTMH-_x1EGF5n.js} +1 -1
- package/dist/webdist/assets/{chunk-4BX2VUAB-VxAkYvb7.js → chunk-4BX2VUAB-pQD6zp-n.js} +1 -1
- package/dist/webdist/assets/{chunk-55IACEB6-CAz9ZWyR.js → chunk-55IACEB6-DIsjXVDw.js} +1 -1
- package/dist/webdist/assets/{chunk-727SXJPM-CvLX5ghS.js → chunk-727SXJPM-CHDF4RtM.js} +1 -1
- package/dist/webdist/assets/{chunk-AQP2D5EJ-CZ0fsnc1.js → chunk-AQP2D5EJ-CaQFwmUk.js} +1 -1
- package/dist/webdist/assets/{chunk-FMBD7UC4-tjHz_yw8.js → chunk-FMBD7UC4-DENyT0Cw.js} +1 -1
- package/dist/webdist/assets/{chunk-ND2GUHAM-DVSh5E5_.js → chunk-ND2GUHAM-Cv6Yek6j.js} +1 -1
- package/dist/webdist/assets/{chunk-QZHKN3VN-D1rUM2SU.js → chunk-QZHKN3VN-9ATQymUl.js} +1 -1
- package/dist/webdist/assets/circle-x-DsCSoQSk.js +1 -0
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-BwNVmSJo.js +1 -0
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-BwNVmSJo.js +1 -0
- package/dist/webdist/assets/collapsible-Bqsm-UEI.js +1 -0
- package/dist/webdist/assets/common-DHHaSVnF.js +1 -0
- package/dist/webdist/assets/{cose-bilkent-S5V4N54A-DIUqY7o2.js → cose-bilkent-S5V4N54A-r9UktTKw.js} +1 -1
- package/dist/webdist/assets/{dagre-BM42HDAG-BzTcsgZ1.js → dagre-BM42HDAG-BnhFpJ0u.js} +1 -1
- package/dist/webdist/assets/{diagram-2AECGRRQ-DanHH-W_.js → diagram-2AECGRRQ-C8R_FKFE.js} +1 -1
- package/dist/webdist/assets/{diagram-5GNKFQAL-DQ5sWHUL.js → diagram-5GNKFQAL-Cy3ObA7-.js} +1 -1
- package/dist/webdist/assets/{diagram-KO2AKTUF-Jqr_pLwu.js → diagram-KO2AKTUF-BKMSyIfH.js} +1 -1
- package/dist/webdist/assets/{diagram-LMA3HP47-BZ89-cp0.js → diagram-LMA3HP47-M3eYUN5j.js} +1 -1
- package/dist/webdist/assets/{diagram-OG6HWLK6-cidBc5qa.js → diagram-OG6HWLK6-9mfuWNo6.js} +1 -1
- package/dist/webdist/assets/dialog-DTDvWQ5-.js +1 -0
- package/dist/webdist/assets/{erDiagram-TEJ5UH35-DEAKZN7b.js → erDiagram-TEJ5UH35-3pYBvVYw.js} +1 -1
- package/dist/webdist/assets/{flowDiagram-I6XJVG4X-Cyx8gMzN.js → flowDiagram-I6XJVG4X-DkeUYo0c.js} +1 -1
- package/dist/webdist/assets/folder-input-CjdiPWWo.js +1 -0
- package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-CqXYW44d.js → ganttDiagram-6RSMTGT7-Co8G6rQq.js} +1 -1
- package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-0dv_zKYA.js → gitGraphDiagram-PVQCEYII-BeKTVG6x.js} +1 -1
- package/dist/webdist/assets/{index-DHmOrvk5.js → index-BiJl342p.js} +17 -17
- package/dist/webdist/assets/index-CxonSCte.css +1 -0
- package/dist/webdist/assets/index-MBZ7hSNo.js +10 -0
- package/dist/webdist/assets/{infoDiagram-5YYISTIA-PxeM6E3u.js → infoDiagram-5YYISTIA-CSNZJB_2.js} +1 -1
- package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-BnNdTjj1.js → ishikawaDiagram-YF4QCWOH-Bl5G3Tv8.js} +1 -1
- package/dist/webdist/assets/{journeyDiagram-JHISSGLW-D3AHUGxz.js → journeyDiagram-JHISSGLW-8vGboHEi.js} +1 -1
- package/dist/webdist/assets/{kanban-definition-UN3LZRKU-YARl6SUt.js → kanban-definition-UN3LZRKU-Exhgn-QL.js} +1 -1
- package/dist/webdist/assets/{linear-CMEhaWGh.js → linear-BQ7-Rkhb.js} +1 -1
- package/dist/webdist/assets/{mermaid.core-Dfl2pPvv.js → mermaid.core-CQX56k_o.js} +4 -4
- package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-BojWC6jk.js → mindmap-definition-RKZ34NQL-Bo7PZvAB.js} +1 -1
- package/dist/webdist/assets/pencil-D52zQ528.js +1 -0
- package/dist/webdist/assets/personas-BvApKJbI.js +305 -0
- package/dist/webdist/assets/{pieDiagram-4H26LBE5-cjsdMtJJ.js → pieDiagram-4H26LBE5-q8hQguI2.js} +1 -1
- package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-DYVcgQuL.js → quadrantDiagram-W4KKPZXB-D7oJYa2u.js} +1 -1
- package/dist/webdist/assets/{radix-BY-fjzwm.js → radix-DHuxYtIT.js} +2 -2
- package/dist/webdist/assets/refresh-cw-CCa9KASO.js +1 -0
- package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-Dkj58nhB.js → requirementDiagram-4Y6WPE33-BB3AxfxX.js} +1 -1
- package/dist/webdist/assets/rotate-ccw-CjT25zbE.js +1 -0
- package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-l3GwUuNl.js → sankeyDiagram-5OEKKPKP-rBs95ARp.js} +1 -1
- package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-CWj1Dplo.js → sequenceDiagram-3UESZ5HK-Dl8lIjVG.js} +1 -1
- package/dist/webdist/assets/star-CGoRw01k.js +1 -0
- package/dist/webdist/assets/{stateDiagram-AJRCARHV-zbnPRcoE.js → stateDiagram-AJRCARHV-CLoQmP-w.js} +1 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-ByjTcwsY.js +1 -0
- package/dist/webdist/assets/{timeline-definition-PNZ67QCA-C_zAo1WZ.js → timeline-definition-PNZ67QCA-xQjF3Uja.js} +1 -1
- package/dist/webdist/assets/{trash-2-DJmVqx0w.js → trash-2-DtSyrOcz.js} +1 -1
- package/dist/webdist/assets/useIsDesktop-BFaxGM2l.js +1 -0
- package/dist/webdist/assets/{vendor-W5GIuFcv.js → vendor-BE0HADM8.js} +1 -1
- package/dist/webdist/assets/{vennDiagram-CIIHVFJN-DxfcocQu.js → vennDiagram-CIIHVFJN-CAcPzo7k.js} +1 -1
- package/dist/webdist/assets/{wardley-L42UT6IY-BnZ8BinJ.js → wardley-L42UT6IY-BPakyqHF.js} +1 -1
- package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-DMk9z7hr.js → wardleyDiagram-YWT4CUSO-DFeQJlAo.js} +1 -1
- package/dist/webdist/assets/wrench-BhEqkhsG.js +1 -0
- package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-BUtTPXPF.js → xychartDiagram-2RQKCTM6-CmW19GSM.js} +1 -1
- package/dist/webdist/index.html +4 -4
- package/package.json +1 -1
- package/dist/webdist/assets/AgentsScreen-sMeMB3IQ.js +0 -2
- package/dist/webdist/assets/ChatScreen-rXB7PrWJ.js +0 -65
- package/dist/webdist/assets/CustomizeScreen-BBwvtk28.js +0 -4
- package/dist/webdist/assets/DeveloperScreen-fXV-qXB5.js +0 -1
- package/dist/webdist/assets/EnginesScreen-CrmYzhio.js +0 -7
- package/dist/webdist/assets/ModelDetailDialog-Dlt5uF3h.js +0 -1
- package/dist/webdist/assets/ModelDirs-BYN-DmHL.js +0 -1
- package/dist/webdist/assets/ModelsScreen-DncNvsj6.js +0 -2
- package/dist/webdist/assets/SettingsScreen-DXX3r4Ep.js +0 -1
- package/dist/webdist/assets/channel-Bt5-ol5R.js +0 -1
- package/dist/webdist/assets/check-jMeH6YsH.js +0 -1
- package/dist/webdist/assets/chevron-down-C5ZMFCtb.js +0 -1
- package/dist/webdist/assets/chevron-right-DNVnXbZ1.js +0 -1
- package/dist/webdist/assets/circle-check-qnghY_qF.js +0 -1
- package/dist/webdist/assets/circle-x-FM28MwOL.js +0 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-jfnUpxOr.js +0 -1
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-jfnUpxOr.js +0 -1
- package/dist/webdist/assets/collapsible-CwqYFc9v.js +0 -1
- package/dist/webdist/assets/common-BD4Wa3KL.js +0 -1
- package/dist/webdist/assets/copy-button-DypRNkqI.js +0 -1
- package/dist/webdist/assets/dialog-3hyZ9j4e.js +0 -1
- package/dist/webdist/assets/index-B_WtFoLC.css +0 -1
- package/dist/webdist/assets/index-CriwC5VH.js +0 -10
- package/dist/webdist/assets/pencil-CS33zp9q.js +0 -1
- package/dist/webdist/assets/personas-CzcXp8zg.js +0 -75
- package/dist/webdist/assets/plus-BO3vGy6f.js +0 -1
- package/dist/webdist/assets/refresh-cw-Cco9RapO.js +0 -1
- package/dist/webdist/assets/save-CSISNMk8.js +0 -1
- package/dist/webdist/assets/skeleton-CBRf-twG.js +0 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-Ukx8AVKZ.js +0 -1
- package/dist/webdist/assets/vram-Dn53zt7R.js +0 -1
- package/dist/webdist/assets/wrench-Di5WLTz2.js +0 -1
package/README.md
CHANGED
|
@@ -177,8 +177,8 @@ the detail:
|
|
|
177
177
|
- **Saved per-model profiles, per engine** — tune once per (model, engine) pair, so
|
|
178
178
|
switching engines (or between two installs of the same engine, e.g. a fork) never
|
|
179
179
|
overwrites another engine's tuning for the same model.
|
|
180
|
-
- **Configurable VRAM headroom** (Settings →
|
|
181
|
-
how much VRAM to keep free for other GPU workloads instead of a fixed margin.
|
|
180
|
+
- **Configurable VRAM headroom** (Settings → Models & loading → Advanced, 300 MB–2 GB, default
|
|
181
|
+
1 GB) — tell auto-tune how much VRAM to keep free for other GPU workloads instead of a fixed margin.
|
|
182
182
|
|
|
183
183
|
</details>
|
|
184
184
|
|
|
@@ -196,9 +196,11 @@ the detail:
|
|
|
196
196
|
the model draws when a comparison, trend, or hierarchy is genuinely worth a visual.
|
|
197
197
|
- **Live artifacts** — `html`, `svg`, and `mermaid` replies render as **sandboxed, offline
|
|
198
198
|
previews** shown as an image, with one-click export to **PNG / JPEG / SVG / animated GIF / HTML**.
|
|
199
|
-
- **
|
|
200
|
-
Creative · Research) per conversation, no prompt-wrangling required. The
|
|
201
|
-
produces polished, self-contained, previewable designs by default
|
|
199
|
+
- **Agents** — pick a style (Default · **Designer** · Concise · Detailed · Blunt · Formal · Tutor ·
|
|
200
|
+
Creative · Research · **Lite** · **Code**) per conversation, no prompt-wrangling required. The
|
|
201
|
+
**Designer** persona produces polished, self-contained, previewable designs by default; **Lite**
|
|
202
|
+
strips the hidden prompt to the bare minimum for the fastest responses. See below for editing
|
|
203
|
+
built-ins or creating your own.
|
|
202
204
|
- **Edit, regenerate, delete, copy** any message; **persistent, searchable conversations**
|
|
203
205
|
with rename, delete, and **auto-generated titles**, organized into **drag-resizable,
|
|
204
206
|
collapsible folders** you create/rename/move conversations into.
|
|
@@ -214,22 +216,22 @@ the detail:
|
|
|
214
216
|
in every chat with no restart. A **Research** persona forces multi-step web search and cites sources inline.
|
|
215
217
|
- **Tool-call approval gate** — every tool call asks for your approval by default before it runs,
|
|
216
218
|
with **Deny**, **Allow**, **Allow for this chat**, or **Always Allow** on an inline bar above the
|
|
217
|
-
composer. Set per-tool defaults globally from
|
|
219
|
+
composer. Set per-tool defaults globally from Settings → Tools & safety.
|
|
218
220
|
|
|
219
221
|
</details>
|
|
220
222
|
|
|
221
223
|
<details>
|
|
222
|
-
<summary><strong>🤖
|
|
224
|
+
<summary><strong>🤖 Customize → Agents — edit any built-in, or build your own</strong></summary>
|
|
223
225
|
|
|
224
226
|
<br/>
|
|
225
227
|
|
|
226
|
-
- **
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
- **Edit any built-in agent in place** — system prompt, which shared skills it uses, and which
|
|
229
|
+
tools it may call — with a one-click **Reset** back to the original.
|
|
230
|
+
- **Create your own agent** from scratch: a name, description, and system prompt, plus a
|
|
231
|
+
checklist of which shared skills and which tools it's allowed to use (everything on by
|
|
232
|
+
default). Pick it from the same in-chat agent picker as the built-ins.
|
|
233
|
+
- **MCP tools grouped by server** in the tool checklist — one toggle selects or deselects an
|
|
234
|
+
entire server's tools at once, or expand it to pick individually.
|
|
233
235
|
|
|
234
236
|
</details>
|
|
235
237
|
|
|
@@ -253,7 +255,7 @@ curl http://127.0.0.1:6996/v1/chat/completions \
|
|
|
253
255
|
- **Anthropic-compatible** `/v1/messages` — including **tool use and streaming** — which powers
|
|
254
256
|
Claude Code below. No other local host offers this.
|
|
255
257
|
- **Structured output** — constrain any response to a **GBNF grammar** (or JSON shape).
|
|
256
|
-
- **API-key auth** you can require when sharing over a LAN (Settings → Network).
|
|
258
|
+
- **API-key auth** you can require when sharing over a LAN (Settings → Network & sharing).
|
|
257
259
|
|
|
258
260
|
**The gateway loads models for you.** Most local hosts make you load a model first, then call it.
|
|
259
261
|
TurboLLM's gateway reads the `model` field of any incoming request, **fuzzy-matches it to your
|
|
@@ -265,7 +267,7 @@ coding model, a vision model, and an embedder just names each one and it works
|
|
|
265
267
|
`opencode`, `kilo`, `openclaw`, and `hermes` — each gets pointed at TurboLLM the way that tool
|
|
266
268
|
expects (config file merge, or its own CLI command) instead of a manual copy-paste setup. Inside
|
|
267
269
|
Claude Code, `/model` lists your local models directly (with **Auto Model Swap** on in Settings →
|
|
268
|
-
|
|
270
|
+
Models & loading) so you can switch mid-session instead of only at launch.
|
|
269
271
|
|
|
270
272
|
</details>
|
|
271
273
|
|
|
@@ -283,7 +285,7 @@ fight for memory (and one usually OOMs). TurboLLM can hand the GPU over automati
|
|
|
283
285
|
It's **push-based, not polling** — ComfyUI signals TurboLLM the moment a job starts/ends, so the
|
|
284
286
|
handoff is immediate and deterministic (the model is gone *before* ComfyUI executes).
|
|
285
287
|
|
|
286
|
-
**One-time setup** (Settings → ComfyUI): turn on **Pause for ComfyUI**, enter your ComfyUI folder
|
|
288
|
+
**One-time setup** (Settings → Network & sharing → ComfyUI): turn on **Pause for ComfyUI**, enter your ComfyUI folder
|
|
287
289
|
(the one containing `custom_nodes`), click **Install gate** (it writes a small custom node wired to
|
|
288
290
|
this daemon), then **restart ComfyUI** once. The panel shows a live indicator (rendering / idle /
|
|
289
291
|
connected); **Remove** undoes it.
|
|
@@ -331,7 +333,7 @@ daemon any time with **Ctrl+C**.
|
|
|
331
333
|
No other local-LLM app lets you run **whatever inference engine you want**. TurboLLM treats
|
|
332
334
|
the engine as a swappable component.
|
|
333
335
|
|
|
334
|
-
**Add a custom engine** (Engines screen → **Add engine**):
|
|
336
|
+
**Add a custom engine** (Engines screen → **Add your own engine**):
|
|
335
337
|
|
|
336
338
|
1. Compile or download any `llama-server`-compatible binary — stock
|
|
337
339
|
[llama.cpp](https://github.com/ggml-org/llama.cpp), a community fork, or your own build.
|
|
@@ -396,13 +398,14 @@ any OpenAI- or Anthropic-compatible tool (Open WebUI, Kilo Code, opencode, …).
|
|
|
396
398
|
## Use it from any device on your network
|
|
397
399
|
|
|
398
400
|
The UI runs in the browser, so any phone, tablet, or laptop on your LAN can use the model on
|
|
399
|
-
your GPU box
|
|
401
|
+
your GPU box — every screen is responsive down to phone widths, with the desktop layout
|
|
402
|
+
untouched:
|
|
400
403
|
|
|
401
404
|
```bash
|
|
402
405
|
turbollm --addr 0.0.0.0:6996 # bind all interfaces, then open http://<your-ip>:6996
|
|
403
406
|
```
|
|
404
407
|
|
|
405
|
-
Turn on **Require API key** in Settings → Network when you expose it.
|
|
408
|
+
Turn on **Require API key** in Settings → Network & sharing when you expose it.
|
|
406
409
|
|
|
407
410
|
---
|
|
408
411
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/agents/distiller.ts
|
|
4
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
|
|
7
|
+
// src/engines/compat.ts
|
|
8
|
+
function engineAcceptsFormat(engineKind, format) {
|
|
9
|
+
if (engineKind === "mlx") return format === "mlx";
|
|
10
|
+
if (engineKind === "vllm") return format === "mlx";
|
|
11
|
+
return format === "gguf";
|
|
12
|
+
}
|
|
13
|
+
var ENGINE_MODEL_ALIAS = "default_model";
|
|
14
|
+
function engineModelAlias(engineKind) {
|
|
15
|
+
return engineKind === "mlx" || engineKind === "vllm" || engineKind === "sglang" ? ENGINE_MODEL_ALIAS : null;
|
|
16
|
+
}
|
|
17
|
+
var VENDOR_DISPLAY = {
|
|
18
|
+
nvidia: "NVIDIA",
|
|
19
|
+
amd: "AMD",
|
|
20
|
+
intel: "Intel",
|
|
21
|
+
apple: "Apple Silicon",
|
|
22
|
+
unknown: "no detected GPU"
|
|
23
|
+
};
|
|
24
|
+
var PLATFORM_DISPLAY = {
|
|
25
|
+
darwin: "macOS",
|
|
26
|
+
win32: "Windows",
|
|
27
|
+
linux: "Linux"
|
|
28
|
+
};
|
|
29
|
+
function platformName(p) {
|
|
30
|
+
return PLATFORM_DISPLAY[p] ?? p;
|
|
31
|
+
}
|
|
32
|
+
function platformReason(allowed) {
|
|
33
|
+
const names = allowed.map(platformName);
|
|
34
|
+
const joined = names.length <= 1 ? names.join("") : `${names.slice(0, -1).join(", ")} & ${names[names.length - 1]}`;
|
|
35
|
+
return `${joined} only`;
|
|
36
|
+
}
|
|
37
|
+
function evaluateVariant(p, r) {
|
|
38
|
+
if (r.platform && !r.platform.includes(p.platform)) {
|
|
39
|
+
return { ok: false, reason: platformReason(r.platform) };
|
|
40
|
+
}
|
|
41
|
+
if (r.arch && !r.arch.includes(p.arch)) {
|
|
42
|
+
return { ok: false, reason: `Needs ${r.arch.join(" / ")}` };
|
|
43
|
+
}
|
|
44
|
+
if (r.gpuVendor && !r.gpuVendor.includes(p.gpuVendor)) {
|
|
45
|
+
const needs = r.gpuVendor.map((v) => VENDOR_DISPLAY[v]).join(" / ");
|
|
46
|
+
const article = /^[NAEIO]/.test(needs) ? "an" : "a";
|
|
47
|
+
return { ok: false, reason: `Needs ${article} ${needs} GPU \u2014 you have ${VENDOR_DISPLAY[p.gpuVendor]}` };
|
|
48
|
+
}
|
|
49
|
+
if (r.minVramMb !== void 0 && p.vramMb > 0 && p.vramMb < r.minVramMb) {
|
|
50
|
+
const gb = Math.round(r.minVramMb / 1024);
|
|
51
|
+
return { ok: false, reason: `Needs ~${gb} GB VRAM` };
|
|
52
|
+
}
|
|
53
|
+
return { ok: true };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/agents/distiller.ts
|
|
57
|
+
var DISTILL_SYSTEM = `You write a single reusable SKILL from the material provided. A skill captures a repeatable approach so it can be applied to similar future tasks.
|
|
58
|
+
|
|
59
|
+
Output a JSON object: {"name": <short kebab-case id>, "description": <one sentence: when to use this skill>, "procedure": <concise step-by-step procedure, markdown>}
|
|
60
|
+
|
|
61
|
+
Rules:
|
|
62
|
+
- The skill must be GENERAL and reusable, not a one-off answer to this specific input.
|
|
63
|
+
- "name" is a short kebab-case identifier (e.g. "csv-summary", "extract-config-values").
|
|
64
|
+
- "description" is one sentence describing WHEN to apply it.
|
|
65
|
+
- "procedure" is a concise, numbered set of steps \u2014 the how.
|
|
66
|
+
- If the material does not contain a clear reusable approach, output {"name": null, "description": null, "procedure": null}.
|
|
67
|
+
Respond with ONLY the JSON object, nothing else.`;
|
|
68
|
+
async function distill(d, material) {
|
|
69
|
+
const ms = d.manager.status();
|
|
70
|
+
const target = d.manager.target();
|
|
71
|
+
if (ms.state !== "running" || !ms.model || !target) return { name: null, description: null, procedure: null };
|
|
72
|
+
const model = engineModelAlias(d.registry.active()?.kind ?? "") ?? ms.model.key;
|
|
73
|
+
try {
|
|
74
|
+
const res = await fetch(`${target}/v1/chat/completions`, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: { "Content-Type": "application/json" },
|
|
77
|
+
body: JSON.stringify({
|
|
78
|
+
model,
|
|
79
|
+
messages: [
|
|
80
|
+
{ role: "system", content: DISTILL_SYSTEM },
|
|
81
|
+
{ role: "user", content: `Material:
|
|
82
|
+
|
|
83
|
+
${material.slice(0, 24e3)}
|
|
84
|
+
|
|
85
|
+
Write ONE reusable skill as raw JSON \u2014 no markdown fences.` }
|
|
86
|
+
],
|
|
87
|
+
temperature: 0.3,
|
|
88
|
+
max_tokens: 800,
|
|
89
|
+
reasoning_budget: 0,
|
|
90
|
+
chat_template_kwargs: { enable_thinking: false },
|
|
91
|
+
response_format: { type: "json_object" },
|
|
92
|
+
stream: false
|
|
93
|
+
}),
|
|
94
|
+
signal: AbortSignal.timeout(9e4)
|
|
95
|
+
});
|
|
96
|
+
if (!res.ok) return { name: null, description: null, procedure: null };
|
|
97
|
+
const data = await res.json();
|
|
98
|
+
let txt = data.choices?.[0]?.message?.content ?? "";
|
|
99
|
+
txt = txt.replace(/<think>[\s\S]*?<\/think>/gi, "").replace(/```json|```/gi, "").trim();
|
|
100
|
+
const m = txt.match(/\{[\s\S]*\}/);
|
|
101
|
+
if (!m) return { name: null, description: null, procedure: null };
|
|
102
|
+
const j = JSON.parse(m[0]);
|
|
103
|
+
const valid = (s) => typeof s === "string" && s.trim() && s.toLowerCase() !== "null" ? s.trim() : null;
|
|
104
|
+
const name = valid(j.name);
|
|
105
|
+
const procedure = valid(j.procedure);
|
|
106
|
+
if (!name || !procedure) return { name: null, description: null, procedure: null };
|
|
107
|
+
return { name: name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64), description: valid(j.description) ?? "", procedure };
|
|
108
|
+
} catch {
|
|
109
|
+
return { name: null, description: null, procedure: null };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function distillFromConversation(d, transcript) {
|
|
113
|
+
const text = transcript.map((m) => `${m.role.toUpperCase()}: ${m.content}`).join("\n");
|
|
114
|
+
return distill(d, `A successful completed task:
|
|
115
|
+
|
|
116
|
+
${text}`);
|
|
117
|
+
}
|
|
118
|
+
var FOLDER_TEXT = /\.(md|txt|json|ya?ml|csv|js|ts|py|sh|html?|css)$/i;
|
|
119
|
+
function collectFolderFiles(folder, max) {
|
|
120
|
+
const files = [];
|
|
121
|
+
const walk = (dir, depth) => {
|
|
122
|
+
if (depth > 4 || files.length >= max) return;
|
|
123
|
+
let entries;
|
|
124
|
+
try {
|
|
125
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
126
|
+
} catch {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
for (const e of entries) {
|
|
130
|
+
if (files.length >= max) return;
|
|
131
|
+
const p = join(dir, e.name);
|
|
132
|
+
if (e.isDirectory()) {
|
|
133
|
+
walk(p, depth + 1);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (!FOLDER_TEXT.test(e.name)) continue;
|
|
137
|
+
try {
|
|
138
|
+
if (statSync(p).size > 2e5) continue;
|
|
139
|
+
const content = readFileSync(p, "utf8").slice(0, 6e3).trim();
|
|
140
|
+
if (content) files.push({ name: e.name, content });
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
walk(folder, 0);
|
|
146
|
+
return files;
|
|
147
|
+
}
|
|
148
|
+
async function distillSkillsFromFolder(d, folder, opts = {}) {
|
|
149
|
+
if (!existsSync(folder) || !statSync(folder).isDirectory()) return [];
|
|
150
|
+
const max = opts.max ?? 12;
|
|
151
|
+
const files = collectFolderFiles(folder, max);
|
|
152
|
+
if (!files.length) return [];
|
|
153
|
+
const out = [];
|
|
154
|
+
const seen = /* @__PURE__ */ new Set();
|
|
155
|
+
for (let i = 0; i < files.length; i++) {
|
|
156
|
+
const f = files[i];
|
|
157
|
+
opts.onProgress?.(i, files.length, f.name);
|
|
158
|
+
const s = await distill(d, `A reusable approach captured in "${f.name}":
|
|
159
|
+
|
|
160
|
+
${f.content}`);
|
|
161
|
+
if (s.name && s.procedure && !seen.has(s.name)) {
|
|
162
|
+
seen.add(s.name);
|
|
163
|
+
out.push(s);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
engineAcceptsFormat,
|
|
171
|
+
engineModelAlias,
|
|
172
|
+
evaluateVariant,
|
|
173
|
+
distillFromConversation,
|
|
174
|
+
distillSkillsFromFolder
|
|
175
|
+
};
|