@gmickel/gno 0.7.0 → 0.8.1
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 +21 -0
- package/README.md +90 -50
- package/THIRD_PARTY_NOTICES.md +22 -0
- package/assets/screenshots/webui-ask-answer.png +0 -0
- package/assets/screenshots/webui-collections.png +0 -0
- package/assets/screenshots/webui-editor.png +0 -0
- package/assets/screenshots/webui-home.png +0 -0
- package/assets/skill/SKILL.md +12 -12
- package/assets/skill/cli-reference.md +59 -57
- package/assets/skill/examples.md +8 -7
- package/assets/skill/mcp-reference.md +8 -4
- package/package.json +31 -24
- package/src/app/constants.ts +43 -42
- package/src/cli/colors.ts +1 -1
- package/src/cli/commands/ask.ts +44 -43
- package/src/cli/commands/cleanup.ts +9 -8
- package/src/cli/commands/collection/add.ts +12 -12
- package/src/cli/commands/collection/index.ts +4 -4
- package/src/cli/commands/collection/list.ts +26 -25
- package/src/cli/commands/collection/remove.ts +10 -10
- package/src/cli/commands/collection/rename.ts +10 -10
- package/src/cli/commands/context/add.ts +1 -1
- package/src/cli/commands/context/check.ts +17 -17
- package/src/cli/commands/context/index.ts +4 -4
- package/src/cli/commands/context/list.ts +11 -11
- package/src/cli/commands/context/rm.ts +1 -1
- package/src/cli/commands/doctor.ts +86 -84
- package/src/cli/commands/embed.ts +30 -28
- package/src/cli/commands/get.ts +27 -26
- package/src/cli/commands/index-cmd.ts +9 -9
- package/src/cli/commands/index.ts +16 -16
- package/src/cli/commands/init.ts +13 -12
- package/src/cli/commands/ls.ts +20 -19
- package/src/cli/commands/mcp/config.ts +30 -28
- package/src/cli/commands/mcp/index.ts +4 -4
- package/src/cli/commands/mcp/install.ts +17 -17
- package/src/cli/commands/mcp/paths.ts +133 -133
- package/src/cli/commands/mcp/status.ts +21 -21
- package/src/cli/commands/mcp/uninstall.ts +13 -13
- package/src/cli/commands/mcp.ts +2 -2
- package/src/cli/commands/models/clear.ts +12 -11
- package/src/cli/commands/models/index.ts +5 -5
- package/src/cli/commands/models/list.ts +31 -30
- package/src/cli/commands/models/path.ts +1 -1
- package/src/cli/commands/models/pull.ts +19 -18
- package/src/cli/commands/models/use.ts +4 -4
- package/src/cli/commands/multi-get.ts +38 -36
- package/src/cli/commands/query.ts +21 -20
- package/src/cli/commands/ref-parser.ts +10 -10
- package/src/cli/commands/reset.ts +40 -39
- package/src/cli/commands/search.ts +14 -13
- package/src/cli/commands/serve.ts +4 -4
- package/src/cli/commands/shared.ts +11 -10
- package/src/cli/commands/skill/index.ts +5 -5
- package/src/cli/commands/skill/install.ts +18 -17
- package/src/cli/commands/skill/paths-cmd.ts +11 -10
- package/src/cli/commands/skill/paths.ts +23 -23
- package/src/cli/commands/skill/show.ts +13 -12
- package/src/cli/commands/skill/uninstall.ts +16 -15
- package/src/cli/commands/status.ts +25 -24
- package/src/cli/commands/update.ts +3 -3
- package/src/cli/commands/vsearch.ts +17 -16
- package/src/cli/context.ts +5 -5
- package/src/cli/errors.ts +3 -3
- package/src/cli/format/search-results.ts +37 -37
- package/src/cli/options.ts +43 -43
- package/src/cli/program.ts +455 -459
- package/src/cli/progress.ts +1 -1
- package/src/cli/run.ts +24 -23
- package/src/collection/add.ts +9 -8
- package/src/collection/index.ts +3 -3
- package/src/collection/remove.ts +7 -6
- package/src/collection/types.ts +6 -6
- package/src/config/defaults.ts +1 -1
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +19 -18
- package/src/config/paths.ts +9 -8
- package/src/config/saver.ts +14 -13
- package/src/config/types.ts +53 -52
- package/src/converters/adapters/markitdownTs/adapter.ts +21 -19
- package/src/converters/adapters/officeparser/adapter.ts +18 -16
- package/src/converters/canonicalize.ts +12 -12
- package/src/converters/errors.ts +26 -22
- package/src/converters/index.ts +8 -8
- package/src/converters/mime.ts +25 -25
- package/src/converters/native/markdown.ts +10 -9
- package/src/converters/native/plaintext.ts +8 -7
- package/src/converters/path.ts +2 -2
- package/src/converters/pipeline.ts +11 -10
- package/src/converters/registry.ts +8 -8
- package/src/converters/types.ts +14 -14
- package/src/converters/versions.ts +4 -4
- package/src/index.ts +4 -4
- package/src/ingestion/chunker.ts +10 -9
- package/src/ingestion/index.ts +6 -6
- package/src/ingestion/language.ts +62 -62
- package/src/ingestion/sync.ts +50 -49
- package/src/ingestion/types.ts +10 -10
- package/src/ingestion/walker.ts +14 -13
- package/src/llm/cache.ts +51 -49
- package/src/llm/errors.ts +40 -36
- package/src/llm/index.ts +9 -9
- package/src/llm/lockfile.ts +6 -6
- package/src/llm/nodeLlamaCpp/adapter.ts +13 -12
- package/src/llm/nodeLlamaCpp/embedding.ts +9 -8
- package/src/llm/nodeLlamaCpp/generation.ts +7 -6
- package/src/llm/nodeLlamaCpp/lifecycle.ts +11 -10
- package/src/llm/nodeLlamaCpp/rerank.ts +6 -5
- package/src/llm/policy.ts +5 -5
- package/src/llm/registry.ts +6 -5
- package/src/llm/types.ts +2 -2
- package/src/mcp/resources/index.ts +15 -13
- package/src/mcp/server.ts +25 -23
- package/src/mcp/tools/get.ts +25 -23
- package/src/mcp/tools/index.ts +32 -29
- package/src/mcp/tools/multi-get.ts +34 -32
- package/src/mcp/tools/query.ts +29 -27
- package/src/mcp/tools/search.ts +14 -12
- package/src/mcp/tools/status.ts +12 -11
- package/src/mcp/tools/vsearch.ts +26 -24
- package/src/pipeline/answer.ts +9 -9
- package/src/pipeline/chunk-lookup.ts +1 -1
- package/src/pipeline/contextual.ts +4 -4
- package/src/pipeline/expansion.ts +23 -21
- package/src/pipeline/explain.ts +21 -21
- package/src/pipeline/fusion.ts +9 -9
- package/src/pipeline/hybrid.ts +41 -42
- package/src/pipeline/index.ts +10 -10
- package/src/pipeline/query-language.ts +39 -39
- package/src/pipeline/rerank.ts +8 -7
- package/src/pipeline/search.ts +22 -22
- package/src/pipeline/types.ts +8 -8
- package/src/pipeline/vsearch.ts +21 -24
- package/src/serve/CLAUDE.md +21 -15
- package/src/serve/config-sync.ts +9 -8
- package/src/serve/context.ts +19 -18
- package/src/serve/index.ts +1 -1
- package/src/serve/jobs.ts +7 -7
- package/src/serve/public/app.tsx +79 -25
- package/src/serve/public/components/AddCollectionDialog.tsx +382 -0
- package/src/serve/public/components/CaptureButton.tsx +60 -0
- package/src/serve/public/components/CaptureModal.tsx +365 -0
- package/src/serve/public/components/IndexingProgress.tsx +333 -0
- package/src/serve/public/components/ShortcutHelpModal.tsx +106 -0
- package/src/serve/public/components/ai-elements/code-block.tsx +42 -32
- package/src/serve/public/components/ai-elements/conversation.tsx +16 -14
- package/src/serve/public/components/ai-elements/inline-citation.tsx +33 -32
- package/src/serve/public/components/ai-elements/loader.tsx +5 -4
- package/src/serve/public/components/ai-elements/message.tsx +39 -37
- package/src/serve/public/components/ai-elements/prompt-input.tsx +97 -95
- package/src/serve/public/components/ai-elements/sources.tsx +12 -10
- package/src/serve/public/components/ai-elements/suggestion.tsx +10 -9
- package/src/serve/public/components/editor/CodeMirrorEditor.tsx +142 -0
- package/src/serve/public/components/editor/MarkdownPreview.tsx +311 -0
- package/src/serve/public/components/editor/index.ts +6 -0
- package/src/serve/public/components/preset-selector.tsx +29 -28
- package/src/serve/public/components/ui/badge.tsx +13 -12
- package/src/serve/public/components/ui/button-group.tsx +13 -12
- package/src/serve/public/components/ui/button.tsx +23 -22
- package/src/serve/public/components/ui/card.tsx +16 -16
- package/src/serve/public/components/ui/carousel.tsx +36 -35
- package/src/serve/public/components/ui/collapsible.tsx +1 -1
- package/src/serve/public/components/ui/command.tsx +17 -15
- package/src/serve/public/components/ui/dialog.tsx +13 -12
- package/src/serve/public/components/ui/dropdown-menu.tsx +13 -12
- package/src/serve/public/components/ui/hover-card.tsx +6 -5
- package/src/serve/public/components/ui/input-group.tsx +45 -43
- package/src/serve/public/components/ui/input.tsx +6 -6
- package/src/serve/public/components/ui/progress.tsx +5 -4
- package/src/serve/public/components/ui/scroll-area.tsx +11 -10
- package/src/serve/public/components/ui/select.tsx +19 -18
- package/src/serve/public/components/ui/separator.tsx +6 -5
- package/src/serve/public/components/ui/table.tsx +18 -18
- package/src/serve/public/components/ui/textarea.tsx +4 -4
- package/src/serve/public/components/ui/tooltip.tsx +5 -4
- package/src/serve/public/globals.css +27 -4
- package/src/serve/public/hooks/use-api.ts +8 -8
- package/src/serve/public/hooks/useCaptureModal.tsx +83 -0
- package/src/serve/public/hooks/useKeyboardShortcuts.ts +85 -0
- package/src/serve/public/index.html +4 -4
- package/src/serve/public/lib/utils.ts +6 -0
- package/src/serve/public/pages/Ask.tsx +27 -26
- package/src/serve/public/pages/Browse.tsx +28 -27
- package/src/serve/public/pages/Collections.tsx +439 -0
- package/src/serve/public/pages/Dashboard.tsx +166 -40
- package/src/serve/public/pages/DocView.tsx +258 -73
- package/src/serve/public/pages/DocumentEditor.tsx +510 -0
- package/src/serve/public/pages/Search.tsx +80 -58
- package/src/serve/routes/api.ts +272 -155
- package/src/serve/security.ts +4 -4
- package/src/serve/server.ts +66 -48
- package/src/store/index.ts +5 -5
- package/src/store/migrations/001-initial.ts +24 -23
- package/src/store/migrations/002-documents-fts.ts +7 -6
- package/src/store/migrations/index.ts +4 -4
- package/src/store/migrations/runner.ts +17 -15
- package/src/store/sqlite/adapter.ts +123 -121
- package/src/store/sqlite/fts5-snowball.ts +24 -23
- package/src/store/sqlite/index.ts +1 -1
- package/src/store/sqlite/setup.ts +12 -12
- package/src/store/sqlite/types.ts +4 -4
- package/src/store/types.ts +19 -19
- package/src/store/vector/index.ts +3 -3
- package/src/store/vector/sqlite-vec.ts +23 -20
- package/src/store/vector/stats.ts +10 -8
- package/src/store/vector/types.ts +2 -2
- package/vendor/fts5-snowball/README.md +6 -6
- package/assets/screenshots/webui-ask-answer.jpg +0 -0
- package/assets/screenshots/webui-home.jpg +0 -0
package/src/llm/cache.ts
CHANGED
|
@@ -6,13 +6,23 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// node:crypto: createHash for safe lock filenames
|
|
9
|
-
import { createHash } from
|
|
10
|
-
import { mkdir, open, readFile, rename, rm, stat } from
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
import { mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
|
|
11
11
|
// node:path: join for path construction, isAbsolute for cross-platform path detection
|
|
12
|
-
import { isAbsolute, join } from
|
|
12
|
+
import { isAbsolute, join } from "node:path";
|
|
13
13
|
// node:url: fileURLToPath for proper file:// URL handling
|
|
14
|
-
import { fileURLToPath } from
|
|
15
|
-
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
import type { DownloadPolicy } from "./policy";
|
|
17
|
+
import type {
|
|
18
|
+
DownloadProgress,
|
|
19
|
+
LlmResult,
|
|
20
|
+
ModelCacheEntry,
|
|
21
|
+
ModelType,
|
|
22
|
+
ProgressCallback,
|
|
23
|
+
} from "./types";
|
|
24
|
+
|
|
25
|
+
import { getModelsCachePath } from "../app/constants";
|
|
16
26
|
import {
|
|
17
27
|
autoDownloadDisabledError,
|
|
18
28
|
downloadFailedError,
|
|
@@ -20,16 +30,8 @@ import {
|
|
|
20
30
|
lockFailedError,
|
|
21
31
|
modelNotCachedError,
|
|
22
32
|
modelNotFoundError,
|
|
23
|
-
} from
|
|
24
|
-
import { getLockPath, getManifestLockPath, withLock } from
|
|
25
|
-
import type { DownloadPolicy } from './policy';
|
|
26
|
-
import type {
|
|
27
|
-
DownloadProgress,
|
|
28
|
-
LlmResult,
|
|
29
|
-
ModelCacheEntry,
|
|
30
|
-
ModelType,
|
|
31
|
-
ProgressCallback,
|
|
32
|
-
} from './types';
|
|
33
|
+
} from "./errors";
|
|
34
|
+
import { getLockPath, getManifestLockPath, withLock } from "./lockfile";
|
|
33
35
|
|
|
34
36
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
37
|
// URI Parsing
|
|
@@ -41,14 +43,14 @@ const HF_PATH_PATTERN = /^([^/]+)\/([^/]+)\/(.+\.gguf)$/;
|
|
|
41
43
|
|
|
42
44
|
export type ParsedModelUri =
|
|
43
45
|
| {
|
|
44
|
-
scheme:
|
|
46
|
+
scheme: "hf";
|
|
45
47
|
org: string;
|
|
46
48
|
repo: string;
|
|
47
49
|
file: string;
|
|
48
50
|
quantization?: string;
|
|
49
51
|
}
|
|
50
52
|
| {
|
|
51
|
-
scheme:
|
|
53
|
+
scheme: "file";
|
|
52
54
|
file: string;
|
|
53
55
|
};
|
|
54
56
|
|
|
@@ -69,7 +71,7 @@ export function parseModelUri(
|
|
|
69
71
|
uri: string
|
|
70
72
|
): { ok: true; value: ParsedModelUri } | { ok: false; error: string } {
|
|
71
73
|
// Handle hf: scheme
|
|
72
|
-
if (uri.startsWith(
|
|
74
|
+
if (uri.startsWith("hf:")) {
|
|
73
75
|
const rest = uri.slice(3);
|
|
74
76
|
|
|
75
77
|
// Check for quantization shorthand: hf:org/repo:Q4_K_M
|
|
@@ -81,10 +83,10 @@ export function parseModelUri(
|
|
|
81
83
|
return {
|
|
82
84
|
ok: true,
|
|
83
85
|
value: {
|
|
84
|
-
scheme:
|
|
86
|
+
scheme: "hf",
|
|
85
87
|
org,
|
|
86
88
|
repo,
|
|
87
|
-
file:
|
|
89
|
+
file: "", // Will be resolved by node-llama-cpp
|
|
88
90
|
quantization: quant,
|
|
89
91
|
},
|
|
90
92
|
};
|
|
@@ -100,7 +102,7 @@ export function parseModelUri(
|
|
|
100
102
|
return {
|
|
101
103
|
ok: true,
|
|
102
104
|
value: {
|
|
103
|
-
scheme:
|
|
105
|
+
scheme: "hf",
|
|
104
106
|
org,
|
|
105
107
|
repo,
|
|
106
108
|
file,
|
|
@@ -113,12 +115,12 @@ export function parseModelUri(
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
// Handle file:// URLs (proper file URLs like file:///C:/path or file:///path)
|
|
116
|
-
if (uri.startsWith(
|
|
118
|
+
if (uri.startsWith("file://")) {
|
|
117
119
|
try {
|
|
118
120
|
const filePath = fileURLToPath(new URL(uri));
|
|
119
121
|
return {
|
|
120
122
|
ok: true,
|
|
121
|
-
value: { scheme:
|
|
123
|
+
value: { scheme: "file", file: filePath },
|
|
122
124
|
};
|
|
123
125
|
} catch {
|
|
124
126
|
return { ok: false, error: `Invalid file URL: ${uri}` };
|
|
@@ -126,14 +128,14 @@ export function parseModelUri(
|
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
// Handle simplified file: scheme (file:/path or file:C:\path)
|
|
129
|
-
if (uri.startsWith(
|
|
131
|
+
if (uri.startsWith("file:")) {
|
|
130
132
|
const path = uri.slice(5);
|
|
131
133
|
if (!path) {
|
|
132
|
-
return { ok: false, error:
|
|
134
|
+
return { ok: false, error: "Empty file path" };
|
|
133
135
|
}
|
|
134
136
|
return {
|
|
135
137
|
ok: true,
|
|
136
|
-
value: { scheme:
|
|
138
|
+
value: { scheme: "file", file: path },
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
141
|
|
|
@@ -141,7 +143,7 @@ export function parseModelUri(
|
|
|
141
143
|
if (isAbsolute(uri)) {
|
|
142
144
|
return {
|
|
143
145
|
ok: true,
|
|
144
|
-
value: { scheme:
|
|
146
|
+
value: { scheme: "file", file: uri },
|
|
145
147
|
};
|
|
146
148
|
}
|
|
147
149
|
|
|
@@ -152,7 +154,7 @@ export function parseModelUri(
|
|
|
152
154
|
* Convert parsed URI back to node-llama-cpp format.
|
|
153
155
|
*/
|
|
154
156
|
export function toNodeLlamaCppUri(parsed: ParsedModelUri): string {
|
|
155
|
-
if (parsed.scheme ===
|
|
157
|
+
if (parsed.scheme === "file") {
|
|
156
158
|
return parsed.file;
|
|
157
159
|
}
|
|
158
160
|
|
|
@@ -168,11 +170,11 @@ export function toNodeLlamaCppUri(parsed: ParsedModelUri): string {
|
|
|
168
170
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
169
171
|
|
|
170
172
|
interface Manifest {
|
|
171
|
-
version:
|
|
173
|
+
version: "1.0";
|
|
172
174
|
models: ModelCacheEntry[];
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
const MANIFEST_VERSION =
|
|
177
|
+
const MANIFEST_VERSION = "1.0" as const;
|
|
176
178
|
|
|
177
179
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
178
180
|
// ModelCache
|
|
@@ -185,7 +187,7 @@ export class ModelCache {
|
|
|
185
187
|
|
|
186
188
|
constructor(cacheDir?: string) {
|
|
187
189
|
this.dir = cacheDir ?? getModelsCachePath();
|
|
188
|
-
this.manifestPath = join(this.dir,
|
|
190
|
+
this.manifestPath = join(this.dir, "manifest.json");
|
|
189
191
|
}
|
|
190
192
|
|
|
191
193
|
/**
|
|
@@ -199,7 +201,7 @@ export class ModelCache {
|
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
// Local files: verify existence
|
|
202
|
-
if (parsed.value.scheme ===
|
|
204
|
+
if (parsed.value.scheme === "file") {
|
|
203
205
|
const exists = await this.fileExists(parsed.value.file);
|
|
204
206
|
if (!exists) {
|
|
205
207
|
return {
|
|
@@ -238,7 +240,7 @@ export class ModelCache {
|
|
|
238
240
|
}
|
|
239
241
|
|
|
240
242
|
// Local files: just verify
|
|
241
|
-
if (parsed.value.scheme ===
|
|
243
|
+
if (parsed.value.scheme === "file") {
|
|
242
244
|
const exists = await this.fileExists(parsed.value.file);
|
|
243
245
|
if (!exists) {
|
|
244
246
|
return {
|
|
@@ -266,7 +268,7 @@ export class ModelCache {
|
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
try {
|
|
269
|
-
const { resolveModelFile } = await import(
|
|
271
|
+
const { resolveModelFile } = await import("node-llama-cpp");
|
|
270
272
|
|
|
271
273
|
// Convert to node-llama-cpp format (handles quantization shorthand)
|
|
272
274
|
// node-llama-cpp needs hf: prefix to identify HuggingFace models
|
|
@@ -279,11 +281,11 @@ export class ModelCache {
|
|
|
279
281
|
// Type-safe check for download progress status
|
|
280
282
|
if (
|
|
281
283
|
status &&
|
|
282
|
-
typeof status ===
|
|
283
|
-
|
|
284
|
-
status.type ===
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
typeof status === "object" &&
|
|
285
|
+
"type" in status &&
|
|
286
|
+
status.type === "download" &&
|
|
287
|
+
"downloadedSize" in status &&
|
|
288
|
+
"totalSize" in status
|
|
287
289
|
) {
|
|
288
290
|
const s = status as {
|
|
289
291
|
downloadedSize: number;
|
|
@@ -340,7 +342,7 @@ export class ModelCache {
|
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
// Local files: just verify existence (no download needed)
|
|
343
|
-
if (parsed.value.scheme ===
|
|
345
|
+
if (parsed.value.scheme === "file") {
|
|
344
346
|
const exists = await this.fileExists(parsed.value.file);
|
|
345
347
|
if (!exists) {
|
|
346
348
|
return {
|
|
@@ -366,9 +368,9 @@ export class ModelCache {
|
|
|
366
368
|
// Acquire lock for download (prevents concurrent downloads of same model)
|
|
367
369
|
// Use hash for lock filename to avoid collisions and path issues
|
|
368
370
|
await mkdir(this.dir, { recursive: true });
|
|
369
|
-
const lockName = createHash(
|
|
371
|
+
const lockName = createHash("sha256")
|
|
370
372
|
.update(uri)
|
|
371
|
-
.digest(
|
|
373
|
+
.digest("hex")
|
|
372
374
|
.slice(0, 32);
|
|
373
375
|
const lockPath = getLockPath(join(this.dir, lockName));
|
|
374
376
|
|
|
@@ -409,7 +411,7 @@ export class ModelCache {
|
|
|
409
411
|
async getCachedPath(uri: string): Promise<string | null> {
|
|
410
412
|
// Handle file: URIs directly (check filesystem, not manifest)
|
|
411
413
|
const parsed = parseModelUri(uri);
|
|
412
|
-
if (parsed.ok && parsed.value.scheme ===
|
|
414
|
+
if (parsed.ok && parsed.value.scheme === "file") {
|
|
413
415
|
const exists = await this.fileExists(parsed.value.file);
|
|
414
416
|
return exists ? parsed.value.file : null;
|
|
415
417
|
}
|
|
@@ -505,7 +507,7 @@ export class ModelCache {
|
|
|
505
507
|
*/
|
|
506
508
|
private async readManifestFromDisk(): Promise<Manifest> {
|
|
507
509
|
try {
|
|
508
|
-
const content = await readFile(this.manifestPath,
|
|
510
|
+
const content = await readFile(this.manifestPath, "utf-8");
|
|
509
511
|
return JSON.parse(content) as Manifest;
|
|
510
512
|
} catch {
|
|
511
513
|
// No manifest or invalid - create empty
|
|
@@ -539,7 +541,7 @@ export class ModelCache {
|
|
|
539
541
|
});
|
|
540
542
|
|
|
541
543
|
if (result === null) {
|
|
542
|
-
throw new Error(
|
|
544
|
+
throw new Error("Failed to acquire manifest lock");
|
|
543
545
|
}
|
|
544
546
|
}
|
|
545
547
|
|
|
@@ -553,7 +555,7 @@ export class ModelCache {
|
|
|
553
555
|
const content = JSON.stringify(manifest, null, 2);
|
|
554
556
|
|
|
555
557
|
// Write to temp file with fsync
|
|
556
|
-
const fh = await open(tmpPath,
|
|
558
|
+
const fh = await open(tmpPath, "w");
|
|
557
559
|
try {
|
|
558
560
|
await fh.writeFile(content);
|
|
559
561
|
await fh.sync();
|
|
@@ -565,9 +567,9 @@ export class ModelCache {
|
|
|
565
567
|
await rename(tmpPath, this.manifestPath);
|
|
566
568
|
|
|
567
569
|
// Fsync parent directory for rename durability (best-effort, not supported on Windows)
|
|
568
|
-
if (process.platform !==
|
|
570
|
+
if (process.platform !== "win32") {
|
|
569
571
|
try {
|
|
570
|
-
const dirFh = await open(this.dir,
|
|
572
|
+
const dirFh = await open(this.dir, "r");
|
|
571
573
|
try {
|
|
572
574
|
await dirFh.sync();
|
|
573
575
|
} finally {
|
|
@@ -603,7 +605,7 @@ export class ModelCache {
|
|
|
603
605
|
type,
|
|
604
606
|
path: modelPath,
|
|
605
607
|
size,
|
|
606
|
-
checksum:
|
|
608
|
+
checksum: "", // TODO: compute SHA-256 for large files
|
|
607
609
|
cachedAt: new Date().toISOString(),
|
|
608
610
|
});
|
|
609
611
|
});
|
package/src/llm/errors.ts
CHANGED
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
11
|
|
|
12
12
|
export type LlmErrorCode =
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
13
|
+
| "MODEL_NOT_FOUND"
|
|
14
|
+
| "MODEL_NOT_CACHED"
|
|
15
|
+
| "MODEL_DOWNLOAD_FAILED"
|
|
16
|
+
| "MODEL_LOAD_FAILED"
|
|
17
|
+
| "MODEL_CORRUPTED"
|
|
18
|
+
| "INFERENCE_FAILED"
|
|
19
|
+
| "TIMEOUT"
|
|
20
|
+
| "OUT_OF_MEMORY"
|
|
21
|
+
| "INVALID_URI"
|
|
22
|
+
| "LOCK_FAILED"
|
|
23
|
+
| "AUTO_DOWNLOAD_DISABLED";
|
|
24
24
|
|
|
25
25
|
export interface LlmError {
|
|
26
26
|
code: LlmErrorCode;
|
|
@@ -59,19 +59,23 @@ function normalizeCause(
|
|
|
59
59
|
return { name: cause.name, message };
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
if (typeof cause ===
|
|
62
|
+
if (typeof cause === "string") {
|
|
63
63
|
return cause.length > MAX_CAUSE_LENGTH
|
|
64
64
|
? `${cause.slice(0, MAX_CAUSE_LENGTH)}...`
|
|
65
65
|
: cause;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
try {
|
|
69
|
-
|
|
69
|
+
// Handle objects with custom toString, otherwise use JSON
|
|
70
|
+
const str =
|
|
71
|
+
typeof cause === "object" && cause !== null
|
|
72
|
+
? JSON.stringify(cause)
|
|
73
|
+
: String(cause as string | number | boolean);
|
|
70
74
|
return str.length > MAX_CAUSE_LENGTH
|
|
71
75
|
? `${str.slice(0, MAX_CAUSE_LENGTH)}...`
|
|
72
76
|
: str;
|
|
73
77
|
} catch {
|
|
74
|
-
return
|
|
78
|
+
return "[unserializable cause]";
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
|
|
@@ -80,7 +84,7 @@ function normalizeCause(
|
|
|
80
84
|
*/
|
|
81
85
|
export function llmError(
|
|
82
86
|
code: LlmErrorCode,
|
|
83
|
-
opts: Omit<LlmError,
|
|
87
|
+
opts: Omit<LlmError, "code">
|
|
84
88
|
): LlmError {
|
|
85
89
|
return {
|
|
86
90
|
code,
|
|
@@ -94,10 +98,10 @@ export function llmError(
|
|
|
94
98
|
*/
|
|
95
99
|
export function isRetryable(code: LlmErrorCode): boolean {
|
|
96
100
|
return [
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
"MODEL_DOWNLOAD_FAILED",
|
|
102
|
+
"TIMEOUT",
|
|
103
|
+
"INFERENCE_FAILED",
|
|
104
|
+
"LOCK_FAILED",
|
|
101
105
|
].includes(code);
|
|
102
106
|
}
|
|
103
107
|
|
|
@@ -106,7 +110,7 @@ export function isRetryable(code: LlmErrorCode): boolean {
|
|
|
106
110
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
107
111
|
|
|
108
112
|
export function modelNotFoundError(uri: string, details?: string): LlmError {
|
|
109
|
-
return llmError(
|
|
113
|
+
return llmError("MODEL_NOT_FOUND", {
|
|
110
114
|
message: details
|
|
111
115
|
? `Model not found: ${details}`
|
|
112
116
|
: `Model not found: ${uri}`,
|
|
@@ -117,9 +121,9 @@ export function modelNotFoundError(uri: string, details?: string): LlmError {
|
|
|
117
121
|
|
|
118
122
|
export function modelNotCachedError(
|
|
119
123
|
uri: string,
|
|
120
|
-
modelType:
|
|
124
|
+
modelType: "embed" | "rerank" | "gen"
|
|
121
125
|
): LlmError {
|
|
122
|
-
return llmError(
|
|
126
|
+
return llmError("MODEL_NOT_CACHED", {
|
|
123
127
|
message: `${modelType} model not cached`,
|
|
124
128
|
modelUri: uri,
|
|
125
129
|
retryable: false,
|
|
@@ -128,7 +132,7 @@ export function modelNotCachedError(
|
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
export function downloadFailedError(uri: string, cause?: unknown): LlmError {
|
|
131
|
-
return llmError(
|
|
135
|
+
return llmError("MODEL_DOWNLOAD_FAILED", {
|
|
132
136
|
message: `Failed to download model: ${uri}`,
|
|
133
137
|
modelUri: uri,
|
|
134
138
|
retryable: true,
|
|
@@ -137,27 +141,27 @@ export function downloadFailedError(uri: string, cause?: unknown): LlmError {
|
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
export function loadFailedError(uri: string, cause?: unknown): LlmError {
|
|
140
|
-
return llmError(
|
|
144
|
+
return llmError("MODEL_LOAD_FAILED", {
|
|
141
145
|
message: `Failed to load model: ${uri}`,
|
|
142
146
|
modelUri: uri,
|
|
143
147
|
retryable: false,
|
|
144
148
|
cause,
|
|
145
|
-
suggestion:
|
|
149
|
+
suggestion: "Run: gno doctor",
|
|
146
150
|
});
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
export function corruptedError(uri: string, cause?: unknown): LlmError {
|
|
150
|
-
return llmError(
|
|
154
|
+
return llmError("MODEL_CORRUPTED", {
|
|
151
155
|
message: `Model file corrupted: ${uri}`,
|
|
152
156
|
modelUri: uri,
|
|
153
157
|
retryable: false,
|
|
154
158
|
cause,
|
|
155
|
-
suggestion:
|
|
159
|
+
suggestion: "Run: gno models clear && gno models pull",
|
|
156
160
|
});
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
export function inferenceFailedError(uri: string, cause?: unknown): LlmError {
|
|
160
|
-
return llmError(
|
|
164
|
+
return llmError("INFERENCE_FAILED", {
|
|
161
165
|
message: `Inference failed for model: ${uri}`,
|
|
162
166
|
modelUri: uri,
|
|
163
167
|
retryable: true,
|
|
@@ -167,10 +171,10 @@ export function inferenceFailedError(uri: string, cause?: unknown): LlmError {
|
|
|
167
171
|
|
|
168
172
|
export function timeoutError(
|
|
169
173
|
uri: string,
|
|
170
|
-
operation:
|
|
174
|
+
operation: "load" | "inference",
|
|
171
175
|
timeoutMs: number
|
|
172
176
|
): LlmError {
|
|
173
|
-
return llmError(
|
|
177
|
+
return llmError("TIMEOUT", {
|
|
174
178
|
message: `${operation} timed out after ${timeoutMs}ms`,
|
|
175
179
|
modelUri: uri,
|
|
176
180
|
retryable: true,
|
|
@@ -178,17 +182,17 @@ export function timeoutError(
|
|
|
178
182
|
}
|
|
179
183
|
|
|
180
184
|
export function outOfMemoryError(uri: string, cause?: unknown): LlmError {
|
|
181
|
-
return llmError(
|
|
185
|
+
return llmError("OUT_OF_MEMORY", {
|
|
182
186
|
message: `Out of memory loading model: ${uri}`,
|
|
183
187
|
modelUri: uri,
|
|
184
188
|
retryable: false,
|
|
185
189
|
cause,
|
|
186
|
-
suggestion:
|
|
190
|
+
suggestion: "Try a smaller quantization (Q4_K_M) or close other apps",
|
|
187
191
|
});
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
export function invalidUriError(uri: string, details: string): LlmError {
|
|
191
|
-
return llmError(
|
|
195
|
+
return llmError("INVALID_URI", {
|
|
192
196
|
message: `Invalid model URI: ${details}`,
|
|
193
197
|
modelUri: uri,
|
|
194
198
|
retryable: false,
|
|
@@ -196,16 +200,16 @@ export function invalidUriError(uri: string, details: string): LlmError {
|
|
|
196
200
|
}
|
|
197
201
|
|
|
198
202
|
export function lockFailedError(uri: string): LlmError {
|
|
199
|
-
return llmError(
|
|
203
|
+
return llmError("LOCK_FAILED", {
|
|
200
204
|
message: `Failed to acquire lock for model download: ${uri}`,
|
|
201
205
|
modelUri: uri,
|
|
202
206
|
retryable: true,
|
|
203
|
-
suggestion:
|
|
207
|
+
suggestion: "Another process may be downloading. Wait and retry.",
|
|
204
208
|
});
|
|
205
209
|
}
|
|
206
210
|
|
|
207
211
|
export function autoDownloadDisabledError(uri: string): LlmError {
|
|
208
|
-
return llmError(
|
|
212
|
+
return llmError("AUTO_DOWNLOAD_DISABLED", {
|
|
209
213
|
message: `Model not cached and auto-download disabled: ${uri}`,
|
|
210
214
|
modelUri: uri,
|
|
211
215
|
retryable: false,
|
package/src/llm/index.ts
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// Re-export config types (source of truth in config/types.ts)
|
|
8
|
-
export type { ModelConfig, ModelPreset } from
|
|
9
|
-
export type { ParsedModelUri } from
|
|
8
|
+
export type { ModelConfig, ModelPreset } from "../config/types";
|
|
9
|
+
export type { ParsedModelUri } from "./cache";
|
|
10
10
|
// Cache
|
|
11
|
-
export { ModelCache, parseModelUri, toNodeLlamaCppUri } from
|
|
11
|
+
export { ModelCache, parseModelUri, toNodeLlamaCppUri } from "./cache";
|
|
12
12
|
// Errors
|
|
13
|
-
export type { LlmError, LlmErrorCode } from
|
|
13
|
+
export type { LlmError, LlmErrorCode } from "./errors";
|
|
14
14
|
export {
|
|
15
15
|
corruptedError,
|
|
16
16
|
downloadFailedError,
|
|
@@ -23,15 +23,15 @@ export {
|
|
|
23
23
|
modelNotFoundError,
|
|
24
24
|
outOfMemoryError,
|
|
25
25
|
timeoutError,
|
|
26
|
-
} from
|
|
26
|
+
} from "./errors";
|
|
27
27
|
// Adapter
|
|
28
|
-
export { createLlmAdapter, LlmAdapter } from
|
|
28
|
+
export { createLlmAdapter, LlmAdapter } from "./nodeLlamaCpp/adapter";
|
|
29
29
|
// Lifecycle
|
|
30
30
|
export {
|
|
31
31
|
getModelManager,
|
|
32
32
|
ModelManager,
|
|
33
33
|
resetModelManager,
|
|
34
|
-
} from
|
|
34
|
+
} from "./nodeLlamaCpp/lifecycle";
|
|
35
35
|
// Registry
|
|
36
36
|
export {
|
|
37
37
|
getActivePreset,
|
|
@@ -39,7 +39,7 @@ export {
|
|
|
39
39
|
getPreset,
|
|
40
40
|
listPresets,
|
|
41
41
|
resolveModelUri,
|
|
42
|
-
} from
|
|
42
|
+
} from "./registry";
|
|
43
43
|
// Types
|
|
44
44
|
export type {
|
|
45
45
|
DownloadProgress,
|
|
@@ -55,4 +55,4 @@ export type {
|
|
|
55
55
|
ProgressCallback,
|
|
56
56
|
RerankPort,
|
|
57
57
|
RerankScore,
|
|
58
|
-
} from
|
|
58
|
+
} from "./types";
|
package/src/llm/lockfile.ts
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* @module src/llm/lockfile
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { open, rename, rm, stat } from
|
|
8
|
+
import { open, rename, rm, stat } from "node:fs/promises";
|
|
9
9
|
// node:os: hostname and user for lock ownership
|
|
10
|
-
import { hostname, userInfo } from
|
|
10
|
+
import { hostname, userInfo } from "node:os";
|
|
11
11
|
// node:path: join for manifest lock path
|
|
12
|
-
import { join } from
|
|
12
|
+
import { join } from "node:path";
|
|
13
13
|
|
|
14
14
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
15
|
// Constants
|
|
@@ -101,7 +101,7 @@ async function createLockExclusive(
|
|
|
101
101
|
const content = JSON.stringify(meta, null, 2);
|
|
102
102
|
|
|
103
103
|
// Create lock file with O_EXCL - fails if exists
|
|
104
|
-
const fh = await open(lockPath,
|
|
104
|
+
const fh = await open(lockPath, "wx");
|
|
105
105
|
try {
|
|
106
106
|
await fh.writeFile(content);
|
|
107
107
|
await fh.sync();
|
|
@@ -147,7 +147,7 @@ export async function acquireLock(
|
|
|
147
147
|
};
|
|
148
148
|
} catch (e) {
|
|
149
149
|
// EEXIST means lock exists
|
|
150
|
-
if (e && typeof e ===
|
|
150
|
+
if (e && typeof e === "object" && "code" in e && e.code === "EEXIST") {
|
|
151
151
|
// Check if stale
|
|
152
152
|
const stale = await isLockStale(lockPath, ttlMs);
|
|
153
153
|
|
|
@@ -212,5 +212,5 @@ export function getLockPath(modelPath: string): string {
|
|
|
212
212
|
* Get the manifest lock path for a cache directory.
|
|
213
213
|
*/
|
|
214
214
|
export function getManifestLockPath(cacheDir: string): string {
|
|
215
|
-
return join(cacheDir,
|
|
215
|
+
return join(cacheDir, "manifest.lock");
|
|
216
216
|
}
|
|
@@ -5,21 +5,22 @@
|
|
|
5
5
|
* @module src/llm/nodeLlamaCpp/adapter
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { Config } from
|
|
9
|
-
import {
|
|
10
|
-
import type { DownloadPolicy } from '../policy';
|
|
11
|
-
import { getActivePreset, getModelConfig } from '../registry';
|
|
8
|
+
import type { Config } from "../../config/types";
|
|
9
|
+
import type { DownloadPolicy } from "../policy";
|
|
12
10
|
import type {
|
|
13
11
|
EmbeddingPort,
|
|
14
12
|
GenerationPort,
|
|
15
13
|
LlmResult,
|
|
16
14
|
ProgressCallback,
|
|
17
15
|
RerankPort,
|
|
18
|
-
} from
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
16
|
+
} from "../types";
|
|
17
|
+
|
|
18
|
+
import { ModelCache } from "../cache";
|
|
19
|
+
import { getActivePreset, getModelConfig } from "../registry";
|
|
20
|
+
import { NodeLlamaCppEmbedding } from "./embedding";
|
|
21
|
+
import { NodeLlamaCppGeneration } from "./generation";
|
|
22
|
+
import { getModelManager, type ModelManager } from "./lifecycle";
|
|
23
|
+
import { NodeLlamaCppRerank } from "./rerank";
|
|
23
24
|
|
|
24
25
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
26
|
// Types
|
|
@@ -66,7 +67,7 @@ export class LlmAdapter {
|
|
|
66
67
|
// Ensure model is available (downloads if policy allows)
|
|
67
68
|
const resolved = await this.cache.ensureModel(
|
|
68
69
|
uri,
|
|
69
|
-
|
|
70
|
+
"embed",
|
|
70
71
|
policy,
|
|
71
72
|
options?.onProgress
|
|
72
73
|
);
|
|
@@ -95,7 +96,7 @@ export class LlmAdapter {
|
|
|
95
96
|
// Ensure model is available (downloads if policy allows)
|
|
96
97
|
const resolved = await this.cache.ensureModel(
|
|
97
98
|
uri,
|
|
98
|
-
|
|
99
|
+
"gen",
|
|
99
100
|
policy,
|
|
100
101
|
options?.onProgress
|
|
101
102
|
);
|
|
@@ -124,7 +125,7 @@ export class LlmAdapter {
|
|
|
124
125
|
// Ensure model is available (downloads if policy allows)
|
|
125
126
|
const resolved = await this.cache.ensureModel(
|
|
126
127
|
uri,
|
|
127
|
-
|
|
128
|
+
"rerank",
|
|
128
129
|
policy,
|
|
129
130
|
options?.onProgress
|
|
130
131
|
);
|