mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix(memory): keep llama runtime optional (#71425)
* fix(memory): keep llama runtime optional * fix(memory): harden optional llama runtime guard
This commit is contained in:
@@ -29,6 +29,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Browser: add viewport coordinate clicks for managed and existing-session automation, plus `openclaw browser click-coords` for CLI use. (#54452) Thanks @dluttz.
|
||||
- Browser/config: support per-profile `browser.profiles.<name>.headless` overrides for locally launched browser profiles, so one profile can run headless without forcing all browser profiles headless. Thanks @nakamotoliu.
|
||||
- Plugins/PDF: move local PDF extraction into a bundled `document-extract` plugin so core no longer owns `pdfjs-dist` or PDF image-rendering dependencies. Thanks @vincentkoc.
|
||||
- Dependencies/memory: stop installing `node-llama-cpp` by default; local embeddings now load it only when operators install the optional runtime package. Thanks @vincentkoc.
|
||||
- Matrix: require full cross-signing identity trust for self-device verification and add `openclaw matrix verify self` so operators can establish that trust from the CLI. (#70401) Thanks @gumadeiras.
|
||||
- WebChat/sessions: keep runtime-only prompt context out of visible transcript history and scrub legacy wrappers from session history surfaces. Thanks @91wan.
|
||||
- Gradium: add a bundled text-to-speech provider with voice-note and telephony output support. (#64958) Thanks @LaurentMazare.
|
||||
|
||||
@@ -38,8 +38,9 @@ To set a provider explicitly:
|
||||
|
||||
Without an embedding provider, only keyword search is available.
|
||||
|
||||
To force the built-in local embedding provider, point `local.modelPath` at a
|
||||
GGUF file:
|
||||
To force the built-in local embedding provider, install the optional
|
||||
`node-llama-cpp` runtime package next to OpenClaw, then point `local.modelPath`
|
||||
at a GGUF file:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -66,7 +67,7 @@ GGUF file:
|
||||
| Voyage | `voyage` | Yes | |
|
||||
| Mistral | `mistral` | Yes | |
|
||||
| Ollama | `ollama` | No | Local, set explicitly |
|
||||
| Local | `local` | Yes (first) | GGUF model, ~0.6 GB download |
|
||||
| Local | `local` | Yes (first) | Optional `node-llama-cpp` runtime |
|
||||
|
||||
Auto-detection picks the first provider whose API key can be resolved, in the
|
||||
order shown. Set `memorySearch.provider` to override.
|
||||
|
||||
@@ -15,7 +15,8 @@ binary, and can index content beyond your workspace memory files.
|
||||
- **Reranking and query expansion** for better recall.
|
||||
- **Index extra directories** -- project docs, team notes, anything on disk.
|
||||
- **Index session transcripts** -- recall earlier conversations.
|
||||
- **Fully local** -- runs via Bun + node-llama-cpp, auto-downloads GGUF models.
|
||||
- **Fully local** -- runs with the optional node-llama-cpp runtime package and
|
||||
auto-downloads GGUF models.
|
||||
- **Automatic fallback** -- if QMD is unavailable, OpenClaw falls back to the
|
||||
builtin engine seamlessly.
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ explicitly:
|
||||
}
|
||||
```
|
||||
|
||||
For local embeddings with no API key, use `provider: "local"` (requires
|
||||
node-llama-cpp).
|
||||
For local embeddings with no API key, install the optional `node-llama-cpp`
|
||||
runtime package next to OpenClaw and use `provider: "local"`.
|
||||
|
||||
## Supported providers
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ import { getProviderEnvVars } from "openclaw/plugin-sdk/provider-env-vars";
|
||||
import { formatErrorMessage } from "../dreaming-shared.js";
|
||||
import { filterUnregisteredMemoryEmbeddingProviderAdapters } from "./provider-adapter-registration.js";
|
||||
|
||||
const NODE_LLAMA_CPP_RUNTIME_PACKAGE = "node-llama-cpp";
|
||||
const NODE_LLAMA_CPP_RUNTIME_VERSION = "3.18.1";
|
||||
const NODE_LLAMA_CPP_INSTALL_SPEC = `${NODE_LLAMA_CPP_RUNTIME_PACKAGE}@${NODE_LLAMA_CPP_RUNTIME_VERSION}`;
|
||||
|
||||
export type BuiltinMemoryEmbeddingProviderDoctorMetadata = {
|
||||
providerId: string;
|
||||
authProviderId: string;
|
||||
@@ -24,7 +28,7 @@ function isNodeLlamaCppMissing(err: unknown): boolean {
|
||||
return false;
|
||||
}
|
||||
const code = (err as Error & { code?: unknown }).code;
|
||||
return code === "ERR_MODULE_NOT_FOUND" && err.message.includes("node-llama-cpp");
|
||||
return code === "ERR_MODULE_NOT_FOUND" && err.message.includes(NODE_LLAMA_CPP_RUNTIME_PACKAGE);
|
||||
}
|
||||
|
||||
function listRemoteEmbeddingSetupHints(): string[] {
|
||||
@@ -55,9 +59,9 @@ function formatLocalSetupError(err: unknown): string {
|
||||
"To enable local embeddings:",
|
||||
"1) Use Node 24 (recommended for installs/updates; Node 22 LTS, currently 22.14+, remains supported)",
|
||||
missing
|
||||
? "2) Reinstall OpenClaw (this should install node-llama-cpp): npm i -g openclaw@latest"
|
||||
? `2) Install optional local embedding runtime next to OpenClaw: npm i -g ${NODE_LLAMA_CPP_INSTALL_SPEC}`
|
||||
: null,
|
||||
"3) If you use pnpm: pnpm approve-builds (select node-llama-cpp), then pnpm rebuild node-llama-cpp",
|
||||
`3) If you use pnpm: pnpm approve-builds (select ${NODE_LLAMA_CPP_RUNTIME_PACKAGE}), then pnpm rebuild ${NODE_LLAMA_CPP_RUNTIME_PACKAGE}`,
|
||||
...listRemoteEmbeddingSetupHints(),
|
||||
]
|
||||
.filter(Boolean)
|
||||
|
||||
@@ -1658,14 +1658,6 @@
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^4.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"node-llama-cpp": "3.18.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"node-llama-cpp": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"axios": "1.15.0",
|
||||
"follow-redirects": "1.16.0",
|
||||
|
||||
714
pnpm-lock.yaml
generated
714
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -137,11 +137,6 @@
|
||||
"class": "core-runtime",
|
||||
"risk": ["parser", "markdown"]
|
||||
},
|
||||
"node-llama-cpp": {
|
||||
"owner": "capability:memory-local-embeddings",
|
||||
"class": "optional-peer-runtime",
|
||||
"risk": ["native", "local-model-runtime", "large-transitive-cone"]
|
||||
},
|
||||
"openai": {
|
||||
"owner": "provider:openai",
|
||||
"class": "default-runtime-initially",
|
||||
|
||||
@@ -22,6 +22,8 @@ type PackageJson = {
|
||||
license?: string;
|
||||
repository?: { url?: string } | string;
|
||||
bin?: Record<string, string>;
|
||||
dependencies?: Record<string, string>;
|
||||
optionalDependencies?: Record<string, string>;
|
||||
peerDependencies?: Record<string, string>;
|
||||
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
|
||||
};
|
||||
@@ -58,6 +60,7 @@ export type NpmDistTagMirrorAuth = {
|
||||
source: "node-auth-token" | "npm-token" | "none";
|
||||
};
|
||||
const EXPECTED_REPOSITORY_URL = "https://github.com/openclaw/openclaw";
|
||||
const OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE = "node-llama-cpp";
|
||||
const MAX_CALVER_DISTANCE_DAYS = 2;
|
||||
const REQUIRED_PACKED_PATHS = [
|
||||
PACKAGE_DIST_INVENTORY_RELATIVE_PATH,
|
||||
@@ -266,15 +269,25 @@ export function collectReleasePackageMetadataErrors(pkg: PackageJson): string[]
|
||||
`package.json bin.openclaw must be "openclaw.mjs"; found "${pkg.bin?.openclaw ?? ""}".`,
|
||||
);
|
||||
}
|
||||
if (pkg.peerDependencies?.["node-llama-cpp"] !== "3.18.1") {
|
||||
if (pkg.dependencies?.[OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE]) {
|
||||
errors.push(
|
||||
`package.json peerDependencies["node-llama-cpp"] must be "3.18.1"; found "${
|
||||
pkg.peerDependencies?.["node-llama-cpp"] ?? ""
|
||||
}".`,
|
||||
`package.json dependencies["${OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE}"] must be omitted; keep it optional.`,
|
||||
);
|
||||
}
|
||||
if (pkg.peerDependenciesMeta?.["node-llama-cpp"]?.optional !== true) {
|
||||
errors.push('package.json peerDependenciesMeta["node-llama-cpp"].optional must be true.');
|
||||
if (pkg.optionalDependencies?.[OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE]) {
|
||||
errors.push(
|
||||
`package.json optionalDependencies["${OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE}"] must be omitted; keep it operator-installed.`,
|
||||
);
|
||||
}
|
||||
if (pkg.peerDependencies?.[OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE]) {
|
||||
errors.push(
|
||||
`package.json peerDependencies["${OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE}"] must be omitted; keep it optional.`,
|
||||
);
|
||||
}
|
||||
if (pkg.peerDependenciesMeta?.[OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE]) {
|
||||
errors.push(
|
||||
`package.json peerDependenciesMeta["${OPTIONAL_LOCAL_EMBEDDING_RUNTIME_PACKAGE}"] must be omitted; keep it optional.`,
|
||||
);
|
||||
}
|
||||
|
||||
return errors;
|
||||
|
||||
@@ -519,13 +519,11 @@ describe("collectReleasePackageMetadataErrors", () => {
|
||||
license: "MIT",
|
||||
repository: { url: "git+https://github.com/openclaw/openclaw.git" },
|
||||
bin: { openclaw: "openclaw.mjs" },
|
||||
peerDependencies: { "node-llama-cpp": "3.18.1" },
|
||||
peerDependenciesMeta: { "node-llama-cpp": { optional: true } },
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("requires node-llama-cpp to stay an optional peer", () => {
|
||||
it("rejects node-llama-cpp as a peer dependency", () => {
|
||||
expect(
|
||||
collectReleasePackageMetadataErrors({
|
||||
name: "openclaw",
|
||||
@@ -534,7 +532,39 @@ describe("collectReleasePackageMetadataErrors", () => {
|
||||
repository: { url: "git+https://github.com/openclaw/openclaw.git" },
|
||||
bin: { openclaw: "openclaw.mjs" },
|
||||
peerDependencies: { "node-llama-cpp": "3.18.1" },
|
||||
peerDependenciesMeta: { "node-llama-cpp": { optional: true } },
|
||||
}),
|
||||
).toContain('package.json peerDependenciesMeta["node-llama-cpp"].optional must be true.');
|
||||
).toEqual([
|
||||
'package.json peerDependencies["node-llama-cpp"] must be omitted; keep it optional.',
|
||||
'package.json peerDependenciesMeta["node-llama-cpp"] must be omitted; keep it optional.',
|
||||
]);
|
||||
});
|
||||
|
||||
it("rejects node-llama-cpp as a direct runtime dependency", () => {
|
||||
expect(
|
||||
collectReleasePackageMetadataErrors({
|
||||
name: "openclaw",
|
||||
description: "Multi-channel AI gateway with extensible messaging integrations",
|
||||
license: "MIT",
|
||||
repository: { url: "git+https://github.com/openclaw/openclaw.git" },
|
||||
bin: { openclaw: "openclaw.mjs" },
|
||||
dependencies: { "node-llama-cpp": "3.18.1" },
|
||||
}),
|
||||
).toContain('package.json dependencies["node-llama-cpp"] must be omitted; keep it optional.');
|
||||
});
|
||||
|
||||
it("rejects node-llama-cpp as an optional dependency", () => {
|
||||
expect(
|
||||
collectReleasePackageMetadataErrors({
|
||||
name: "openclaw",
|
||||
description: "Multi-channel AI gateway with extensible messaging integrations",
|
||||
license: "MIT",
|
||||
repository: { url: "git+https://github.com/openclaw/openclaw.git" },
|
||||
bin: { openclaw: "openclaw.mjs" },
|
||||
optionalDependencies: { "node-llama-cpp": "3.18.1" },
|
||||
}),
|
||||
).toContain(
|
||||
'package.json optionalDependencies["node-llama-cpp"] must be omitted; keep it operator-installed.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user