offgrid-ai 0.3.21 → 0.3.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "offgrid-ai",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "Privacy-first CLI for running local LLMs — discover, configure, run, benchmark",
5
5
  "author": "Eeshan Srivastava (https://eeshans.com)",
6
6
  "type": "module",
package/src/cli.mjs CHANGED
@@ -306,7 +306,6 @@ function downloadedModelCard(num, model, caps) {
306
306
  ["Status", statusText("warning", "Needs one-time setup")],
307
307
  ["Good for", humanCapabilitySummary(caps)],
308
308
  ["Size", formatBytes(model.sizeBytes)],
309
- ["When selected", "offgrid-ai will recommend safe local settings"],
310
309
  ]), { formatBorder: pc.yellow });
311
310
  }
312
311
 
@@ -438,7 +437,7 @@ function printGgufModelDetails(model) {
438
437
  function printManagedModelDetails(model, backend) {
439
438
  console.log("\n" + renderSection(`${backend.label} model`, renderRows([
440
439
  ["Name", pc.bold(model.label)],
441
- ["Status", pc.green("Available from another app")],
440
+ ["Status", pc.green(`Local model via ${backend.label}`)],
442
441
  ["Model ID", pc.cyan(model.id)],
443
442
  ["Quant", model.quant ?? "unknown"],
444
443
  ["Family", model.family ?? "unknown"],
@@ -1,11 +1,11 @@
1
1
  import { estimateMemory } from "./estimate.mjs";
2
- import { pc, formatBytes, renderRows, renderSection, humanCapabilitySummary } from "./ui.mjs";
2
+ import { pc, formatBytes, renderRows, renderSection } from "./ui.mjs";
3
3
 
4
4
  const CACHE_CHOICES = [
5
- { value: "bf16", label: "Balanced", hint: "recommended: stable, good quality" },
6
- { value: "f16", label: "Compatible", hint: "stable fallback, similar memory use" },
7
- { value: "q8_0", label: "Lower memory", hint: "usually safe, uses less memory" },
8
- { value: "q4_0", label: "Smallest memory", hint: "maximum savings, quality/speed tradeoff" },
5
+ { value: "bf16", label: "bf16", hint: "default: stable, good quality" },
6
+ { value: "f16", label: "f16", hint: "stable fallback, similar memory to bf16" },
7
+ { value: "q8_0", label: "q8_0", hint: "lower memory, usually safe" },
8
+ { value: "q4_0", label: "q4_0", hint: "lowest memory, quality/speed tradeoff" },
9
9
  ];
10
10
 
11
11
  const GENERAL_DEFAULTS = {
@@ -26,49 +26,64 @@ export async function configureLocalProfile(prompt, profile) {
26
26
  const caps = profile.capabilities ?? {};
27
27
 
28
28
  console.log("");
29
- console.log(renderSection("Let's set up this model", renderRows([
29
+ console.log(renderSection("Model setup", renderRows([
30
30
  ["Model", pc.bold(profile.label)],
31
- ["Good for", humanCapabilitySummary(caps)],
32
- ["Conversation memory", `${profile.flags.ctxSize.toLocaleString()} tokens`],
33
- ["Memory mode", `${profile.flags.cacheTypeK}/${profile.flags.cacheTypeV}`],
34
- ["Response style", samplingSummary(profile.flags)],
31
+ ["Detected", detectionSummary(caps)],
32
+ ["Context", `${profile.flags.ctxSize.toLocaleString()} tokens`],
33
+ ["KV cache", `${profile.flags.cacheTypeK}/${profile.flags.cacheTypeV}`],
34
+ ["Sampling", samplingSummary(profile.flags)],
35
35
  ])));
36
- console.log(pc.dim("You can accept the recommended settings. Bigger conversation memory uses more RAM.\n"));
36
+ console.log(pc.dim("Larger context windows use more memory. KV cache precision controls memory used by attention history."));
37
+ console.log(pc.dim("Sampling defaults are shown for transparency; you can edit command.json later if needed.\n"));
37
38
 
38
39
  if (caps.mtp) {
39
- console.log(renderSection("MTP available", "This model supports multi-token prediction (MTP). offgrid-ai can run it with llama.cpp MTP on port 8081."));
40
- const useMtp = await prompt.yesNo("Use MTP for this model?", true);
40
+ console.log(renderSection("MTP detected", renderRows([
41
+ ["Backend", "llama.cpp MTP"],
42
+ ["Port", "8081"],
43
+ ["Flags", "--spec-type draft-mtp --spec-draft-n-max 2"],
44
+ ])));
45
+ const useMtp = await prompt.yesNo("Use MTP speculative decoding flags?", true);
41
46
  configured = useMtp ? applyMtpDefaults(configured) : removeMtpDefaults(configured);
42
47
  }
43
48
 
44
49
  if (caps.qat) {
45
50
  console.log("");
46
- console.log(renderSection("QAT model", "This model is marked as quantization-aware trained (QAT). No extra runtime settings are needed."));
51
+ console.log(renderSection("QAT detected", renderRows([
52
+ ["Meaning", "quantization-aware trained"],
53
+ ["Runtime flags", "none required"],
54
+ ])));
47
55
  }
48
56
 
49
57
  if (caps.thinking) {
50
58
  console.log("");
51
- console.log(renderSection("Reasoning mode", "This model can reason step by step. offgrid-ai can use safer defaults that reduce repetitive loops."));
52
- const useThinking = await prompt.yesNo("Use reasoning-friendly defaults?", true);
59
+ console.log(renderSection("Thinking model detected", renderRows([
60
+ ["Defaults", "thinking / loop-safe"],
61
+ ["Flags", "--top-k 64 --presence-penalty 0 --repeat-penalty 1.1"],
62
+ ["Template", "--chat-template-kwargs { enable_thinking: true }"],
63
+ ])));
64
+ const useThinking = await prompt.yesNo("Use these thinking/loop-safe defaults?", true);
53
65
  configured = useThinking ? applyThinkingDefaults(configured) : removeThinkingDefaults(configured);
54
66
  }
55
67
 
56
- const ctxSize = await prompt.number("Conversation memory tokens", configured.flags.ctxSize, 1024, 1048576);
57
- const cacheTypeK = await prompt.choice("Memory mode, part 1", CACHE_CHOICES, configured.flags.cacheTypeK);
58
- const cacheTypeV = await prompt.choice("Memory mode, part 2", CACHE_CHOICES, configured.flags.cacheTypeV);
68
+ const ctxSize = await prompt.number("Context window tokens", configured.flags.ctxSize, 1024, 1048576);
69
+ const cacheTypeK = await prompt.choice("K cache precision", CACHE_CHOICES, configured.flags.cacheTypeK);
70
+ const cacheTypeV = await prompt.choice("V cache precision", CACHE_CHOICES, configured.flags.cacheTypeV);
59
71
  configured = applyRuntimeFlagOverrides(configured, { ctxSize, cacheTypeK, cacheTypeV });
60
72
 
61
73
  console.log("");
62
- console.log(renderSection("Final setup", renderRows([
63
- ["Runs with", configured.backend],
64
- ["Local address", configured.baseUrl],
65
- ["Creativity", configured.flags.temperature],
66
- ["Focus", configured.flags.topP],
67
- ["Reasoning breadth", configured.flags.topK],
74
+ console.log(renderSection("Defaults", renderRows([
75
+ ["Backend", configured.backend],
76
+ ["Endpoint", configured.baseUrl],
77
+ ["Temperature", configured.flags.temperature],
78
+ ["Top-p", configured.flags.topP],
79
+ ["Top-k", configured.flags.topK],
80
+ ["Min-p", configured.flags.minP],
81
+ ["Presence penalty", configured.flags.presencePenalty],
82
+ ["Repeat penalty", configured.flags.repeatPenalty],
68
83
  ])));
69
84
 
70
85
  console.log("\n" + renderMemoryEstimate(configured));
71
- if (!(await prompt.yesNo("Save this model setup?", true))) return null;
86
+ if (!(await prompt.yesNo("Save profile with these settings?", true))) return null;
72
87
  return configured;
73
88
  }
74
89
 
@@ -155,8 +170,8 @@ function renderMemoryEstimate(profile) {
155
170
  const est = estimateMemory(profile.modelPath, profile.mmprojPath, null, profile.flags);
156
171
  return renderSection("Memory estimate", renderRows([
157
172
  ["Estimated total", pc.bold(`~${formatBytes(est.totalBytes)}`)],
158
- ["Model file", formatBytes(est.modelBytes)],
159
- ["Conversation memory", est.kvBytes ? `~${formatBytes(est.kvBytes)} (${profile.flags.ctxSize.toLocaleString()} tokens, ${profile.flags.cacheTypeK}/${profile.flags.cacheTypeV})` : "unknown"],
173
+ ["Model", formatBytes(est.modelBytes)],
174
+ ["KV cache", est.kvBytes ? `~${formatBytes(est.kvBytes)} (${profile.flags.ctxSize.toLocaleString()} ctx, ${profile.flags.cacheTypeK}/${profile.flags.cacheTypeV})` : "unknown"],
160
175
  ...(est.note ? [["Note", pc.yellow(est.note)]] : []),
161
176
  ]));
162
177
  } catch {
@@ -164,6 +179,18 @@ function renderMemoryEstimate(profile) {
164
179
  }
165
180
  }
166
181
 
182
+ function detectionSummary(caps) {
183
+ const parts = [];
184
+ if (caps.architecture) parts.push(caps.architecture);
185
+ if (caps.quant) parts.push(caps.quant);
186
+ if (caps.mtp) parts.push("MTP");
187
+ if (caps.qat) parts.push("QAT");
188
+
189
+ if (caps.thinking) parts.push("thinking");
190
+ if (caps.vision) parts.push("vision");
191
+ return parts.length > 0 ? parts.join(" · ") : "standard GGUF";
192
+ }
193
+
167
194
  function samplingSummary(flags) {
168
195
  return `temp ${flags.temperature}, top-p ${flags.topP}, top-k ${flags.topK}`;
169
196
  }