harper-knowledge 0.1.2 → 0.2.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.
@@ -152,9 +152,10 @@ export async function initEmbeddingModel(config) {
152
152
  return;
153
153
  }
154
154
  const modelPath = await downloadModelIfNeeded(modelName);
155
- // Load the model with node-llama-cpp
155
+ // Load the model with node-llama-cpp (use prebuilt binaries only —
156
+ // build: "never" avoids requiring cmake/make on deployment targets)
156
157
  const { getLlama } = (await import("node-llama-cpp"));
157
- llama = await getLlama({ progressLogs: false });
158
+ llama = await getLlama({ progressLogs: false, build: "never" });
158
159
  embeddingModel = await llama.loadModel({ modelPath });
159
160
  embeddingContext = await embeddingModel.createEmbeddingContext({
160
161
  contextSize: "auto",
@@ -72,6 +72,19 @@ export declare function linkSupersedes(newId: string, oldId: string): Promise<vo
72
72
  * @throws Error if any entry does not exist
73
73
  */
74
74
  export declare function linkSiblings(ids: string[]): Promise<void>;
75
+ /**
76
+ * Reindex embeddings for all entries missing them.
77
+ *
78
+ * Iterates every KnowledgeEntry, generates embeddings for any that
79
+ * don't have one yet, and writes them back. Returns counts of
80
+ * processed, updated, and failed entries.
81
+ */
82
+ export declare function reindexEmbeddings(): Promise<{
83
+ total: number;
84
+ updated: number;
85
+ failed: number;
86
+ skipped: number;
87
+ }>;
75
88
  /**
76
89
  * Link two entries as related.
77
90
  *
@@ -209,6 +209,44 @@ export async function linkSiblings(ids) {
209
209
  });
210
210
  }
211
211
  }
212
+ /**
213
+ * Reindex embeddings for all entries missing them.
214
+ *
215
+ * Iterates every KnowledgeEntry, generates embeddings for any that
216
+ * don't have one yet, and writes them back. Returns counts of
217
+ * processed, updated, and failed entries.
218
+ */
219
+ export async function reindexEmbeddings() {
220
+ let total = 0;
221
+ let updated = 0;
222
+ let failed = 0;
223
+ let skipped = 0;
224
+ for await (const record of databases.kb.KnowledgeEntry.search({})) {
225
+ total++;
226
+ const entry = record;
227
+ // Skip entries that already have embeddings
228
+ if (entry.embedding && entry.embedding.length > 0) {
229
+ skipped++;
230
+ continue;
231
+ }
232
+ try {
233
+ const embeddingText = `${entry.title}\n\n${entry.content}`;
234
+ const embedding = await generateEmbedding(embeddingText);
235
+ await databases.kb.KnowledgeEntry.put({
236
+ ...record,
237
+ id: entry.id,
238
+ embedding,
239
+ });
240
+ updated++;
241
+ logger?.info?.(`Reindexed embedding for entry: ${entry.id}`);
242
+ }
243
+ catch (error) {
244
+ failed++;
245
+ logger?.warn?.(`Failed to reindex entry ${entry.id}:`, error.message);
246
+ }
247
+ }
248
+ return { total, updated, failed, skipped };
249
+ }
212
250
  /**
213
251
  * Link two entries as related.
214
252
  *
package/dist/mcp/tools.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import * as z from "zod/v4";
8
8
  import { search } from "../core/search.js";
9
- import { createEntry, getEntry, updateEntry, stripEmbedding, } from "../core/entries.js";
9
+ import { createEntry, getEntry, updateEntry, stripEmbedding, reindexEmbeddings, } from "../core/entries.js";
10
10
  import { listTags } from "../core/tags.js";
11
11
  import { submitTriage } from "../core/triage.js";
12
12
  import { generateEmbedding } from "../core/embeddings.js";
@@ -494,4 +494,29 @@ export function registerTools(server, caller) {
494
494
  return errorContent("Failed to get edit history. Please try again.");
495
495
  }
496
496
  });
497
+ // =========================================================================
498
+ // 9. knowledge_reindex — Backfill missing embeddings
499
+ // =========================================================================
500
+ server.registerTool("knowledge_reindex", {
501
+ description: "Reindex embeddings for all knowledge entries that are missing them. " +
502
+ "Use this after the embedding model becomes available on a deployment " +
503
+ "where entries were initially created without embeddings. " +
504
+ "Requires write access.",
505
+ inputSchema: {},
506
+ }, async (_input, { authInfo }) => {
507
+ const caller = authInfo;
508
+ if (!caller?.scopes?.includes("mcp:write")) {
509
+ return errorContent("Write access required to reindex embeddings.");
510
+ }
511
+ try {
512
+ const result = await reindexEmbeddings();
513
+ return jsonContent({
514
+ message: "Embedding reindex complete",
515
+ ...result,
516
+ });
517
+ }
518
+ catch (error) {
519
+ return errorContent("Failed to reindex embeddings. Please try again.");
520
+ }
521
+ });
497
522
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harper-knowledge",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Knowledge base plugin for Harper with MCP server integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -55,6 +55,9 @@
55
55
  "prettier": "3.8.1",
56
56
  "typescript": "5.9.3"
57
57
  },
58
+ "optionalDependencies": {
59
+ "@node-llama-cpp/linux-x64": "^3.15.1"
60
+ },
58
61
  "peerDependencies": {
59
62
  "harperdb": ">=4.7.0"
60
63
  },