turbollm 1.5.0 → 1.5.2
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 +9 -0
- package/dist/cli.js +71 -43
- package/dist/webdist/assets/AgentsScreen-BISmGjbo.js +2 -0
- package/dist/webdist/assets/ChatScreen-CdGovrD_.js +57 -0
- package/dist/webdist/assets/{CustomizeScreen-BvwLXcaF.js → CustomizeScreen-vCdaeaYm.js} +1 -1
- package/dist/webdist/assets/{DeveloperScreen-Cf_BjtjZ.js → DeveloperScreen-DTDECTfQ.js} +1 -1
- package/dist/webdist/assets/{EnginesScreen-C-ny7PFc.js → EnginesScreen-BLqB4B4s.js} +3 -3
- package/dist/webdist/assets/ModelDetailDialog-CmcauyUd.js +1 -0
- package/dist/webdist/assets/{ModelDirs-V6R_2wmI.js → ModelDirs-CVl421kH.js} +1 -1
- package/dist/webdist/assets/ModelsScreen-BsjQ-rXT.js +1 -0
- package/dist/webdist/assets/SettingsScreen-DZQ1lIIa.js +1 -0
- package/dist/webdist/assets/{arc-C-BB3uXs.js → arc-BOg2ZUVv.js} +1 -1
- package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-CcJ0ZOqZ.js → architectureDiagram-3BPJPVTR-JA6U-k6V.js} +1 -1
- package/dist/webdist/assets/{blockDiagram-GPEHLZMM-ALJWfOwg.js → blockDiagram-GPEHLZMM-CeFKl5gt.js} +1 -1
- package/dist/webdist/assets/{c4Diagram-AAUBKEIU-BaIxyDOO.js → c4Diagram-AAUBKEIU-NAYIvLue.js} +1 -1
- package/dist/webdist/assets/channel-R-Cc4MxS.js +1 -0
- package/dist/webdist/assets/check-BEf12PVV.js +1 -0
- package/dist/webdist/assets/{chunk-2J33WTMH-EWXF5El7.js → chunk-2J33WTMH-CZAFUB1n.js} +1 -1
- package/dist/webdist/assets/{chunk-4BX2VUAB-B01XIIl5.js → chunk-4BX2VUAB-BwDeI7tm.js} +1 -1
- package/dist/webdist/assets/{chunk-55IACEB6-CAORK4LB.js → chunk-55IACEB6-CrMcwFFf.js} +1 -1
- package/dist/webdist/assets/{chunk-727SXJPM-Bow98gCW.js → chunk-727SXJPM-B8pvS-2b.js} +1 -1
- package/dist/webdist/assets/{chunk-AQP2D5EJ-D3ihkUfL.js → chunk-AQP2D5EJ-4nG2xg8B.js} +1 -1
- package/dist/webdist/assets/{chunk-FMBD7UC4-B3pZV5W8.js → chunk-FMBD7UC4-4dS81TVH.js} +1 -1
- package/dist/webdist/assets/{chunk-ND2GUHAM-Bpfzzi3w.js → chunk-ND2GUHAM-BOND_2fr.js} +1 -1
- package/dist/webdist/assets/{chunk-QZHKN3VN-BAr49pW1.js → chunk-QZHKN3VN-uo5AhgZ_.js} +1 -1
- package/dist/webdist/assets/{circle-check-Bxq6YJMS.js → circle-check-BM1U1LBh.js} +1 -1
- package/dist/webdist/assets/{circle-x-DEBqweG-.js → circle-x-BQVgQDWt.js} +1 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-Bo30oFs_.js +1 -0
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-Bo30oFs_.js +1 -0
- package/dist/webdist/assets/{common-BSLr1MvR.js → common-CREyTFTu.js} +1 -1
- package/dist/webdist/assets/{copy-button-SltJUQtH.js → copy-button-CCE6xN9X.js} +1 -1
- package/dist/webdist/assets/{cose-bilkent-S5V4N54A-BEX523vE.js → cose-bilkent-S5V4N54A-C2Hznw_T.js} +1 -1
- package/dist/webdist/assets/{dagre-BM42HDAG-De5xizkB.js → dagre-BM42HDAG-DfgRJcxi.js} +1 -1
- package/dist/webdist/assets/{diagram-2AECGRRQ-Dlcd7uUE.js → diagram-2AECGRRQ-B3NKQXs-.js} +1 -1
- package/dist/webdist/assets/{diagram-5GNKFQAL-CflK9di5.js → diagram-5GNKFQAL-CsddCSIK.js} +1 -1
- package/dist/webdist/assets/{diagram-KO2AKTUF-Fbl7G2bU.js → diagram-KO2AKTUF-B-0Scyhf.js} +1 -1
- package/dist/webdist/assets/{diagram-LMA3HP47-BC3FH92a.js → diagram-LMA3HP47-zZMoAxon.js} +1 -1
- package/dist/webdist/assets/{diagram-OG6HWLK6-DkL3gfhn.js → diagram-OG6HWLK6-DkhKqVoh.js} +1 -1
- package/dist/webdist/assets/{dialog-D1-Zlj1l.js → dialog-Bu_dNoGl.js} +1 -1
- package/dist/webdist/assets/{erDiagram-TEJ5UH35-DGD3ClXg.js → erDiagram-TEJ5UH35-N3kX36Fb.js} +1 -1
- package/dist/webdist/assets/{flowDiagram-I6XJVG4X-CHzMVGFr.js → flowDiagram-I6XJVG4X-MBmSjZLW.js} +1 -1
- package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-CAB-ravM.js → ganttDiagram-6RSMTGT7-BemcAo3u.js} +1 -1
- package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-DmhgBW8a.js → gitGraphDiagram-PVQCEYII-C3Z2MeCR.js} +1 -1
- package/dist/webdist/assets/html2canvas.esm-DXEQVQnt.js +5 -0
- package/dist/webdist/assets/{index-B7LTDUDR.js → index-C07fQlaV.js} +3 -3
- package/dist/webdist/assets/index-FmTuHPCZ.css +1 -0
- package/dist/webdist/assets/{infoDiagram-5YYISTIA-BswUlelU.js → infoDiagram-5YYISTIA-CJ9bQKM4.js} +1 -1
- package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-DZENhuS5.js → ishikawaDiagram-YF4QCWOH-DAU8qHuy.js} +1 -1
- package/dist/webdist/assets/{journeyDiagram-JHISSGLW-D4BKn3fd.js → journeyDiagram-JHISSGLW-DYWLdObC.js} +1 -1
- package/dist/webdist/assets/{kanban-definition-UN3LZRKU-C4uVyQVS.js → kanban-definition-UN3LZRKU-SRRfAaYQ.js} +1 -1
- package/dist/webdist/assets/{linear-BwE47XoP.js → linear-Cn3E4T4R.js} +1 -1
- package/dist/webdist/assets/{mermaid.core-DdEuSeXx.js → mermaid.core-D0LNV0aO.js} +4 -4
- package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-DxPkmqjP.js → mindmap-definition-RKZ34NQL-4OzvUsMs.js} +1 -1
- package/dist/webdist/assets/{pencil-BavbRBL9.js → pencil-Ds4UYumb.js} +1 -1
- package/dist/webdist/assets/personas-C4oTeImA.js +280 -0
- package/dist/webdist/assets/{pieDiagram-4H26LBE5-CUdk_VXR.js → pieDiagram-4H26LBE5-DnUMRqxT.js} +1 -1
- package/dist/webdist/assets/{plus-BEY4GU1K.js → plus-3UMxyIEk.js} +1 -1
- package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-MT8mFTQj.js → quadrantDiagram-W4KKPZXB-CuGEtBna.js} +1 -1
- package/dist/webdist/assets/{radix-DX0vbZxT.js → radix-4XwKfE88.js} +1 -1
- package/dist/webdist/assets/{refresh-cw-B2qwQZOz.js → refresh-cw-kd3J_RQJ.js} +1 -1
- package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-C5lcbiaL.js → requirementDiagram-4Y6WPE33-D6JrM-I-.js} +1 -1
- package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-gCtNl8Ay.js → sankeyDiagram-5OEKKPKP-7SVXztDZ.js} +1 -1
- package/dist/webdist/assets/{save-B5yXIJpT.js → save-CTNv_c8L.js} +1 -1
- package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-CZm2Qqlo.js → sequenceDiagram-3UESZ5HK-DBPkd_Mz.js} +1 -1
- package/dist/webdist/assets/skeleton-D4pn0jcp.js +1 -0
- package/dist/webdist/assets/{stateDiagram-AJRCARHV-BBf-D1_B.js → stateDiagram-AJRCARHV-CeJn57NH.js} +1 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-64xpB2-o.js +1 -0
- package/dist/webdist/assets/{timeline-definition-PNZ67QCA-pZHclsB5.js → timeline-definition-PNZ67QCA-4wGzSbT-.js} +1 -1
- package/dist/webdist/assets/{trash-2-BOk5wJqV.js → trash-2-Ct_313d6.js} +1 -1
- package/dist/webdist/assets/{vendor-Bhj2wMgn.js → vendor-CKtmMAvG.js} +1 -1
- package/dist/webdist/assets/{vennDiagram-CIIHVFJN-D0G0bNDg.js → vennDiagram-CIIHVFJN-C07Iks1t.js} +1 -1
- package/dist/webdist/assets/{wardley-L42UT6IY-CVfbre9P.js → wardley-L42UT6IY-CkjQxAi3.js} +1 -1
- package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-DpJ-M6Rq.js → wardleyDiagram-YWT4CUSO-CrfLbu_T.js} +1 -1
- package/dist/webdist/assets/{wrench-B_UtGGtF.js → wrench-CBGqccQu.js} +1 -1
- package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-D1Ye9Jsh.js → xychartDiagram-2RQKCTM6-VlKQgaXq.js} +1 -1
- package/dist/webdist/index.html +4 -4
- package/package.json +1 -1
- package/dist/webdist/assets/AgentsScreen-C8ijrgPm.js +0 -2
- package/dist/webdist/assets/ChatScreen-BLAaAAkO.js +0 -48
- package/dist/webdist/assets/ModelDetailDialog-64XpCDO6.js +0 -1
- package/dist/webdist/assets/ModelsScreen-D8EkblWt.js +0 -1
- package/dist/webdist/assets/SettingsScreen-khqKELRg.js +0 -1
- package/dist/webdist/assets/channel-xWwBhwfN.js +0 -1
- package/dist/webdist/assets/check-Dg0H4Og5.js +0 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-CWFRccJj.js +0 -1
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-CWFRccJj.js +0 -1
- package/dist/webdist/assets/index-DnCwNfT4.css +0 -1
- package/dist/webdist/assets/personas-uLVxW5Bm.js +0 -77
- package/dist/webdist/assets/skeleton-CrqR3g7t.js +0 -1
- package/dist/webdist/assets/sparkles-DlKKkTEa.js +0 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-DG-bUrT1.js +0 -1
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
<img src="https://img.shields.io/badge/node-%E2%89%A522-3c873a.svg" alt="node >= 22" />
|
|
18
18
|
<img src="https://img.shields.io/badge/license-FSL--1.1--ALv2-blue.svg" alt="license" />
|
|
19
19
|
<img src="https://img.shields.io/badge/platform-Windows%20%C2%B7%20macOS%20%C2%B7%20Linux-555.svg" alt="platforms" />
|
|
20
|
+
<a href="https://ko-fi.com/mohitsoni"><img src="https://img.shields.io/badge/Ko--fi-support%20us-FF5E5B?logo=kofi&logoColor=white" alt="Ko-fi" /></a>
|
|
21
|
+
<a href="https://discord.gg/v6kRbV7nC"><img src="https://img.shields.io/badge/Discord-join%20chat-5865F2?logo=discord&logoColor=white" alt="Discord" /></a>
|
|
20
22
|
</p>
|
|
21
23
|
|
|
22
24
|
<!-- Brand: shipped app icon web/public/brand/turbollm-icon-512.jpeg · high-res masters web/brand-assets/ (unshipped) · in-app mark web/src/components/Logo.tsx · favicon web/public/favicon.svg -->
|
|
@@ -51,6 +53,7 @@ LLMs** — built for people who today hand-compile forks and hunt forums for the
|
|
|
51
53
|
- [How TurboLLM compares](#how-turbollm-compares)
|
|
52
54
|
- [Troubleshooting](#troubleshooting)
|
|
53
55
|
- [Develop from source](#develop-from-source)
|
|
56
|
+
- [Community](#community)
|
|
54
57
|
- [License](#license)
|
|
55
58
|
|
|
56
59
|
---
|
|
@@ -482,6 +485,12 @@ shadcn/ui frontend. One TypeScript codebase, shipped as an npm package.
|
|
|
482
485
|
|
|
483
486
|
---
|
|
484
487
|
|
|
488
|
+
## Community
|
|
489
|
+
|
|
490
|
+
Questions, ideas, and show-and-tell — join the [Discord](https://discord.gg/v6kRbV7nC).
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
485
494
|
## License
|
|
486
495
|
|
|
487
496
|
Source-available under the **Functional Source License 1.1 (Apache-2.0 future grant)** — SPDX
|
package/dist/cli.js
CHANGED
|
@@ -658,9 +658,24 @@ async function ensureMlxEnv(root, onProgress, upgrade = false) {
|
|
|
658
658
|
const uv = await ensureUv(root, onProgress);
|
|
659
659
|
const envDir = join3(root, "mlx", "venv");
|
|
660
660
|
const py = venvPython(envDir);
|
|
661
|
+
if (existsSync3(py)) {
|
|
662
|
+
let compatible = false;
|
|
663
|
+
try {
|
|
664
|
+
const { stdout } = await execFileP2(py, ["--version"], { timeout: 5e3 });
|
|
665
|
+
const m = stdout.match(/Python 3\.(\d+)/);
|
|
666
|
+
if (m) {
|
|
667
|
+
const minor = Number(m[1]);
|
|
668
|
+
compatible = minor >= 10 && minor <= 13;
|
|
669
|
+
}
|
|
670
|
+
} catch {
|
|
671
|
+
}
|
|
672
|
+
if (!compatible) rmSync2(envDir, { recursive: true, force: true });
|
|
673
|
+
}
|
|
661
674
|
if (!existsSync3(py)) {
|
|
662
675
|
onProgress?.({ phase: "extracting", pct: -1 });
|
|
663
|
-
|
|
676
|
+
const PINNED_PYTHON = "cpython-3.12-macos-aarch64";
|
|
677
|
+
const venvArgs = ["venv", "--python", PINNED_PYTHON, ...existsSync3(envDir) ? ["--clear"] : [], envDir];
|
|
678
|
+
await execFileP2(uv, venvArgs, { cwd: root });
|
|
664
679
|
}
|
|
665
680
|
onProgress?.({ phase: "extracting", pct: -1 });
|
|
666
681
|
const installArgs = ["pip", "install", "--python", py, ...upgrade ? ["--upgrade"] : [], "mlx-lm"];
|
|
@@ -3661,6 +3676,8 @@ function profileToArgs(p, m, caps, cores = 0) {
|
|
|
3661
3676
|
const threads = p.threads > 0 ? p.threads : cores > 0 ? Math.max(1, Math.floor(cores / 2)) : 0;
|
|
3662
3677
|
if (threads > 0) a.push("--threads", String(threads));
|
|
3663
3678
|
if (p.threadsBatch > 0) a.push("--threads-batch", String(p.threadsBatch));
|
|
3679
|
+
if (p.batchSize && p.batchSize > 0 && has("--batch-size")) a.push("--batch-size", String(p.batchSize));
|
|
3680
|
+
if (p.uBatchSize && p.uBatchSize > 0 && has("--ubatch-size")) a.push("--ubatch-size", String(p.uBatchSize));
|
|
3664
3681
|
if (m.vision && p.useMmproj && m.mmprojPath) a.push("--mmproj", m.mmprojPath);
|
|
3665
3682
|
if (m.vision && p.useMmproj && !p.mmprojGpu && has("--no-mmproj-offload")) a.push("--no-mmproj-offload");
|
|
3666
3683
|
if (p.imageMaxTokens > 0 && has("--image-max-tokens")) a.push("--image-max-tokens", String(p.imageMaxTokens));
|
|
@@ -4324,7 +4341,7 @@ var DownloadManager = class {
|
|
|
4324
4341
|
const u = input.url.trim();
|
|
4325
4342
|
if (!/^https?:\/\//i.test(u)) throw new DownloadError("invalid_url", "URL must start with http:// or https://.");
|
|
4326
4343
|
const path = safePathname(u);
|
|
4327
|
-
if (!subdir && !/\.gguf$/i.test(path) && !HF_BLOB_RE.test(u)) {
|
|
4344
|
+
if (!subdir && !/\.gguf$/i.test(path) && !HF_BLOB_RE.test(u.split("?")[0])) {
|
|
4328
4345
|
throw new DownloadError("invalid_url", "URL must point to a .gguf file.");
|
|
4329
4346
|
}
|
|
4330
4347
|
filename = basename2(path);
|
|
@@ -4767,6 +4784,9 @@ var BenchRunner = class {
|
|
|
4767
4784
|
// stop/restart/load (kill switches) interrupts auto-tune immediately rather than
|
|
4768
4785
|
// waiting out the current candidate's request.
|
|
4769
4786
|
abort = null;
|
|
4787
|
+
runLog = [];
|
|
4788
|
+
runLogMeta = null;
|
|
4789
|
+
logWinner = null;
|
|
4770
4790
|
// The finished run's winning candidate, held (not persisted) until the user clicks Save.
|
|
4771
4791
|
winning = null;
|
|
4772
4792
|
/** Live state for GET /status. */
|
|
@@ -4805,6 +4825,24 @@ var BenchRunner = class {
|
|
|
4805
4825
|
const deadline = Date.now() + timeoutMs;
|
|
4806
4826
|
while (this.state.running && Date.now() < deadline) await sleep2(150);
|
|
4807
4827
|
}
|
|
4828
|
+
getLog() {
|
|
4829
|
+
if (!this.runLogMeta) return null;
|
|
4830
|
+
const { modelKey, startedAt: startedAt2, sys } = this.runLogMeta;
|
|
4831
|
+
return {
|
|
4832
|
+
modelKey,
|
|
4833
|
+
startedAt: startedAt2,
|
|
4834
|
+
hardware: {
|
|
4835
|
+
gpus: sys.gpus.map((g) => ({ name: g.name, vramMb: g.vramMb })),
|
|
4836
|
+
ramMb: sys.ramMB,
|
|
4837
|
+
os: sys.os
|
|
4838
|
+
},
|
|
4839
|
+
entries: [...this.runLog],
|
|
4840
|
+
winner: this.logWinner
|
|
4841
|
+
};
|
|
4842
|
+
}
|
|
4843
|
+
emit(step, candidate) {
|
|
4844
|
+
this.runLog.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), step, ...candidate ? { candidate } : {} });
|
|
4845
|
+
}
|
|
4808
4846
|
/** Start a run for `modelKey`. Rejects (throws BenchError) when a run is already
|
|
4809
4847
|
* active, the engine is busy, or the model isn't a benchmarkable GGUF. The run
|
|
4810
4848
|
* itself proceeds in the background; callers get 202 + poll /status. */
|
|
@@ -4826,6 +4864,9 @@ var BenchRunner = class {
|
|
|
4826
4864
|
this.cancelled = false;
|
|
4827
4865
|
this.abort = new AbortController();
|
|
4828
4866
|
this.winning = null;
|
|
4867
|
+
this.runLog = [];
|
|
4868
|
+
this.runLogMeta = null;
|
|
4869
|
+
this.logWinner = null;
|
|
4829
4870
|
this.deadline = Date.now() + TOTAL_BUDGET_MS;
|
|
4830
4871
|
this.state = { running: true, modelKey, step: "Preparing\u2026", candidates: [] };
|
|
4831
4872
|
void this.run(modelKey, entry, base2).catch((e) => {
|
|
@@ -4837,6 +4878,7 @@ var BenchRunner = class {
|
|
|
4837
4878
|
// ---- the run ------------------------------------------------------------
|
|
4838
4879
|
async run(modelKey, entry, base2) {
|
|
4839
4880
|
const sys = getSysInfo();
|
|
4881
|
+
this.runLogMeta = { modelKey, startedAt: (/* @__PURE__ */ new Date()).toISOString(), sys };
|
|
4840
4882
|
const active = this.registry.active();
|
|
4841
4883
|
const caps = active?.capabilities ?? { flags: [], kvTypes: [] };
|
|
4842
4884
|
const saved = this.store.snapshot().modelProfiles[modelKey];
|
|
@@ -4852,6 +4894,7 @@ var BenchRunner = class {
|
|
|
4852
4894
|
kvToTune = decideKvToBench(spread, kvCandidates);
|
|
4853
4895
|
const swing = spread < 0 ? "unknown" : spread >= Number.MAX_SAFE_INTEGER ? "huge" : `${Math.round(spread)} MB`;
|
|
4854
4896
|
this.state = { ...this.state, step: `KV cache swing ${swing} \u2192 tuning ${kvToTune.join(" + ")}`, candidates: results };
|
|
4897
|
+
this.emit(`KV spread ${swing} \u2192 tuning ${kvToTune.join(" + ")}`);
|
|
4855
4898
|
}
|
|
4856
4899
|
for (let i = 0; i < kvToTune.length && !this.cancelled && Date.now() <= this.deadline; i++) {
|
|
4857
4900
|
const kv = kvToTune[i];
|
|
@@ -4875,6 +4918,7 @@ var BenchRunner = class {
|
|
|
4875
4918
|
}
|
|
4876
4919
|
const profile = recommended && hasAnySampling(recommended) ? { ...best.profile, sampling: { ...best.profile.sampling, ...recommended } } : best.profile;
|
|
4877
4920
|
this.winning = { modelKey, profile, cand: best.cand, entry, sys, engineVersion: active?.version ?? "" };
|
|
4921
|
+
this.logWinner = { params: best.cand.params, tps: best.cand.tps ?? 0, prefillTps: best.cand.prefillTps, ttftMs: best.cand.ttftMs ?? 0, vramMb: best.cand.vramMb };
|
|
4878
4922
|
this.state = {
|
|
4879
4923
|
running: false,
|
|
4880
4924
|
modelKey,
|
|
@@ -5021,7 +5065,7 @@ var BenchRunner = class {
|
|
|
5021
5065
|
/** Record a VRAM-probe trial in the candidate list (tps/prefill are null — nothing was generated;
|
|
5022
5066
|
* only the load outcome and absolute VRAM are known). */
|
|
5023
5067
|
pushProbe(results, base2, knob, value, probe2) {
|
|
5024
|
-
|
|
5068
|
+
const probeCand = {
|
|
5025
5069
|
label: `probe ${knob}=${value}`,
|
|
5026
5070
|
params: {
|
|
5027
5071
|
ctx: base2.ctx,
|
|
@@ -5037,7 +5081,9 @@ var BenchRunner = class {
|
|
|
5037
5081
|
ttftMs: null,
|
|
5038
5082
|
vramMb: null,
|
|
5039
5083
|
vramAbsMb: probe2.vramAbsMb
|
|
5040
|
-
}
|
|
5084
|
+
};
|
|
5085
|
+
results.push(probeCand);
|
|
5086
|
+
this.emit(`${probeCand.label} \u2192 ${probe2.outcome}${probe2.vramAbsMb != null ? ` (${probe2.vramAbsMb} MB)` : ""}`, probeCand);
|
|
5041
5087
|
this.state = { ...this.state, candidates: results };
|
|
5042
5088
|
}
|
|
5043
5089
|
/** Phase 2: the single full prefill + t/s benchmark, at the offload the VRAM probe chose. Pushes
|
|
@@ -5046,6 +5092,7 @@ var BenchRunner = class {
|
|
|
5046
5092
|
this.state = { ...this.state, step: `KV ${profile.kvTypeK}: measuring best (${label})\u2026`, candidates: results };
|
|
5047
5093
|
const cand = await this.measure(entry, sys, profile, caps, label, `Measuring ${label}`);
|
|
5048
5094
|
results.push(cand);
|
|
5095
|
+
this.emit(`bench ${label} \u2192 ${cand.outcome}${cand.tps != null ? ` (${cand.tps.toFixed(1)} tok/s)` : ""}`, cand);
|
|
5049
5096
|
this.state = { ...this.state, candidates: results, bestTps: cand.tps ?? this.state.bestTps };
|
|
5050
5097
|
await this.settleGpu();
|
|
5051
5098
|
return cand.outcome === "ok" && cand.tps !== null ? { cand, profile } : null;
|
|
@@ -7363,6 +7410,7 @@ var AgentRunner = class {
|
|
|
7363
7410
|
acs = /* @__PURE__ */ new Map();
|
|
7364
7411
|
queue = [];
|
|
7365
7412
|
active = null;
|
|
7413
|
+
interruptedIds = /* @__PURE__ */ new Set();
|
|
7366
7414
|
constructor(d) {
|
|
7367
7415
|
this.d = d;
|
|
7368
7416
|
}
|
|
@@ -7440,11 +7488,13 @@ var AgentRunner = class {
|
|
|
7440
7488
|
this.d.db.updateAgentRun?.(pending.id, { status: "done", endedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7441
7489
|
} catch (e) {
|
|
7442
7490
|
const isAbort = e?.name === "AbortError";
|
|
7443
|
-
this.
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7491
|
+
if (!this.interruptedIds.delete(pending.id)) {
|
|
7492
|
+
this.d.db.updateAgentRun?.(pending.id, {
|
|
7493
|
+
status: isAbort ? "cancelled" : "failed",
|
|
7494
|
+
error: isAbort ? void 0 : e.message,
|
|
7495
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7496
|
+
});
|
|
7497
|
+
}
|
|
7448
7498
|
} finally {
|
|
7449
7499
|
this.finish(pending.id);
|
|
7450
7500
|
}
|
|
@@ -7453,6 +7503,10 @@ var AgentRunner = class {
|
|
|
7453
7503
|
this.emitters.get(id)?.emit("done");
|
|
7454
7504
|
this.acs.delete(id);
|
|
7455
7505
|
this.active = null;
|
|
7506
|
+
setTimeout(() => {
|
|
7507
|
+
this.buffers.delete(id);
|
|
7508
|
+
this.emitters.delete(id);
|
|
7509
|
+
}, 5e3);
|
|
7456
7510
|
void this.processNext();
|
|
7457
7511
|
}
|
|
7458
7512
|
cancel(id) {
|
|
@@ -7473,6 +7527,7 @@ var AgentRunner = class {
|
|
|
7473
7527
|
/** Called by the engine manager when the active model is evicted. */
|
|
7474
7528
|
interruptActive() {
|
|
7475
7529
|
if (!this.active) return;
|
|
7530
|
+
this.interruptedIds.add(this.active);
|
|
7476
7531
|
this.acs.get(this.active)?.abort();
|
|
7477
7532
|
this.d.db.updateAgentRun?.(this.active, { status: "interrupted", endedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7478
7533
|
}
|
|
@@ -8021,30 +8076,6 @@ function abortAllInFlightChats() {
|
|
|
8021
8076
|
inflight.clear();
|
|
8022
8077
|
return n;
|
|
8023
8078
|
}
|
|
8024
|
-
var EXPERT_SYSTEM_PROMPT = `You are the TurboLLM in-app expert assistant \u2014 a knowledgeable, friendly guide built into TurboLLM, a local-first desktop app for running large language models on the user's own machine.
|
|
8025
|
-
|
|
8026
|
-
Your job is to help the user get the most out of TurboLLM:
|
|
8027
|
-
- Explain what TurboLLM features do and how to use them: chatting with local models, the Models screen (discover, download, load, and tune models), the Engines screen (install and manage inference backends like llama.cpp), and Settings (idle timeout, model defaults such as context length and GPU layers, auto-load, theme, network/LAN exposure, and privacy/telemetry).
|
|
8028
|
-
- Help the user configure things: picking and loading a model, adjusting sampling (temperature, top-p, top-k, min-p), setting context length and GPU offload, and managing per-thread system prompts and sampling overrides.
|
|
8029
|
-
- Troubleshoot common problems: a model that won't load, slow generation, running out of context, missing or failed engine installs, and GPU/CPU offload questions. Reason from the symptoms the user describes and the hardware they mention.
|
|
8030
|
-
|
|
8031
|
-
Guidelines:
|
|
8032
|
-
- Keep answers practical, concise, and actionable. Prefer concrete steps ("Open the Models screen, click \u2026") over abstract advice.
|
|
8033
|
-
- When something depends on the user's hardware or which model is loaded, say so and ask a brief clarifying question if needed.
|
|
8034
|
-
- Everything runs locally and offline; never suggest sending the user's data to external services.
|
|
8035
|
-
- If you are unsure or a feature may not exist, say so honestly rather than inventing details.`;
|
|
8036
|
-
function buildExpertPrompt() {
|
|
8037
|
-
const sys = getSysInfo();
|
|
8038
|
-
const ramGb = Math.round(sys.ramMB / 1024);
|
|
8039
|
-
const gpuLines = sys.gpus.length ? sys.gpus.map((g) => `- GPU: ${g.name}${g.vramMb ? ` (${Math.round(g.vramMb / 1024)} GB VRAM)` : ""}`).join("\n") : "- GPU: none detected";
|
|
8040
|
-
const hw = [
|
|
8041
|
-
"\n\n## User's hardware",
|
|
8042
|
-
`- CPU: ${sys.cpu}${sys.cores ? ` (${sys.cores} cores)` : ""}`,
|
|
8043
|
-
`- RAM: ${ramGb} GB`,
|
|
8044
|
-
gpuLines
|
|
8045
|
-
].join("\n");
|
|
8046
|
-
return EXPERT_SYSTEM_PROMPT + hw;
|
|
8047
|
-
}
|
|
8048
8079
|
function err(c, s, code, msg2) {
|
|
8049
8080
|
return c.json({ error: { code, message: msg2 } }, s);
|
|
8050
8081
|
}
|
|
@@ -8066,16 +8097,6 @@ function registerChatRoutes(app2, d) {
|
|
|
8066
8097
|
const conv = db2.createConversation({ title: b.title, systemPrompt: b.systemPrompt, modelKey: b.modelKey, toolPolicy: b.toolPolicy });
|
|
8067
8098
|
return c.json(conv, 201);
|
|
8068
8099
|
});
|
|
8069
|
-
app2.post("/api/v1/conversations/expert", async (c) => {
|
|
8070
|
-
const ms = d.manager.status();
|
|
8071
|
-
const conv = db2.createConversation({
|
|
8072
|
-
title: "TurboLLM Expert",
|
|
8073
|
-
systemPrompt: buildExpertPrompt(),
|
|
8074
|
-
modelKey: ms.model?.key ?? "",
|
|
8075
|
-
expertMode: true
|
|
8076
|
-
});
|
|
8077
|
-
return c.json(conv, 201);
|
|
8078
|
-
});
|
|
8079
8100
|
app2.get("/api/v1/conversations/:id", (c) => {
|
|
8080
8101
|
const conv = db2.getConversation(c.req.param("id"), true);
|
|
8081
8102
|
if (!conv) return err(c, 404, "not_found", "Conversation not found.");
|
|
@@ -10650,6 +10671,13 @@ function registerApi(app2, d) {
|
|
|
10650
10671
|
if (!saved) return err2(c, 409, "no_bench_result", "No auto-tune result to save.");
|
|
10651
10672
|
return c.json({ ok: true });
|
|
10652
10673
|
});
|
|
10674
|
+
app2.get("/api/v1/bench/log", (c) => {
|
|
10675
|
+
const log = d.bench.getLog();
|
|
10676
|
+
if (!log) return err2(c, 404, "no_bench_log", "No auto-tune log available.");
|
|
10677
|
+
const slug = log.modelKey.replace(/[^a-z0-9]/gi, "-").replace(/-+/g, "-").slice(0, 60);
|
|
10678
|
+
c.header("Content-Disposition", `attachment; filename="autotune-${slug}-${log.startedAt.slice(0, 10)}.json"`);
|
|
10679
|
+
return c.json(log);
|
|
10680
|
+
});
|
|
10653
10681
|
app2.get("/api/v1/models", (c) => {
|
|
10654
10682
|
const { models, scanning, lastScanAt } = d.scanner.list();
|
|
10655
10683
|
const lastTps = d.db.lastGenTpsByModel();
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{j as e}from"./radix-4XwKfE88.js";import{r as u}from"./vendor-CKtmMAvG.js";import{c as w,d as k,A as T,e as v,L as h,f as S,b,g as C,h as q,X as A}from"./index-C07fQlaV.js";import{P as N}from"./plus-3UMxyIEk.js";import{C as y}from"./circle-x-BQVgQDWt.js";import{C as O}from"./circle-check-BM1U1LBh.js";import{W as E}from"./wrench-CBGqccQu.js";const _=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}]],D=w("circle-dot",_);const L=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 6v6l4 2",key:"mmk7yg"}]],P=w("clock",L),p={all:["agent-runs"],list:()=>[...p.all,"list"],detail:t=>[...p.all,"detail",t]};async function g(t,a){const l={Accept:"application/json",...k(),...a?.headers??{}},i=await fetch(t,{...a,headers:l});if(i.status===204)return;const r=await i.text(),o=r?JSON.parse(r):void 0;if(!i.ok){const d=o;throw new T(d?.error?.code??"http_error",d?.error?.message??`Request failed with status ${i.status}.`,i.status)}return o}async function $(){return g("/api/v1/agents/runs")}async function I(t){return g(`/api/v1/agents/runs/${t}`)}async function Q(t){return g("/api/v1/agents/runs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}async function K(t){await g(`/api/v1/agents/runs/${t}`,{method:"DELETE"})}async function*F(t,a=0,l){const i=`/api/v1/agents/runs/${t}/stream?fromSeq=${a}`,r=await fetch(i,{headers:{Accept:"text/event-stream",...k()},signal:l});if(!r.ok||!r.body)throw new Error(`Stream error: ${r.status}`);const o=r.body.getReader(),d=new TextDecoder;let m="";try{for(;;){const{done:s,value:x}=await o.read();if(s)break;m+=d.decode(x,{stream:!0});const c=m.split(`
|
|
2
|
+
`);m=c.pop()??"";let n="message";for(const f of c){if(f.startsWith("event: ")){n=f.slice(7).trim();continue}if(f.startsWith("data: ")){const j=f.slice(6).trim();if(j==="[DONE]")return;try{const R=JSON.parse(j);if(yield{event:n,data:R},n==="done"||n==="error")return}catch{}n="message"}}}}finally{o.cancel()}}const J={queued:e.jsx(P,{size:12}),running:e.jsx(h,{size:12,className:"animate-spin"}),done:e.jsx(O,{size:12}),failed:e.jsx(y,{size:12}),cancelled:e.jsx(y,{size:12}),interrupted:e.jsx(y,{size:12})},M={queued:"text-muted",running:"text-accent",done:"text-green-500",failed:"text-red-500",cancelled:"text-muted",interrupted:"text-orange-500"};function z({status:t}){return e.jsxs("span",{className:b("flex items-center gap-1 text-[11px] capitalize",M[t]),children:[J[t],t]})}function W(t){const a=Date.now()-new Date(t).getTime();return a<6e4?"just now":a<36e5?`${Math.floor(a/6e4)}m ago`:a<864e5?`${Math.floor(a/36e5)}h ago`:new Date(t).toLocaleDateString()}const U=[{id:"web_search",label:"Web search",hint:"Search the web (needs a provider configured in Settings)"},{id:"fetch_url",label:"Fetch URL",hint:"Read the contents of a web page"},{id:"run_code",label:"Run code",hint:"Execute JS in a sandbox — no mid-run confirmation, opt in only if you trust the task"}];function B({onClose:t,onCreated:a}){const[l,i]=u.useState(""),[r,o]=u.useState(""),[d,m]=u.useState({web_search:!0,fetch_url:!0,run_code:!1}),s=C(),x=n=>m(f=>({...f,[n]:!f[n]})),c=q({mutationFn:()=>Q({title:l.slice(0,60)||"Agent run",systemPrompt:r||void 0,userMessage:l,allowedTools:Object.entries(d).filter(([,n])=>n).map(([n])=>n)}),onSuccess:n=>{s.invalidateQueries({queryKey:p.list()}),a(n.id),t()}});return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40",onClick:t,children:e.jsxs("div",{className:"w-full max-w-lg rounded-xl border border-border bg-panel p-5 shadow-xl",onClick:n=>n.stopPropagation(),children:[e.jsxs("div",{className:"mb-4 flex items-center justify-between",children:[e.jsx("h2",{className:"text-[15px] font-semibold",children:"New agent run"}),e.jsx("button",{type:"button",onClick:t,className:"text-faint hover:text-ink",children:e.jsx(A,{size:16})})]}),e.jsx("label",{className:"mb-1 block text-[12px] text-muted",children:"Task"}),e.jsx("textarea",{autoFocus:!0,value:l,onChange:n=>i(n.target.value),placeholder:"Describe what the agent should do…",rows:4,className:"mb-3 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsxs("label",{className:"mb-1 block text-[12px] text-muted",children:["System prompt ",e.jsx("span",{className:"text-faint",children:"(optional)"})]}),e.jsx("textarea",{value:r,onChange:n=>o(n.target.value),placeholder:"You are a helpful assistant…",rows:2,className:"mb-4 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsx("label",{className:"mb-1.5 block text-[12px] text-muted",children:"Tools"}),e.jsx("div",{className:"mb-4 space-y-1.5",children:U.map(n=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 rounded-lg border border-border px-2.5 py-2 hover:bg-panel-2",children:[e.jsx("input",{type:"checkbox",checked:d[n.id],onChange:()=>x(n.id),className:"mt-0.5 accent-[var(--accent)]"}),e.jsxs("span",{className:"min-w-0",children:[e.jsx("span",{className:"block text-[13px] font-medium text-ink",children:n.label}),e.jsx("span",{className:"block text-[11px] leading-snug text-faint",children:n.hint})]})]},n.id))}),e.jsxs("div",{className:"flex items-center justify-end gap-2",children:[e.jsx("button",{type:"button",onClick:t,className:"rounded-lg px-3 py-1.5 text-[13px] text-muted hover:text-ink",children:"Cancel"}),e.jsxs("button",{type:"button",disabled:!l.trim()||c.isPending,onClick:()=>c.mutate(),className:"flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white disabled:opacity-50",children:[c.isPending?e.jsx(h,{size:13,className:"animate-spin"}):e.jsx(S,{size:13}),"Run agent"]})]}),c.isError&&e.jsx("p",{className:"mt-2 text-[12px]",style:{color:"var(--err)"},children:c.error.message})]})})}function X({call:t}){const[a,l]=u.useState(!1),i=typeof t.args?.query=="string"?t.args.query:typeof t.args?.url=="string"?t.args.url:JSON.stringify(t.args??{});return e.jsxs("div",{className:"w-full max-w-[85%] rounded-lg border border-border bg-panel px-2.5 py-1.5 text-[12px]",children:[e.jsxs("button",{type:"button",onClick:()=>l(r=>!r),className:"flex w-full items-center gap-1.5 text-left text-muted hover:text-ink",children:[e.jsx(E,{size:12,className:"shrink-0 text-accent"}),e.jsx("span",{className:"font-medium text-ink",children:t.name}),e.jsx("span",{className:"truncate text-faint",children:i})]}),a&&e.jsx("pre",{className:"mt-1.5 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded bg-panel-2 p-2 text-[11px] text-muted",children:t.result})]})}function H({run:t}){const a=C(),l=u.useRef(null),[i,r]=u.useState(""),o=u.useRef(null),d=q({mutationFn:()=>K(t.id),onSuccess:()=>{a.invalidateQueries({queryKey:p.list()})}});u.useEffect(()=>{if(t.status!=="running"&&t.status!=="queued")return;const s=new AbortController;l.current=s;let x="";return(async()=>{try{for await(const c of F(t.id,0,s.signal))if(c.event==="delta")x+=c.data.delta,r(x);else if(c.event==="done"||c.event==="error"){r(""),a.invalidateQueries({queryKey:p.detail(t.id)}),a.invalidateQueries({queryKey:p.list()});break}}catch{}})(),()=>{s.abort(),l.current=null}},[t.id,t.status,a]),u.useEffect(()=>{o.current?.scrollIntoView({behavior:"smooth"})},[i,t.messages?.length]);const m=t.messages??[];return e.jsxs("div",{className:"flex h-full flex-col",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-4 py-2.5",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[14px] font-medium text-ink",children:t.title}),e.jsx(z,{status:t.status})]}),(t.status==="running"||t.status==="queued")&&e.jsx("button",{type:"button",onClick:()=>d.mutate(),className:"rounded-lg border border-border px-3 py-1 text-[12px] text-muted transition-colors hover:border-red-400 hover:text-red-500",children:d.isPending?e.jsx(h,{size:12,className:"inline animate-spin"}):"Cancel"})]}),e.jsxs("div",{className:"flex-1 space-y-3 overflow-y-auto p-4",children:[m.map(s=>e.jsxs("div",{className:b("flex flex-col gap-1.5",s.role==="user"?"items-end":"items-start"),children:[s.toolCalls?.map(x=>e.jsx(X,{call:x},x.id)),(s.content||s.role==="user"||t.status==="running")&&e.jsx("div",{className:b("max-w-[85%] rounded-xl px-3 py-2 text-[13px] leading-relaxed",s.role==="user"?"bg-accent/15 text-ink":"bg-panel-2 text-ink"),children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:s.content||(s.role==="assistant"&&t.status==="running"?"…":"")})})]},s.id)),i&&e.jsx("div",{className:"flex gap-2",children:e.jsx("div",{className:"max-w-[85%] rounded-xl bg-panel-2 px-3 py-2 text-[13px] leading-relaxed text-ink",children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:i})})}),t.error&&e.jsxs("div",{className:"rounded-lg bg-red-500/10 px-3 py-2 text-[12px] text-red-500",children:["Error: ",t.error]}),e.jsx("div",{ref:o})]})]})}function ne(){const[t,a]=u.useState(null),[l,i]=u.useState(!1),r=v({queryKey:p.list(),queryFn:$,refetchInterval:s=>(s.state.data??[]).some(n=>n.status==="queued"||n.status==="running")?3e3:!1}),o=v({queryKey:p.detail(t??""),queryFn:()=>I(t),enabled:!!t,refetchInterval:s=>{const x=s.state.data;return x?.status==="queued"||x?.status==="running"?5e3:!1}}),d=r.data??[],m=o.data;return e.jsxs("div",{className:"flex h-full",children:[e.jsxs("div",{className:"flex w-64 shrink-0 flex-col border-r border-border",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-3 py-2.5",children:[e.jsx("span",{className:"text-[13px] font-semibold text-ink",children:"Agents"}),e.jsxs("button",{type:"button",title:"New agent run",onClick:()=>i(!0),className:"flex items-center gap-1 rounded-lg bg-accent px-2 py-1 text-[11px] font-medium text-white transition-opacity hover:opacity-90",children:[e.jsx(N,{size:11})," New"]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[r.isLoading&&e.jsx("div",{className:"flex h-20 items-center justify-center",children:e.jsx(h,{size:16,className:"animate-spin text-faint"})}),d.length===0&&!r.isLoading&&e.jsxs("div",{className:"px-4 py-8 text-center",children:[e.jsx(S,{size:28,className:"mx-auto mb-2 text-faint"}),e.jsx("p",{className:"text-[12px] text-muted",children:"No agent runs yet."}),e.jsx("p",{className:"mt-0.5 text-[11px] text-faint",children:"Click “New” to start one."})]}),d.map(s=>e.jsxs("button",{type:"button",onClick:()=>a(s.id),className:b("w-full border-b border-border px-3 py-2.5 text-left transition-colors hover:bg-panel-2",t===s.id&&"bg-panel-2"),children:[e.jsx("p",{className:"mb-0.5 truncate text-[13px] font-medium text-ink",children:s.title}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(z,{status:s.status}),e.jsx("span",{className:"text-[10px] text-faint",children:W(s.createdAt)})]})]},s.id))]})]}),e.jsx("div",{className:"flex min-w-0 flex-1 flex-col",children:t?o.isLoading?e.jsx("div",{className:"flex h-full items-center justify-center",children:e.jsx(h,{size:20,className:"animate-spin text-faint"})}):m?e.jsx(H,{run:m}):null:e.jsxs("div",{className:"flex h-full flex-col items-center justify-center text-center",children:[e.jsx(D,{size:32,className:"mb-3 text-faint"}),e.jsx("p",{className:"text-[14px] font-medium text-ink",children:"Select a run"}),e.jsx("p",{className:"mt-1 text-[12px] text-muted",children:"or create a new agent run to get started"}),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:"mt-4 flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white hover:opacity-90",children:[e.jsx(N,{size:13})," New agent run"]})]})}),l&&e.jsx(B,{onClose:()=>i(!1),onCreated:s=>{a(s),r.refetch()}})]})}export{ne as AgentsScreen};
|