@gmickel/gno 1.46.0 → 2.0.0
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 +1 -1
- package/THIRD_PARTY_NOTICES.md +46 -0
- package/assets/skill/SKILL.md +7 -6
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +32 -33
- package/bunfig.toml +2 -0
- package/package.json +40 -26
- package/spec/cli.md +21 -4
- package/spec/db/schema.sql +146 -1
- package/spec/mcp.md +26 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +2 -0
- package/src/cli/commands/ask.ts +6 -1
- package/src/cli/commands/daemon.ts +21 -8
- package/src/cli/commands/embed.ts +77 -41
- package/src/cli/detach.ts +3 -2
- package/src/config/types.ts +3 -3
- package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
- package/src/converters/adapters/officeparser/adapter.ts +1 -2
- package/src/converters/versions.ts +6 -8
- package/src/core/context-evidence.ts +8 -4
- package/src/core/job-manager.ts +95 -13
- package/src/core/network-boundary-inventory.ts +10 -0
- package/src/core/shutdown-budget.ts +45 -0
- package/src/embed/backlog.ts +107 -4
- package/src/embed/batch.ts +42 -2
- package/src/embed/fingerprint.ts +16 -0
- package/src/embed/retry.ts +113 -5
- package/src/embed/variant-backlog.ts +105 -0
- package/src/embed/variant-plan.ts +62 -0
- package/src/embed/variant-retry.ts +113 -0
- package/src/ingestion/graph-reconciliation.ts +327 -0
- package/src/ingestion/sync.ts +9 -272
- package/src/llm/http-inference.ts +6 -0
- package/src/llm/httpEmbedding.ts +37 -6
- package/src/llm/httpGeneration.ts +18 -3
- package/src/llm/httpRerank.ts +23 -5
- package/src/llm/inference-cancellation.ts +168 -0
- package/src/llm/inference-scope.ts +202 -0
- package/src/llm/lazy-ports.ts +115 -0
- package/src/llm/native-worker/client.ts +541 -0
- package/src/llm/native-worker/dispatcher.ts +228 -0
- package/src/llm/native-worker/embedding-identity.ts +33 -0
- package/src/llm/native-worker/entry.ts +173 -0
- package/src/llm/native-worker/errors.ts +32 -0
- package/src/llm/native-worker/evaluation.ts +16 -0
- package/src/llm/native-worker/owned-exit.ts +108 -0
- package/src/llm/native-worker/owner.ts +141 -0
- package/src/llm/native-worker/ports.ts +317 -0
- package/src/llm/native-worker/protocol.ts +442 -0
- package/src/llm/native-worker/runtime-config.ts +92 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
- package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
- package/src/llm/nodeLlamaCpp/generation.ts +34 -5
- package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
- package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
- package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
- package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
- package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
- package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
- package/src/llm/types.ts +35 -5
- package/src/mcp/context.ts +27 -0
- package/src/mcp/http-transport.ts +12 -10
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-profile.ts +30 -8
- package/src/mcp/tools/context.ts +8 -11
- package/src/mcp/tools/embed.ts +1 -1
- package/src/mcp/tools/index-cmd.ts +1 -1
- package/src/mcp/tools/index.ts +10 -8
- package/src/mcp/tools/query.ts +14 -30
- package/src/mcp/tools/vsearch.ts +1 -1
- package/src/pipeline/answer.ts +23 -3
- package/src/pipeline/claim-verifier.ts +6 -0
- package/src/pipeline/expansion.ts +43 -40
- package/src/pipeline/explain.ts +6 -2
- package/src/pipeline/filters.ts +63 -0
- package/src/pipeline/fusion.ts +29 -9
- package/src/pipeline/graph-retrieval.ts +29 -9
- package/src/pipeline/hybrid.ts +198 -55
- package/src/pipeline/hydration.ts +161 -0
- package/src/pipeline/owner-fusion.ts +87 -0
- package/src/pipeline/rerank.ts +35 -11
- package/src/pipeline/search.ts +13 -2
- package/src/pipeline/types.ts +5 -3
- package/src/pipeline/vsearch.ts +87 -7
- package/src/sdk/client.ts +47 -3
- package/src/sdk/embed.ts +63 -39
- package/src/serve/background-runtime.ts +1 -1
- package/src/serve/context.ts +41 -56
- package/src/serve/embed-scheduler.ts +58 -35
- package/src/serve/public/components/IndexingProgress.tsx +46 -60
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/shiki-language-ids.ts +14 -0
- package/src/serve/resident-admission.ts +36 -36
- package/src/serve/resident-background-work.ts +20 -2
- package/src/serve/resident-request.ts +11 -5
- package/src/serve/resident-runtime.ts +97 -61
- package/src/serve/resident-shutdown.ts +153 -0
- package/src/serve/routes/api.ts +3 -1
- package/src/serve/server.ts +47 -26
- package/src/store/migrations/028-vector-variants.ts +54 -0
- package/src/store/migrations/029-graph-reference-state.ts +77 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +251 -183
- package/src/store/sqlite/eligibility.ts +174 -0
- package/src/store/sqlite/graph-edge-application.ts +66 -0
- package/src/store/sqlite/graph-reference-state.ts +194 -0
- package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
- package/src/store/types.ts +80 -12
- package/src/store/vector/eligibility.ts +36 -0
- package/src/store/vector/freshness.ts +33 -6
- package/src/store/vector/lazy.ts +81 -0
- package/src/store/vector/sqlite-vec.ts +106 -54
- package/src/store/vector/stats.ts +14 -3
- package/src/store/vector/types.ts +35 -2
- package/src/store/vector/variant-search.ts +192 -0
- package/src/store/vector/variants.ts +451 -0
- package/vendor/converters/markitdown-ts/LICENSE +21 -0
- package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
- package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
- package/vendor/converters/markitdown-ts/package.json +77 -0
- package/vendor/converters/officeparser/LICENSE +21 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
- package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
- package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
- package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
- package/vendor/converters/officeparser/dist/cli.js +381 -0
- package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
- package/vendor/converters/officeparser/dist/defaults.js +218 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
- package/vendor/converters/officeparser/dist/index.d.ts +60 -0
- package/vendor/converters/officeparser/dist/index.js +72 -0
- package/vendor/converters/officeparser/dist/index.mjs +18 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
- package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
- package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
- package/vendor/converters/officeparser/dist/types.js +107 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
- package/vendor/converters/officeparser/package.json +147 -0
- package/vendor/converters/upstream-manifest.json +124 -0
- package/vendor/dependency-fixes/README.md +77 -0
- package/vendor/dependency-fixes/vendor-converters.py +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-627emwpj.js +0 -75
- /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
|
@@ -1,33 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import type { LlamaInitOptions } from "./lifecycle-options";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
resolveLlamaGpuMode,
|
|
5
|
+
resolveLlamaBuildMode,
|
|
6
|
+
resolveLlamaBackendInitTimeoutMs,
|
|
7
|
+
shouldRetryLlamaWithCpu,
|
|
8
|
+
} from "./lifecycle-options";
|
|
9
|
+
export {
|
|
10
|
+
resolveLlamaGpuMode,
|
|
11
|
+
resolveLlamaBuildMode,
|
|
12
|
+
resolveLlamaBackendInitTimeoutMs,
|
|
13
|
+
shouldRetryLlamaWithCpu,
|
|
14
|
+
} from "./lifecycle-options";
|
|
15
|
+
export type { LlamaGpuMode, LlamaBuildMode } from "./lifecycle-options";
|
|
11
16
|
|
|
12
17
|
import type { ModelConfig } from "../../config/types";
|
|
13
18
|
import type { LlmResult, LoadedModel, ModelType } from "../types";
|
|
14
19
|
|
|
15
20
|
import { loadFailedError, outOfMemoryError, timeoutError } from "../errors";
|
|
21
|
+
import { installSimulatorLifetimeGuard } from "./simulator-install";
|
|
16
22
|
|
|
17
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
23
|
// Types
|
|
19
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
24
|
|
|
21
25
|
type Llama = Awaited<ReturnType<typeof import("node-llama-cpp").getLlama>>;
|
|
22
26
|
type LlamaModel = Awaited<ReturnType<Llama["loadModel"]>>;
|
|
23
|
-
export type LlamaGpuMode = "auto" | "metal" | "vulkan" | "cuda" | false;
|
|
24
|
-
export type LlamaBuildMode = "never" | "autoAttempt";
|
|
25
|
-
|
|
26
|
-
type LlamaInitOptions = LlamaOptions & {
|
|
27
|
-
build: LlamaBuildMode;
|
|
28
|
-
gpu: LlamaGpuMode;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
27
|
interface CachedModel {
|
|
32
28
|
uri: string;
|
|
33
29
|
type: ModelType;
|
|
@@ -50,100 +46,14 @@ export interface ModelLifecycleStats {
|
|
|
50
46
|
inflightLoads: number;
|
|
51
47
|
}
|
|
52
48
|
|
|
53
|
-
let invalidGpuModeWarned = false;
|
|
54
|
-
let invalidBuildModeWarned = false;
|
|
55
49
|
let gpuFallbackWarned = false;
|
|
56
50
|
let backendTimeoutWarned = false;
|
|
57
51
|
|
|
58
|
-
const DEFAULT_BACKEND_INIT_TIMEOUT_MS = 30_000;
|
|
59
|
-
|
|
60
|
-
export function resolveLlamaGpuMode(
|
|
61
|
-
env: NodeJS.ProcessEnv = process.env
|
|
62
|
-
): LlamaGpuMode {
|
|
63
|
-
const raw = (env.GNO_LLAMA_GPU ?? env.NODE_LLAMA_CPP_GPU ?? "auto")
|
|
64
|
-
.trim()
|
|
65
|
-
.toLowerCase();
|
|
66
|
-
if (!raw || raw === "auto") {
|
|
67
|
-
return "auto";
|
|
68
|
-
}
|
|
69
|
-
if (raw === "metal" || raw === "vulkan" || raw === "cuda") {
|
|
70
|
-
return raw;
|
|
71
|
-
}
|
|
72
|
-
if (
|
|
73
|
-
raw === "false" ||
|
|
74
|
-
raw === "off" ||
|
|
75
|
-
raw === "none" ||
|
|
76
|
-
raw === "disable" ||
|
|
77
|
-
raw === "disabled" ||
|
|
78
|
-
raw === "0"
|
|
79
|
-
) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
if (!invalidGpuModeWarned) {
|
|
83
|
-
invalidGpuModeWarned = true;
|
|
84
|
-
console.warn(
|
|
85
|
-
`[llama] Invalid GNO_LLAMA_GPU/NODE_LLAMA_CPP_GPU value "${raw}", using auto`
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return "auto";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function resolveLlamaBuildMode(
|
|
92
|
-
env: NodeJS.ProcessEnv = process.env
|
|
93
|
-
): LlamaBuildMode {
|
|
94
|
-
const raw = (env.GNO_LLAMA_BUILD ?? "never").trim().toLowerCase();
|
|
95
|
-
if (
|
|
96
|
-
!raw ||
|
|
97
|
-
raw === "never" ||
|
|
98
|
-
raw === "prebuilt" ||
|
|
99
|
-
raw === "prebuilt-only"
|
|
100
|
-
) {
|
|
101
|
-
return "never";
|
|
102
|
-
}
|
|
103
|
-
if (
|
|
104
|
-
raw === "autoattempt" ||
|
|
105
|
-
raw === "auto-attempt" ||
|
|
106
|
-
raw === "source" ||
|
|
107
|
-
raw === "build"
|
|
108
|
-
) {
|
|
109
|
-
return "autoAttempt";
|
|
110
|
-
}
|
|
111
|
-
if (!invalidBuildModeWarned) {
|
|
112
|
-
invalidBuildModeWarned = true;
|
|
113
|
-
console.warn(`[llama] Invalid GNO_LLAMA_BUILD value "${raw}", using never`);
|
|
114
|
-
}
|
|
115
|
-
return "never";
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function resolveLlamaBackendInitTimeoutMs(
|
|
119
|
-
env: NodeJS.ProcessEnv = process.env
|
|
120
|
-
): number {
|
|
121
|
-
const raw = env.GNO_LLAMA_INIT_TIMEOUT_MS;
|
|
122
|
-
if (!raw) {
|
|
123
|
-
return DEFAULT_BACKEND_INIT_TIMEOUT_MS;
|
|
124
|
-
}
|
|
125
|
-
const parsed = Number.parseInt(raw, 10);
|
|
126
|
-
return Number.isFinite(parsed) && parsed > 0
|
|
127
|
-
? parsed
|
|
128
|
-
: DEFAULT_BACKEND_INIT_TIMEOUT_MS;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function shouldRetryLlamaWithCpu(
|
|
132
|
-
gpu: LlamaGpuMode,
|
|
133
|
-
platformName = platform()
|
|
134
|
-
): boolean {
|
|
135
|
-
if (gpu === false) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
return gpu !== "auto" || platformName === "win32";
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
142
|
-
// ModelManager
|
|
143
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
144
|
-
|
|
145
52
|
export class ModelManager {
|
|
146
53
|
private llama: Llama | null = null;
|
|
54
|
+
private llamaInit: Promise<Llama> | null = null;
|
|
55
|
+
private closing: Promise<void> | null = null;
|
|
56
|
+
private readonly lateCleanup = new Set<Promise<void>>();
|
|
147
57
|
private readonly models: Map<string, CachedModel> = new Map();
|
|
148
58
|
private readonly disposalTimers: Map<string, ReturnType<typeof setTimeout>> =
|
|
149
59
|
new Map();
|
|
@@ -152,22 +62,38 @@ export class ModelManager {
|
|
|
152
62
|
private readonly leaseDrainWaiters = new Set<() => void>();
|
|
153
63
|
private readonly config: ModelConfig;
|
|
154
64
|
private activeLeases = 0;
|
|
65
|
+
private broadLeases = 0;
|
|
66
|
+
private readonly modelLeases = new Map<string, number>();
|
|
67
|
+
private readonly expiresAt = new Map<string, number>();
|
|
68
|
+
private readonly retiringModels = new Map<string, Promise<void>>();
|
|
155
69
|
private leaseAcquisitions = 0;
|
|
156
70
|
private leaseReleases = 0;
|
|
157
71
|
private loadAttempts = 0;
|
|
158
72
|
private loadSuccesses = 0;
|
|
159
73
|
private loadFailures = 0;
|
|
160
74
|
|
|
161
|
-
constructor(
|
|
75
|
+
constructor(
|
|
76
|
+
config: ModelConfig,
|
|
77
|
+
private readonly awaitNativeLoadSettlement = false
|
|
78
|
+
) {
|
|
162
79
|
this.config = config;
|
|
163
80
|
}
|
|
164
81
|
|
|
165
|
-
/**
|
|
166
|
-
* Get or initialize the Llama instance.
|
|
167
|
-
* Uses lazy loading - only imports node-llama-cpp on first use.
|
|
168
|
-
*/
|
|
169
82
|
async getLlama(): Promise<Llama> {
|
|
83
|
+
if (this.closing) throw new Error("Model manager is disposing");
|
|
84
|
+
if (this.llama) return this.llama;
|
|
85
|
+
this.llamaInit ??= this.initializeLlama().finally(() => {
|
|
86
|
+
this.llamaInit = null;
|
|
87
|
+
});
|
|
88
|
+
return this.llamaInit;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async initializeLlama(): Promise<Llama> {
|
|
92
|
+
// A timed-out native constructor still owns work until its loser is cleaned.
|
|
93
|
+
await Promise.allSettled(this.lateCleanup);
|
|
94
|
+
if (this.closing) throw new Error("Model manager is disposing");
|
|
170
95
|
if (!this.llama) {
|
|
96
|
+
await installSimulatorLifetimeGuard();
|
|
171
97
|
const { getLlama, LlamaLogLevel } = await import("node-llama-cpp");
|
|
172
98
|
const gpu = resolveLlamaGpuMode();
|
|
173
99
|
const build = resolveLlamaBuildMode();
|
|
@@ -184,7 +110,11 @@ export class ModelManager {
|
|
|
184
110
|
timeoutMs
|
|
185
111
|
);
|
|
186
112
|
} catch (error) {
|
|
187
|
-
if (
|
|
113
|
+
if (
|
|
114
|
+
this.closing ||
|
|
115
|
+
(error instanceof Error && error.name === "TimeoutError") ||
|
|
116
|
+
!shouldRetryLlamaWithCpu(gpu)
|
|
117
|
+
) {
|
|
188
118
|
throw error;
|
|
189
119
|
}
|
|
190
120
|
if (!gpuFallbackWarned) {
|
|
@@ -206,6 +136,12 @@ export class ModelManager {
|
|
|
206
136
|
);
|
|
207
137
|
}
|
|
208
138
|
}
|
|
139
|
+
if (this.closing) {
|
|
140
|
+
const llama = this.llama;
|
|
141
|
+
this.llama = null;
|
|
142
|
+
await llama.dispose();
|
|
143
|
+
throw new Error("Model manager disposed during initialization");
|
|
144
|
+
}
|
|
209
145
|
return this.llama;
|
|
210
146
|
}
|
|
211
147
|
|
|
@@ -215,21 +151,46 @@ export class ModelManager {
|
|
|
215
151
|
timeoutMs: number
|
|
216
152
|
): Promise<Llama> {
|
|
217
153
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
154
|
+
let expired = false;
|
|
155
|
+
const initializationDeadline = performance.now() + timeoutMs;
|
|
156
|
+
const initialization = getLlama(options).then(async (llama) => {
|
|
157
|
+
if (expired || performance.now() >= initializationDeadline) {
|
|
158
|
+
await llama.dispose();
|
|
159
|
+
throw new DOMException(
|
|
160
|
+
"Backend initialization expired",
|
|
161
|
+
"TimeoutError"
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return llama;
|
|
165
|
+
});
|
|
166
|
+
const cleanup = initialization.then(
|
|
167
|
+
() => {},
|
|
168
|
+
() => {}
|
|
169
|
+
);
|
|
170
|
+
this.lateCleanup.add(cleanup);
|
|
171
|
+
void cleanup.finally(() => this.lateCleanup.delete(cleanup));
|
|
218
172
|
try {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
})
|
|
232
|
-
|
|
173
|
+
const deadline = new Promise<never>((_, reject) => {
|
|
174
|
+
timeoutId = setTimeout(() => {
|
|
175
|
+
expired = true;
|
|
176
|
+
if (!backendTimeoutWarned) {
|
|
177
|
+
backendTimeoutWarned = true;
|
|
178
|
+
console.warn(
|
|
179
|
+
`[llama] Backend initialization timed out after ${timeoutMs}ms`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const error = new Error(`Backend init timeout after ${timeoutMs}ms`);
|
|
183
|
+
error.name = "TimeoutError";
|
|
184
|
+
reject(error);
|
|
185
|
+
}, timeoutMs);
|
|
186
|
+
});
|
|
187
|
+
// Native child completion must wait for actual backend settlement even
|
|
188
|
+
// after the init budget expires. Expiry still rejects and forbids fallback.
|
|
189
|
+
if (this.awaitNativeLoadSettlement) {
|
|
190
|
+
void deadline.catch(() => {});
|
|
191
|
+
return await initialization;
|
|
192
|
+
}
|
|
193
|
+
return await Promise.race([initialization, deadline]);
|
|
233
194
|
} finally {
|
|
234
195
|
if (timeoutId) {
|
|
235
196
|
clearTimeout(timeoutId);
|
|
@@ -237,19 +198,23 @@ export class ModelManager {
|
|
|
237
198
|
}
|
|
238
199
|
}
|
|
239
200
|
|
|
240
|
-
/**
|
|
241
|
-
* Load a model by path.
|
|
242
|
-
* Uses caching, inflight deduplication, and TTL-based disposal.
|
|
243
|
-
*/
|
|
244
201
|
loadModel(
|
|
245
202
|
modelPath: string,
|
|
246
203
|
uri: string,
|
|
247
|
-
type: ModelType
|
|
204
|
+
type: ModelType,
|
|
205
|
+
signal?: AbortSignal
|
|
248
206
|
): Promise<LlmResult<LoadedModel>> {
|
|
207
|
+
const retiring = this.retiringModels.get(uri);
|
|
208
|
+
if (retiring)
|
|
209
|
+
return retiring.then(() => this.loadModel(modelPath, uri, type, signal));
|
|
210
|
+
if (this.closing)
|
|
211
|
+
return Promise.resolve({
|
|
212
|
+
ok: false,
|
|
213
|
+
error: loadFailedError(uri, new Error("Model manager is disposing")),
|
|
214
|
+
});
|
|
249
215
|
// Check cache first
|
|
250
216
|
const cached = this.models.get(uri);
|
|
251
217
|
if (cached) {
|
|
252
|
-
this.resetDisposalTimer(uri);
|
|
253
218
|
return Promise.resolve({
|
|
254
219
|
ok: true as const,
|
|
255
220
|
value: {
|
|
@@ -268,50 +233,76 @@ export class ModelManager {
|
|
|
268
233
|
}
|
|
269
234
|
|
|
270
235
|
// Start new load with cleanup
|
|
271
|
-
const loadPromise = this.loadModelInternal(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
236
|
+
const loadPromise = this.loadModelInternal(
|
|
237
|
+
modelPath,
|
|
238
|
+
uri,
|
|
239
|
+
type,
|
|
240
|
+
signal
|
|
241
|
+
).finally(() => {
|
|
242
|
+
this.inflightLoads.delete(uri);
|
|
243
|
+
});
|
|
276
244
|
this.inflightLoads.set(uri, loadPromise);
|
|
277
245
|
return loadPromise;
|
|
278
246
|
}
|
|
279
247
|
|
|
280
|
-
/**
|
|
281
|
-
* Internal model loading with timeout handling.
|
|
282
|
-
*/
|
|
283
248
|
private async loadModelInternal(
|
|
284
249
|
modelPath: string,
|
|
285
250
|
uri: string,
|
|
286
|
-
type: ModelType
|
|
251
|
+
type: ModelType,
|
|
252
|
+
signal?: AbortSignal
|
|
287
253
|
): Promise<LlmResult<LoadedModel>> {
|
|
288
254
|
this.loadAttempts += 1;
|
|
289
255
|
const timeoutMs = this.config.loadTimeout;
|
|
290
256
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
291
257
|
let timedOut = false;
|
|
258
|
+
const controller = new AbortController();
|
|
259
|
+
const loadSignal = signal
|
|
260
|
+
? AbortSignal.any([signal, controller.signal])
|
|
261
|
+
: controller.signal;
|
|
292
262
|
|
|
293
263
|
// Capture loadPromise outside try block so we can dispose it on timeout
|
|
294
264
|
let loadPromise: Promise<LlamaModel> | null = null;
|
|
295
265
|
|
|
296
266
|
try {
|
|
297
267
|
const llama = await this.getLlama();
|
|
298
|
-
|
|
268
|
+
loadSignal.throwIfAborted();
|
|
269
|
+
const loadDeadline = performance.now() + timeoutMs;
|
|
270
|
+
loadPromise = llama.loadModel({ modelPath, loadSignal });
|
|
299
271
|
|
|
300
272
|
// Create timeout with proper cleanup
|
|
301
273
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
302
274
|
timeoutId = setTimeout(() => {
|
|
303
275
|
timedOut = true;
|
|
276
|
+
controller.abort(
|
|
277
|
+
new DOMException("Model load timed out", "TimeoutError")
|
|
278
|
+
);
|
|
304
279
|
reject(new Error(`Load timeout after ${timeoutMs}ms`));
|
|
305
280
|
}, timeoutMs);
|
|
306
281
|
});
|
|
307
282
|
|
|
308
|
-
|
|
283
|
+
// The isolated child cannot report completion while a noncooperative
|
|
284
|
+
// loader still owns native allocations. Parent cancellation delivers early.
|
|
285
|
+
if (this.awaitNativeLoadSettlement) void timeoutPromise.catch(() => {});
|
|
286
|
+
const model = this.awaitNativeLoadSettlement
|
|
287
|
+
? await loadPromise
|
|
288
|
+
: await Promise.race([loadPromise, timeoutPromise]);
|
|
289
|
+
if (this.awaitNativeLoadSettlement && performance.now() >= loadDeadline)
|
|
290
|
+
timedOut = true;
|
|
291
|
+
if (this.awaitNativeLoadSettlement && (timedOut || loadSignal.aborted)) {
|
|
292
|
+
await model.dispose();
|
|
293
|
+
if (timedOut) throw new Error("Model load timeout");
|
|
294
|
+
loadSignal.throwIfAborted();
|
|
295
|
+
}
|
|
309
296
|
|
|
310
297
|
// Clear timeout on success
|
|
311
298
|
if (timeoutId) {
|
|
312
299
|
clearTimeout(timeoutId);
|
|
313
300
|
}
|
|
314
301
|
|
|
302
|
+
if (this.closing) {
|
|
303
|
+
await model.dispose();
|
|
304
|
+
throw new Error("Model manager disposed during model loading");
|
|
305
|
+
}
|
|
315
306
|
const now = Date.now();
|
|
316
307
|
const cachedModel: CachedModel = {
|
|
317
308
|
uri,
|
|
@@ -321,6 +312,7 @@ export class ModelManager {
|
|
|
321
312
|
};
|
|
322
313
|
|
|
323
314
|
this.models.set(uri, cachedModel);
|
|
315
|
+
this.expiresAt.set(uri, Date.now() + this.config.warmModelTtl);
|
|
324
316
|
this.setDisposalTimer(uri);
|
|
325
317
|
this.loadSuccesses += 1;
|
|
326
318
|
|
|
@@ -341,11 +333,11 @@ export class ModelManager {
|
|
|
341
333
|
}
|
|
342
334
|
|
|
343
335
|
// Dispose late-arriving model after timeout to prevent memory leak
|
|
344
|
-
if (timedOut && loadPromise) {
|
|
345
|
-
loadPromise.then(
|
|
336
|
+
if (timedOut && loadPromise && !this.awaitNativeLoadSettlement) {
|
|
337
|
+
const cleanup = loadPromise.then(
|
|
346
338
|
(model) => {
|
|
347
339
|
// Dispose model that arrived after timeout
|
|
348
|
-
model.dispose().catch(() => {
|
|
340
|
+
return model.dispose().catch(() => {
|
|
349
341
|
// Ignore dispose errors
|
|
350
342
|
});
|
|
351
343
|
},
|
|
@@ -353,10 +345,16 @@ export class ModelManager {
|
|
|
353
345
|
// Ignore load errors after timeout
|
|
354
346
|
}
|
|
355
347
|
);
|
|
348
|
+
this.lateCleanup.add(cleanup);
|
|
349
|
+
void cleanup.finally(() => this.lateCleanup.delete(cleanup));
|
|
356
350
|
}
|
|
357
351
|
|
|
358
352
|
if (e instanceof Error) {
|
|
359
|
-
if (
|
|
353
|
+
if (
|
|
354
|
+
timedOut ||
|
|
355
|
+
e.name === "TimeoutError" ||
|
|
356
|
+
e.message.includes("timeout")
|
|
357
|
+
) {
|
|
360
358
|
return {
|
|
361
359
|
ok: false,
|
|
362
360
|
error: timeoutError(uri, "load", this.config.loadTimeout),
|
|
@@ -370,23 +368,23 @@ export class ModelManager {
|
|
|
370
368
|
}
|
|
371
369
|
}
|
|
372
370
|
|
|
373
|
-
/**
|
|
374
|
-
* Get a loaded model by URI (no loading).
|
|
375
|
-
*/
|
|
376
371
|
getLoadedModel(uri: string): CachedModel | undefined {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
this.resetDisposalTimer(uri);
|
|
380
|
-
}
|
|
381
|
-
return model;
|
|
372
|
+
if (this.closing) return;
|
|
373
|
+
return this.models.get(uri);
|
|
382
374
|
}
|
|
383
375
|
|
|
384
|
-
|
|
385
|
-
|
|
376
|
+
acquireLease(uri?: string, activity = true): ModelLease {
|
|
377
|
+
if (this.closing) throw new Error("Model manager is disposing");
|
|
386
378
|
this.activeLeases += 1;
|
|
387
379
|
this.leaseAcquisitions += 1;
|
|
388
|
-
|
|
389
|
-
this.
|
|
380
|
+
if (uri === undefined) this.broadLeases += 1;
|
|
381
|
+
else this.modelLeases.set(uri, (this.modelLeases.get(uri) ?? 0) + 1);
|
|
382
|
+
for (const [key, timer] of this.disposalTimers) {
|
|
383
|
+
if (uri === undefined || key === uri) {
|
|
384
|
+
clearTimeout(timer);
|
|
385
|
+
this.disposalTimers.delete(key);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
390
388
|
|
|
391
389
|
let released = false;
|
|
392
390
|
return {
|
|
@@ -395,10 +393,22 @@ export class ModelManager {
|
|
|
395
393
|
released = true;
|
|
396
394
|
this.activeLeases = Math.max(0, this.activeLeases - 1);
|
|
397
395
|
this.leaseReleases += 1;
|
|
396
|
+
if (uri === undefined) this.broadLeases -= 1;
|
|
397
|
+
else {
|
|
398
|
+
const count = (this.modelLeases.get(uri) ?? 1) - 1;
|
|
399
|
+
if (count) this.modelLeases.set(uri, count);
|
|
400
|
+
else this.modelLeases.delete(uri);
|
|
401
|
+
}
|
|
402
|
+
for (const key of this.models.keys()) {
|
|
403
|
+
if (uri === undefined || key === uri) {
|
|
404
|
+
if (activity)
|
|
405
|
+
this.expiresAt.set(key, Date.now() + this.config.warmModelTtl);
|
|
406
|
+
this.resetDisposalTimer(key);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
398
409
|
if (this.activeLeases === 0) {
|
|
399
410
|
for (const resolve of this.leaseDrainWaiters) resolve();
|
|
400
411
|
this.leaseDrainWaiters.clear();
|
|
401
|
-
for (const uri of this.models.keys()) this.setDisposalTimer(uri);
|
|
402
412
|
}
|
|
403
413
|
},
|
|
404
414
|
};
|
|
@@ -417,21 +427,14 @@ export class ModelManager {
|
|
|
417
427
|
};
|
|
418
428
|
}
|
|
419
429
|
|
|
420
|
-
/**
|
|
421
|
-
* Check if a model is loaded.
|
|
422
|
-
*/
|
|
423
430
|
isLoaded(uri: string): boolean {
|
|
424
431
|
return this.models.has(uri);
|
|
425
432
|
}
|
|
426
433
|
|
|
427
|
-
/**
|
|
428
|
-
* Dispose a specific model.
|
|
429
|
-
*/
|
|
430
434
|
async dispose(uri: string): Promise<void> {
|
|
431
|
-
if (this.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
+
if (this.broadLeases > 0 || this.modelLeases.has(uri)) return;
|
|
436
|
+
const retiring = this.retiringModels.get(uri);
|
|
437
|
+
if (retiring) return retiring;
|
|
435
438
|
const cached = this.models.get(uri);
|
|
436
439
|
if (!cached) {
|
|
437
440
|
return;
|
|
@@ -444,20 +447,29 @@ export class ModelManager {
|
|
|
444
447
|
this.disposalTimers.delete(uri);
|
|
445
448
|
}
|
|
446
449
|
|
|
447
|
-
//
|
|
448
|
-
try {
|
|
449
|
-
await cached.model.dispose();
|
|
450
|
-
} catch {
|
|
451
|
-
// Ignore disposal errors
|
|
452
|
-
}
|
|
453
|
-
|
|
450
|
+
// Remove ownership before yielding so acquisitions cannot see a retiring model.
|
|
454
451
|
this.models.delete(uri);
|
|
452
|
+
this.expiresAt.delete(uri);
|
|
453
|
+
const cleanup = cached.model
|
|
454
|
+
.dispose()
|
|
455
|
+
.catch(() => {})
|
|
456
|
+
.finally(() => {
|
|
457
|
+
this.retiringModels.delete(uri);
|
|
458
|
+
});
|
|
459
|
+
this.retiringModels.set(uri, cleanup);
|
|
460
|
+
this.lateCleanup.add(cleanup);
|
|
461
|
+
await cleanup;
|
|
462
|
+
this.lateCleanup.delete(cleanup);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
disposeAll(): Promise<void> {
|
|
466
|
+
this.closing ??= this.disposeAllInternal().finally(() => {
|
|
467
|
+
this.closing = null;
|
|
468
|
+
});
|
|
469
|
+
return this.closing;
|
|
455
470
|
}
|
|
456
471
|
|
|
457
|
-
|
|
458
|
-
* Dispose all loaded models.
|
|
459
|
-
*/
|
|
460
|
-
async disposeAll(): Promise<void> {
|
|
472
|
+
private async disposeAllInternal(): Promise<void> {
|
|
461
473
|
if (this.activeLeases > 0) {
|
|
462
474
|
await new Promise<void>((resolve) => this.leaseDrainWaiters.add(resolve));
|
|
463
475
|
}
|
|
@@ -479,13 +491,13 @@ export class ModelManager {
|
|
|
479
491
|
this.models.delete(uri);
|
|
480
492
|
}
|
|
481
493
|
|
|
482
|
-
|
|
494
|
+
await this.llamaInit?.catch(() => {});
|
|
495
|
+
await Promise.allSettled(this.lateCleanup);
|
|
496
|
+
const llama = this.llama;
|
|
483
497
|
this.llama = null;
|
|
498
|
+
await llama?.dispose();
|
|
484
499
|
}
|
|
485
500
|
|
|
486
|
-
/**
|
|
487
|
-
* Get list of loaded models.
|
|
488
|
-
*/
|
|
489
501
|
getLoadedModels(): Array<{ uri: string; type: ModelType; loadedAt: number }> {
|
|
490
502
|
return Array.from(this.models.values()).map((m) => ({
|
|
491
503
|
uri: m.uri,
|
|
@@ -494,17 +506,18 @@ export class ModelManager {
|
|
|
494
506
|
}));
|
|
495
507
|
}
|
|
496
508
|
|
|
497
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
498
509
|
// Private
|
|
499
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
500
510
|
|
|
501
511
|
private setDisposalTimer(uri: string): void {
|
|
502
|
-
if (this.
|
|
503
|
-
const timer = setTimeout(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
512
|
+
if (this.broadLeases > 0 || this.modelLeases.has(uri)) return;
|
|
513
|
+
const timer = setTimeout(
|
|
514
|
+
() => {
|
|
515
|
+
this.dispose(uri).catch(() => {
|
|
516
|
+
// Ignore disposal errors in timer callback
|
|
517
|
+
});
|
|
518
|
+
},
|
|
519
|
+
Math.max(0, (this.expiresAt.get(uri) ?? Date.now()) - Date.now())
|
|
520
|
+
);
|
|
508
521
|
|
|
509
522
|
// Allow CLI processes to exit without waiting for TTL timer
|
|
510
523
|
if (typeof timer.unref === "function") {
|
|
@@ -523,15 +536,10 @@ export class ModelManager {
|
|
|
523
536
|
}
|
|
524
537
|
}
|
|
525
538
|
|
|
526
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
527
539
|
// Singleton
|
|
528
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
529
540
|
|
|
530
541
|
let defaultManager: ModelManager | null = null;
|
|
531
542
|
|
|
532
|
-
/**
|
|
533
|
-
* Get the default ModelManager singleton.
|
|
534
|
-
*/
|
|
535
543
|
export function getModelManager(config: ModelConfig): ModelManager {
|
|
536
544
|
if (!defaultManager) {
|
|
537
545
|
defaultManager = new ModelManager(config);
|
|
@@ -539,9 +547,6 @@ export function getModelManager(config: ModelConfig): ModelManager {
|
|
|
539
547
|
return defaultManager;
|
|
540
548
|
}
|
|
541
549
|
|
|
542
|
-
/**
|
|
543
|
-
* Reset the default manager (for testing).
|
|
544
|
-
*/
|
|
545
550
|
export async function resetModelManager(): Promise<void> {
|
|
546
551
|
if (defaultManager) {
|
|
547
552
|
await defaultManager.disposeAll();
|