@powerhousedao/knowledge-note 1.0.3 → 1.0.5

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.
Files changed (30) hide show
  1. package/README.md +359 -122
  2. package/dist/editors/knowledge-vault/components/DriveExplorer.d.ts.map +1 -1
  3. package/dist/editors/knowledge-vault/components/DriveExplorer.js +8 -2
  4. package/dist/editors/knowledge-vault/components/SearchView.d.ts +2 -0
  5. package/dist/editors/knowledge-vault/components/SearchView.d.ts.map +1 -0
  6. package/dist/editors/knowledge-vault/components/SearchView.js +96 -0
  7. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts +25 -0
  8. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts.map +1 -0
  9. package/dist/editors/knowledge-vault/hooks/use-graph-search.js +207 -0
  10. package/dist/package.json +2 -1
  11. package/dist/processors/graph-indexer/embedder.d.ts +5 -0
  12. package/dist/processors/graph-indexer/embedder.d.ts.map +1 -0
  13. package/dist/processors/graph-indexer/embedder.js +27 -0
  14. package/dist/processors/graph-indexer/embedding-store.d.ts +11 -0
  15. package/dist/processors/graph-indexer/embedding-store.d.ts.map +1 -0
  16. package/dist/processors/graph-indexer/embedding-store.js +70 -0
  17. package/dist/processors/graph-indexer/index.d.ts.map +1 -1
  18. package/dist/processors/graph-indexer/index.js +48 -0
  19. package/dist/processors/graph-indexer/migrations.d.ts.map +1 -1
  20. package/dist/processors/graph-indexer/migrations.js +35 -0
  21. package/dist/processors/graph-indexer/query.d.ts +21 -0
  22. package/dist/processors/graph-indexer/query.d.ts.map +1 -1
  23. package/dist/processors/graph-indexer/query.js +154 -13
  24. package/dist/processors/graph-indexer/schema.d.ts +11 -0
  25. package/dist/processors/graph-indexer/schema.d.ts.map +1 -1
  26. package/dist/style.css +63 -0
  27. package/dist/subgraphs/knowledge-graph/subgraph.d.ts +321 -12
  28. package/dist/subgraphs/knowledge-graph/subgraph.d.ts.map +1 -1
  29. package/dist/subgraphs/knowledge-graph/subgraph.js +237 -15
  30. package/package.json +2 -1
@@ -2,6 +2,8 @@ import { gql } from "graphql-tag";
2
2
  import { BaseSubgraph } from "@powerhousedao/reactor-api";
3
3
  import { GraphIndexerProcessor } from "../../processors/graph-indexer/index.js";
4
4
  import { createGraphQuery } from "../../processors/graph-indexer/query.js";
