offgrid-ai 0.3.21 → 0.3.22

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.22",
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
@@ -438,7 +438,7 @@ function printGgufModelDetails(model) {
438
438
  function printManagedModelDetails(model, backend) {
439
439
  console.log("\n" + renderSection(`${backend.label} model`, renderRows([
440
440
  ["Name", pc.bold(model.label)],
441
- ["Status", pc.green("Available from another app")],
441
+ ["Status", pc.green(`Local model via ${backend.label}`)],
442
442
  ["Model ID", pc.cyan(model.id)],
443
443
  ["Quant", model.quant ?? "unknown"],
444
444
  ["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
  }