5
+ import { generateEmbedding } from "../../processors/graph-indexer/embedder.js";
6
+ import { searchSimilar, getEmbedding, upsertEmbedding, } from "../../processors/graph-indexer/embedding-store.js";
5
7
  export class KnowledgeGraphSubgraph extends BaseSubgraph {
6
8
  name = "knowledgeGraph";
7
9
  typeDefs = gql `
@@ -12,6 +14,11 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
12
14
  description: String
13
15
  noteType: String
14
16
  status: String
17
+ content: String
18
+ author: String
19
+ sourceOrigin: String
20
+ createdAt: String
21
+ topics: [String!]!
15
22
  updatedAt: String!
16
23
  }
17
24
 
@@ -36,6 +43,17 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
36
43
  viaLinkType: String
37
44
  }
38
45
 
46
+ type TopicInfo {
47
+ name: String!
48
+ noteCount: Int!
49
+ }
50
+
51
+ type RelatedNode {
52
+ node: KnowledgeGraphNode!
53
+ sharedTopics: [String!]!
54
+ sharedTopicCount: Int!
55
+ }
56
+
39
57
  extend type Query {
40
58
  knowledgeGraphNodes(driveId: ID!): [KnowledgeGraphNode!]!
41
59
  knowledgeGraphEdges(driveId: ID!): [KnowledgeGraphEdge!]!
@@ -72,6 +90,46 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
72
90
  documentId: String!
73
91
  ): [KnowledgeGraphEdge!]!
74
92
 
93
+ knowledgeGraphTopics(driveId: ID!): [TopicInfo!]!
94
+ knowledgeGraphByTopic(
95
+ driveId: ID!
96
+ topic: String!
97
+ ): [KnowledgeGraphNode!]!
98
+ knowledgeGraphRelatedByTopic(
99
+ driveId: ID!
100
+ documentId: String!
101
+ limit: Int
102
+ ): [RelatedNode!]!
103
+ knowledgeGraphFullSearch(
104
+ driveId: ID!
105
+ query: String!
106
+ limit: Int
107
+ ): [KnowledgeGraphNode!]!
108
+ knowledgeGraphByAuthor(
109
+ driveId: ID!
110
+ author: String!
111
+ ): [KnowledgeGraphNode!]!
112
+ knowledgeGraphByOrigin(
113
+ driveId: ID!
114
+ origin: String!
115
+ ): [KnowledgeGraphNode!]!
116
+ knowledgeGraphRecent(
117
+ driveId: ID!
118
+ limit: Int
119
+ since: String
120
+ ): [KnowledgeGraphNode!]!
121
+
122
+ knowledgeGraphSemanticSearch(
123
+ driveId: ID!
124
+ query: String!
125
+ limit: Int
126
+ ): [SemanticResult!]!
127
+ knowledgeGraphSimilar(
128
+ driveId: ID!
129
+ documentId: String!
130
+ limit: Int
131
+ ): [SemanticResult!]!
132
+
75
133
  """
76
134
  Debug: raw processor DB tables
77
135
  """
@@ -84,6 +142,11 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
84
142
  sharedTarget: KnowledgeGraphNode!
85
143
  }
86
144
 
145
+ type SemanticResult {
146
+ node: KnowledgeGraphNode!
147
+ similarity: Float!
148
+ }
149
+
87
150
  type GraphDebugInfo {
88
151
  rawNodeCount: Int!
89
152
  rawEdgeCount: Int!
@@ -107,6 +170,19 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
107
170
  }
108
171
  `;
109
172
  resolvers = {
173
+ KnowledgeGraphNode: {
174
+ topics: async (parent) => {
175
+ // If topics were already resolved inline, return them
176
+ if (parent.topics)
177
+ return parent.topics;
178
+ // Otherwise resolve via field resolver using _driveId hint
179
+ if (parent._driveId) {
180
+ const query = this.getQuery(parent._driveId);
181
+ return query.topicsForNode(parent.documentId);
182
+ }
183
+ return [];
184
+ },
185
+ },
110
186
  Mutation: {
111
187
  knowledgeGraphReindex: (_, args) => this.reindexDrive(args.driveId),
112
188
  },
@@ -114,7 +190,8 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
114
190
  knowledgeGraphNodes: async (_, args) => {
115
191
  await this.ensureGraphDoc(args.driveId);
116
192
  const query = this.getQuery(args.driveId);
117
- return query.allNodes();
193
+ const nodes = await query.allNodes();
194
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
118
195
  },
119
196
  knowledgeGraphEdges: async (_, args) => {
120
197
  await this.ensureGraphDoc(args.driveId);
@@ -128,19 +205,26 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
128
205
  },
129
206
  knowledgeGraphNodeByDocumentId: async (_, args) => {
130
207
  const query = this.getQuery(args.driveId);
131
- return query.nodeByDocumentId(args.documentId) ?? null;
208
+ const node = await query.nodeByDocumentId(args.documentId);
209
+ return node ? { ...node, _driveId: args.driveId } : null;
132
210
  },
133
211
  knowledgeGraphOrphans: async (_, args) => {
134
212
  const query = this.getQuery(args.driveId);
135
- return query.orphanNodes();
213
+ const nodes = await query.orphanNodes();
214
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
136
215
  },
137
216
  knowledgeGraphConnections: async (_, args) => {
138
217
  const query = this.getQuery(args.driveId);
139
- return query.connections(args.documentId, args.depth ?? 2);
218
+ const connections = await query.connections(args.documentId, args.depth ?? 2);
219
+ return connections.map((c) => ({
220
+ ...c,
221
+ node: { ...c.node, _driveId: args.driveId },
222
+ }));
140
223
  },
141
224
  knowledgeGraphNodesByStatus: async (_, args) => {
142
225
  const query = this.getQuery(args.driveId);
143
- return query.nodesByStatus(args.status);
226
+ const nodes = await query.nodesByStatus(args.status);
227
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
144
228
  },
145
229
  knowledgeGraphBacklinks: async (_, args) => {
146
230
  const query = this.getQuery(args.driveId);
@@ -152,25 +236,101 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
152
236
  },
153
237
  knowledgeGraphSearch: async (_, args) => {
154
238
  const query = this.getQuery(args.driveId);
155
- return query.searchNodes(args.query, args.limit ?? 50);
239
+ const nodes = await query.searchNodes(args.query, args.limit ?? 50);
240
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
156
241
  },
157
242
  knowledgeGraphTriangles: async (_, args) => {
158
243
  const query = this.getQuery(args.driveId);
159
244
  const triangles = await query.triangles(args.limit ?? 20);
160
245
  return triangles.map((t) => ({
161
- noteA: t.a,
162
- noteB: t.b,
163
- sharedTarget: t.sharedTarget,
246
+ noteA: { ...t.a, _driveId: args.driveId },
247
+ noteB: { ...t.b, _driveId: args.driveId },
248
+ sharedTarget: { ...t.sharedTarget, _driveId: args.driveId },
164
249
  }));
165
250
  },
166
251
  knowledgeGraphBridges: async (_, args) => {
167
252
  const query = this.getQuery(args.driveId);
168
- return query.bridges();
253
+ const nodes = await query.bridges();
254
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
169
255
  },
170
256
  knowledgeGraphForwardLinks: async (_, args) => {
171
257
  const query = this.getQuery(args.driveId);
172
258
  return query.forwardLinks(args.documentId);
173
259
  },
260
+ // --- Phase 1: new query resolvers ---
261
+ knowledgeGraphTopics: async (_, args) => {
262
+ const query = this.getQuery(args.driveId);
263
+ return query.topicStats();
264
+ },
265
+ knowledgeGraphByTopic: async (_, args) => {
266
+ const query = this.getQuery(args.driveId);
267
+ const nodes = await query.nodesByTopic(args.topic);
268
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
269
+ },
270
+ knowledgeGraphRelatedByTopic: async (_, args) => {
271
+ const query = this.getQuery(args.driveId);
272
+ const results = await query.relatedByTopic(args.documentId, args.limit ?? 10);
273
+ return results.map((r) => ({
274
+ ...r,
275
+ node: { ...r.node, _driveId: args.driveId },
276
+ }));
277
+ },
278
+ knowledgeGraphFullSearch: async (_, args) => {
279
+ const query = this.getQuery(args.driveId);
280
+ const nodes = await query.fullSearch(args.query, args.limit ?? 50);
281
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
282
+ },
283
+ knowledgeGraphByAuthor: async (_, args) => {
284
+ const query = this.getQuery(args.driveId);
285
+ const nodes = await query.nodesByAuthor(args.author);
286
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
287
+ },
288
+ knowledgeGraphByOrigin: async (_, args) => {
289
+ const query = this.getQuery(args.driveId);
290
+ const nodes = await query.nodesByOrigin(args.origin);
291
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
292
+ },
293
+ knowledgeGraphRecent: async (_, args) => {
294
+ const query = this.getQuery(args.driveId);
295
+ const nodes = await query.recentNodes(args.limit ?? 20, args.since ?? undefined);
296
+ return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
297
+ },
298
+ knowledgeGraphSemanticSearch: async (_, args) => {
299
+ const queryEmbedding = await generateEmbedding(args.query);
300
+ const results = await searchSimilar(queryEmbedding, args.limit ?? 10);
301
+ const graphQuery = this.getQuery(args.driveId);
302
+ const semanticResults = [];
303
+ for (const result of results) {
304
+ const node = await graphQuery.nodeByDocumentId(result.documentId);
305
+ if (node) {
306
+ semanticResults.push({
307
+ node: { ...node, _driveId: args.driveId },
308
+ similarity: result.similarity,
309
+ });
310
+ }
311
+ }
312
+ return semanticResults;
313
+ },
314
+ knowledgeGraphSimilar: async (_, args) => {
315
+ const embedding = await getEmbedding(args.documentId);
316
+ if (!embedding)
317
+ return [];
318
+ const results = await searchSimilar(embedding, (args.limit ?? 10) + 1);
319
+ const graphQuery = this.getQuery(args.driveId);
320
+ const semanticResults = [];
321
+ for (const result of results) {
322
+ if (result.documentId === args.documentId)
323
+ continue;
324
+ const node = await graphQuery.nodeByDocumentId(result.documentId);
325
+ if (node) {
326
+ semanticResults.push({
327
+ node: { ...node, _driveId: args.driveId },
328
+ similarity: result.similarity,
329
+ });
330
+ }
331
+ }
332
+ return semanticResults.slice(0, args.limit ?? 10);
333
+ },
174
334
  knowledgeGraphDebug: async (_, args) => {
175
335
  const namespace = GraphIndexerProcessor.getNamespace(args.driveId);
176
336
  try {
@@ -193,7 +353,13 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
193
353
  description: r.description,
194
354
  noteType: r.note_type,
195
355
  status: r.status,
356
+ content: r.content,
357
+ author: r.author,
358
+ sourceOrigin: r.source_origin,
359
+ createdAt: r.created_at,
360
+ topics: [],
196
361
  updatedAt: r.updated_at,
362
+ _driveId: args.driveId,
197
363
  })),
198
364
  rawEdges: rawEdges.map((r) => ({
199
365
  id: r.id,
@@ -224,17 +390,28 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
224
390
  }
225
391
  /**
226
392
  * Returns a Kysely<DB> instance scoped to the processor's namespace
227
- * for the given drive. Centralizes the Legacy IRelationalDb cast.
393
+ * for the given drive. Centralizes the Legacy -> IRelationalDb cast.
394
+ */
395
+ /**
396
+ * Read-only namespaced query builder — use for all SELECT resolvers.
228
397
  */
229
398
  getDb(driveId) {
230
399
  return GraphIndexerProcessor.query(driveId, this.relationalDb);
231
400
  }
401
+ /**
402
+ * Writable namespaced Kysely instance — use for reindex (INSERT/DELETE).
403
+ * createNamespace returns a full Kysely, not the read-only query builder.
404
+ */
405
+ async getWritableDb(driveId) {
406
+ const namespace = GraphIndexerProcessor.getNamespace(driveId);
407
+ return (await this.relationalDb.createNamespace(namespace));
408
+ }
232
409
  getQuery(driveId) {
233
410
  return createGraphQuery(this.getDb(driveId));
234
411
  }
235
412
  /**
236
413
  * Ensures a bai/knowledge-graph document exists in the given drive.
237
- * Called lazily on first query supports API/plugin access without
414
+ * Called lazily on first query -- supports API/plugin access without
238
415
  * requiring the Connect UI to have initialized the drive first.
239
416
  */
240
417
  ensuredDrives = new Set();
@@ -246,13 +423,15 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
246
423
  const drive = await this.reactorClient.get(driveId);
247
424
  const nodes = drive.state.global.nodes;
248
425
  const noteNodes = nodes.filter((n) => n.kind === "file" && n.documentType === "bai/knowledge-note");
249
- const db = this.getDb(driveId);
426
+ const db = await this.getWritableDb(driveId);
250
427
  const now = new Date().toISOString();
251
428
  for (const node of noteNodes) {
252
429
  try {
253
430
  const doc = await this.reactorClient.get(node.id);
254
431
  const state = doc.state;
255
432
  const global = state.global;
433
+ // Extract provenance
434
+ const provenance = global.provenance;
256
435
  await db
257
436
  .insertInto("graph_nodes")
258
437
  .values({
@@ -262,6 +441,10 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
262
441
  description: global.description ?? null,
263
442
  note_type: global.noteType ?? null,
264
443
  status: global.status ?? "DRAFT",
444
+ content: global.content ?? null,
445
+ author: provenance?.author ?? null,
446
+ source_origin: provenance?.sourceOrigin ?? null,
447
+ created_at: provenance?.createdAt ?? null,
265
448
  updated_at: now,
266
449
  })
267
450
  .onConflict((oc) => oc.column("document_id").doUpdateSet({
@@ -269,10 +452,36 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
269
452
  description: global.description ?? null,
270
453
  note_type: global.noteType ?? null,
271
454
  status: global.status ?? "DRAFT",
455
+ content: global.content ?? null,
456
+ author: provenance?.author ?? null,
457
+ source_origin: provenance?.sourceOrigin ?? null,
458
+ created_at: provenance?.createdAt ?? null,
272
459
  updated_at: now,
273
460
  }))
274
461
  .execute();
275
462
  indexedNodes++;
463
+ // Reconcile topics
464
+ await db
465
+ .deleteFrom("graph_topics")
466
+ .where("document_id", "=", node.id)
467
+ .execute();
468
+ const topics = global.topics ?? [];
469
+ if (topics.length > 0) {
470
+ await db
471
+ .insertInto("graph_topics")
472
+ .values(topics.map((topic, idx) => {
473
+ const name = typeof topic === "string"
474
+ ? topic
475
+ : (topic.name ?? "");
476
+ return {
477
+ id: `${node.id}-topic-${idx}`,
478
+ document_id: node.id,
479
+ name,
480
+ updated_at: now,
481
+ };
482
+ }))
483
+ .execute();
484
+ }
276
485
  // Reconcile edges
277
486
  await db
278
487
  .deleteFrom("graph_edges")
@@ -294,6 +503,19 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
294
503
  .execute();
295
504
  indexedEdges += links.length;
296
505
  }
506
+ // Generate embedding for semantic search
507
+ const text = [global.title, global.description, global.content]
508
+ .filter(Boolean)
509
+ .join(" ");
510
+ if (text.length > 0) {
511
+ try {
512
+ const emb = await generateEmbedding(text);
513
+ await upsertEmbedding(node.id, emb);
514
+ }
515
+ catch (embErr) {
516
+ console.warn(`[KnowledgeGraphSubgraph] Embedding failed for ${node.id}:`, embErr);
517
+ }
518
+ }
297
519
  }
298
520
  catch (err) {
299
521
  const msg = err instanceof Error ? err.message : String(err);
@@ -327,12 +549,12 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
327
549
  console.log(`[KnowledgeGraphSubgraph] Auto-created KnowledgeGraph in drive ${driveId}` +
328
550
  (parentFolder
329
551
  ? ` (folder: /self/)`
330
- : ` (drive root /self/ folder not found)`));
552
+ : ` (drive root -- /self/ folder not found)`));
331
553
  }
332
554
  }
333
555
  catch (err) {
334
556
  console.error(`[KnowledgeGraphSubgraph] Failed to ensure graph doc:`, err);
335
- // Don't block queries if this fails processor data still works
557
+ // Don't block queries if this fails -- processor data still works
336
558
  }
337
559
  }
338
560
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/knowledge-note",
3
3
  "description": "Knowledge Note document model package for Powerhouse",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [
@@ -107,6 +107,7 @@
107
107
  },
108
108
  "dependencies": {
109
109
  "@electric-sql/pglite": "0.3.15",
110
+ "@huggingface/transformers": "^4.0.1",
110
111
  "@powerhousedao/builder-tools": "6.0.0-dev.105",
111
112
  "@powerhousedao/common": "6.0.0-dev.105",
112
113
  "@powerhousedao/design-system": "6.0.0-dev.105",