@powerhousedao/knowledge-note 1.0.2 → 1.0.4

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 (62) 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/GraphView.d.ts.map +1 -1
  5. package/dist/editors/knowledge-vault/components/GraphView.js +210 -32
  6. package/dist/editors/knowledge-vault/components/NoteList.d.ts.map +1 -1
  7. package/dist/editors/knowledge-vault/components/NoteList.js +13 -16
  8. package/dist/editors/knowledge-vault/components/SearchView.d.ts +2 -0
  9. package/dist/editors/knowledge-vault/components/SearchView.d.ts.map +1 -0
  10. package/dist/editors/knowledge-vault/components/SearchView.js +96 -0
  11. package/dist/editors/knowledge-vault/hooks/use-drive-init.d.ts.map +1 -1
  12. package/dist/editors/knowledge-vault/hooks/use-drive-init.js +0 -2
  13. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts +25 -0
  14. package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts.map +1 -0
  15. package/dist/editors/knowledge-vault/hooks/use-graph-search.js +198 -0
  16. package/dist/package.json +3 -2
  17. package/dist/processors/factory.d.ts.map +1 -1
  18. package/dist/processors/factory.js +0 -3
  19. package/dist/processors/graph-indexer/embedder.d.ts +5 -0
  20. package/dist/processors/graph-indexer/embedder.d.ts.map +1 -0
  21. package/dist/processors/graph-indexer/embedder.js +27 -0
  22. package/dist/processors/graph-indexer/embedding-store.d.ts +11 -0
  23. package/dist/processors/graph-indexer/embedding-store.d.ts.map +1 -0
  24. package/dist/processors/graph-indexer/embedding-store.js +70 -0
  25. package/dist/processors/graph-indexer/index.d.ts.map +1 -1
  26. package/dist/processors/graph-indexer/index.js +53 -8
  27. package/dist/processors/graph-indexer/migrations.d.ts.map +1 -1
  28. package/dist/processors/graph-indexer/migrations.js +35 -0
  29. package/dist/processors/graph-indexer/query.d.ts +21 -0
  30. package/dist/processors/graph-indexer/query.d.ts.map +1 -1
  31. package/dist/processors/graph-indexer/query.js +154 -13
  32. package/dist/processors/graph-indexer/schema.d.ts +11 -0
  33. package/dist/processors/graph-indexer/schema.d.ts.map +1 -1
  34. package/dist/style.css +130 -0
  35. package/dist/subgraphs/index.d.ts +0 -1
  36. package/dist/subgraphs/index.d.ts.map +1 -1
  37. package/dist/subgraphs/index.js +0 -1
  38. package/dist/subgraphs/knowledge-graph/subgraph.d.ts +348 -24
  39. package/dist/subgraphs/knowledge-graph/subgraph.d.ts.map +1 -1
  40. package/dist/subgraphs/knowledge-graph/subgraph.js +334 -20
  41. package/package.json +4 -3
  42. package/dist/processors/methodology-indexer/factory.d.ts +0 -4
  43. package/dist/processors/methodology-indexer/factory.d.ts.map +0 -1
  44. package/dist/processors/methodology-indexer/factory.js +0 -23
  45. package/dist/processors/methodology-indexer/index.d.ts +0 -11
  46. package/dist/processors/methodology-indexer/index.d.ts.map +0 -1
  47. package/dist/processors/methodology-indexer/index.js +0 -116
  48. package/dist/processors/methodology-indexer/migrations.d.ts +0 -4
  49. package/dist/processors/methodology-indexer/migrations.d.ts.map +0 -1
  50. package/dist/processors/methodology-indexer/migrations.js +0 -39
  51. package/dist/processors/methodology-indexer/query.d.ts +0 -35
  52. package/dist/processors/methodology-indexer/query.d.ts.map +0 -1
  53. package/dist/processors/methodology-indexer/query.js +0 -114
  54. package/dist/processors/methodology-indexer/schema.d.ts +0 -22
  55. package/dist/processors/methodology-indexer/schema.d.ts.map +0 -1
  56. package/dist/processors/methodology-indexer/schema.js +0 -1
  57. package/dist/subgraphs/methodology/index.d.ts +0 -2
  58. package/dist/subgraphs/methodology/index.d.ts.map +0 -1
  59. package/dist/subgraphs/methodology/index.js +0 -1
  60. package/dist/subgraphs/methodology/subgraph.d.ts +0 -47
  61. package/dist/subgraphs/methodology/subgraph.d.ts.map +0 -1
  62. package/dist/subgraphs/methodology/subgraph.js +0 -100
@@ -0,0 +1,198 @@
1
+ import { useState, useEffect, useRef, useCallback, useMemo } from "react";
2
+ import { useSelectedDriveId } from "@powerhousedao/reactor-browser";
3
+ /* ------------------------------------------------------------------ */
4
+ /* GraphQL helpers */
5
+ /* ------------------------------------------------------------------ */
6
+ const DEBOUNCE_MS = 300;
7
+ const SUBGRAPH_PATH = "/graphql/knowledgeGraph";
8
+ /**
9
+ * Derive the subgraph endpoint.
10
+ *
11
+ * Priority:
12
+ * 1. VITE_SUBGRAPH_URL env var (explicit override for deployed environments)
13
+ * 2. Vite dev (port 3000/3001) → localhost:4001
14
+ * 3. Same-origin relative path (Connect production — app and reactor share origin)
15
+ *
16
+ * For deployed environments where Connect and Switchboard are on different
17
+ * domains (e.g. connect.example.com vs switchboard-dev.powerhouse.xyz),
18
+ * set VITE_SUBGRAPH_URL=https://switchboard-dev.powerhouse.xyz/graphql/knowledgeGraph
19
+ * in your .env file.
20
+ */
21
+ function resolveEndpoint() {
22
+ // Explicit override via env var
23
+ const envUrl = typeof import.meta !== "undefined" &&
24
+ import.meta.env?.VITE_SUBGRAPH_URL;
25
+ if (envUrl)
26
+ return envUrl;
27
+ // Vite dev server proxying to local reactor
28
+ const port = globalThis.window?.location?.port;
29
+ if (port === "3000" || port === "3001") {
30
+ return `http://localhost:4001${SUBGRAPH_PATH}`;
31
+ }
32
+ // Same-origin (Connect production)
33
+ return SUBGRAPH_PATH;
34
+ }
35
+ async function graphqlFetch(endpoint, query, variables) {
36
+ try {
37
+ const res = await fetch(endpoint, {
38
+ method: "POST",
39
+ headers: { "Content-Type": "application/json" },
40
+ body: JSON.stringify({ query, variables }),
41
+ });
42
+ if (!res.ok)
43
+ return null;
44
+ const json = (await res.json());
45
+ if (json.errors) {
46
+ console.warn("[useGraphSearch] GraphQL errors:", json.errors);
47
+ }
48
+ return json.data ?? null;
49
+ }
50
+ catch (err) {
51
+ console.warn("[useGraphSearch] Fetch failed:", err);
52
+ return null;
53
+ }
54
+ }
55
+ /* ------------------------------------------------------------------ */
56
+ /* Queries */
57
+ /* ------------------------------------------------------------------ */
58
+ const SEMANTIC_SEARCH_QUERY = `
59
+ query SemanticSearch($driveId: ID!, $query: String!, $limit: Int) {
60
+ knowledgeGraphSemanticSearch(driveId: $driveId, query: $query, limit: $limit) {
61
+ node { documentId title description noteType status topics }
62
+ similarity
63
+ }
64
+ }
65
+ `;
66
+ const KEYWORD_SEARCH_QUERY = `
67
+ query FullSearch($driveId: ID!, $query: String!, $limit: Int) {
68
+ knowledgeGraphFullSearch(driveId: $driveId, query: $query, limit: $limit) {
69
+ documentId title description noteType status topics
70
+ }
71
+ }
72
+ `;
73
+ const TOPICS_QUERY = `
74
+ query Topics($driveId: ID!) {
75
+ knowledgeGraphTopics(driveId: $driveId) { name noteCount }
76
+ }
77
+ `;
78
+ /* ------------------------------------------------------------------ */
79
+ /* Hook */
80
+ /* ------------------------------------------------------------------ */
81
+ const STORAGE_KEY = "bai-search-state";
82
+ function loadSearchState() {
83
+ try {
84
+ const raw = sessionStorage.getItem(STORAGE_KEY);
85
+ if (raw) {
86
+ const parsed = JSON.parse(raw);
87
+ return {
88
+ query: parsed.query ?? "",
89
+ mode: parsed.mode === "keyword" ? "keyword" : "semantic",
90
+ };
91
+ }
92
+ }
93
+ catch {
94
+ // ignore
95
+ }
96
+ return { query: "", mode: "semantic" };
97
+ }
98
+ function saveSearchState(query, mode) {
99
+ try {
100
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ query, mode }));
101
+ }
102
+ catch {
103
+ // ignore
104
+ }
105
+ }
106
+ export function useGraphSearch() {
107
+ const driveId = useSelectedDriveId();
108
+ const saved = useRef(loadSearchState());
109
+ const [query, setQueryRaw] = useState(saved.current.query);
110
+ const [results, setResults] = useState([]);
111
+ const [topics, setTopics] = useState([]);
112
+ const [loading, setLoading] = useState(false);
113
+ const [error, setError] = useState(null);
114
+ const [searchMode, setSearchModeRaw] = useState(saved.current.mode);
115
+ const debounceRef = useRef(null);
116
+ const endpoint = useMemo(() => resolveEndpoint(), []);
117
+ // Persist query and mode to sessionStorage
118
+ const setQuery = useCallback((q) => {
119
+ setQueryRaw(q);
120
+ saveSearchState(q, searchMode);
121
+ }, [searchMode]);
122
+ const setSearchMode = useCallback((m) => {
123
+ setSearchModeRaw(m);
124
+ saveSearchState(query, m);
125
+ }, [query]);
126
+ // Fetch topics on mount for empty-state overview
127
+ useEffect(() => {
128
+ if (!driveId)
129
+ return;
130
+ graphqlFetch(endpoint, TOPICS_QUERY, { driveId }).then((data) => {
131
+ if (data?.knowledgeGraphTopics) {
132
+ setTopics(data.knowledgeGraphTopics);
133
+ }
134
+ });
135
+ }, [driveId, endpoint]);
136
+ // Debounced search
137
+ const executeSearch = useCallback(async (q, mode) => {
138
+ if (!driveId || !q.trim()) {
139
+ setResults([]);
140
+ setLoading(false);
141
+ return;
142
+ }
143
+ setLoading(true);
144
+ setError(null);
145
+ if (mode === "semantic") {
146
+ const data = await graphqlFetch(endpoint, SEMANTIC_SEARCH_QUERY, { driveId, query: q, limit: 20 });
147
+ if (data?.knowledgeGraphSemanticSearch) {
148
+ setResults(data.knowledgeGraphSemanticSearch.map((r) => ({
149
+ ...r.node,
150
+ similarity: r.similarity,
151
+ })));
152
+ }
153
+ else {
154
+ setResults([]);
155
+ setError("Semantic search unavailable. Try keyword mode.");
156
+ }
157
+ }
158
+ else {
159
+ const data = await graphqlFetch(endpoint, KEYWORD_SEARCH_QUERY, { driveId, query: q, limit: 20 });
160
+ if (data?.knowledgeGraphFullSearch) {
161
+ setResults(data.knowledgeGraphFullSearch);
162
+ }
163
+ else {
164
+ setResults([]);
165
+ setError("Search failed.");
166
+ }
167
+ }
168
+ setLoading(false);
169
+ }, [driveId, endpoint]);
170
+ // Trigger debounced search on query or mode change
171
+ useEffect(() => {
172
+ if (debounceRef.current)
173
+ clearTimeout(debounceRef.current);
174
+ if (!query.trim()) {
175
+ setResults([]);
176
+ setLoading(false);
177
+ return;
178
+ }
179
+ setLoading(true);
180
+ debounceRef.current = setTimeout(() => {
181
+ void executeSearch(query, searchMode);
182
+ }, DEBOUNCE_MS);
183
+ return () => {
184
+ if (debounceRef.current)
185
+ clearTimeout(debounceRef.current);
186
+ };
187
+ }, [query, searchMode, executeSearch]);
188
+ return {
189
+ query,
190
+ setQuery,
191
+ results,
192
+ topics,
193
+ loading,
194
+ error,
195
+ searchMode,
196
+ setSearchMode,
197
+ };
198
+ }
package/dist/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.2",
4
+ "version": "1.0.4",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [
@@ -107,13 +107,14 @@
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",
113
114
  "@powerhousedao/document-engineering": "1.40.1",
114
115
  "@powerhousedao/vetra": "6.0.0-dev.105",
115
116
  "cytoscape": "^3.33.1",
116
- "cytoscape-cose-bilkent": "^4.1.0",
117
+ "cytoscape-fcose": "^2.2.0",
117
118
  "document-model": "6.0.0-dev.105",
118
119
  "graphql": "16.12.0",
119
120
  "graphql-tag": "^2.12.6",
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../processors/factory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EAErB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIvD,eAAO,MAAM,gBAAgB,GAAI,QAAQ,oBAAoB,MAgB7C,aAAa,gBAAgB,KAAG,OAAO,CAAC,eAAe,EAAE,CAUxE,CAAC"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../processors/factory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EAErB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,eAAO,MAAM,gBAAgB,GAAI,QAAQ,oBAAoB,MAa7C,aAAa,gBAAgB,KAAG,OAAO,CAAC,eAAe,EAAE,CAUxE,CAAC"}
@@ -2,14 +2,11 @@
2
2
  * This file aggregates all processor factories
3
3
  */
4
4
  import { graphIndexerProcessorFactory } from "./graph-indexer/factory.js";
5
- import { methodologyIndexerProcessorFactory } from "./methodology-indexer/factory.js";
6
5
  export const processorFactory = (module) => {
7
6
  console.log(`[processorFactory] Initializing with processorApp: ${module.processorApp}`);
8
7
  const factories = [];
9
8
  // Register graph indexer (watches bai/knowledge-note)
10
9
  factories.push(graphIndexerProcessorFactory(module));
11
- // Register methodology indexer (watches bai/research-claim)
12
- factories.push(methodologyIndexerProcessorFactory(module));
13
10
  console.log(`[processorFactory] Loaded ${factories.length} factories`);
14
11
  // Return the inner function that will be called for each drive
15
12
  return async (driveHeader) => {
@@ -0,0 +1,5 @@
1
+ import { type FeatureExtractionPipeline } from "@huggingface/transformers";
2
+ export declare function getExtractor(): Promise<FeatureExtractionPipeline>;
3
+ export declare function generateEmbedding(text: string): Promise<number[]>;
4
+ export declare function isEmbedderReady(): boolean;
5
+ //# sourceMappingURL=embedder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedder.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/embedder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,2BAA2B,CAAC;AAKnC,wBAAsB,YAAY,IAAI,OAAO,CAAC,yBAAyB,CAAC,CAgBvE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAIvE;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC"}
@@ -0,0 +1,27 @@
1
+ import { pipeline, } from "@huggingface/transformers";
2
+ let extractor = null;
3
+ let loading = null;
4
+ export async function getExtractor() {
5
+ if (extractor)
6
+ return extractor;
7
+ // Prevent concurrent loads — share the same promise
8
+ if (!loading) {
9
+ loading = pipeline("feature-extraction", "Supabase/gte-small", {
10
+ dtype: "q8",
11
+ }).then((ext) => {
12
+ extractor = ext;
13
+ loading = null;
14
+ console.log("[Embedder] Model loaded: Supabase/gte-small (q8)");
15
+ return extractor;
16
+ });
17
+ }
18
+ return loading;
19
+ }
20
+ export async function generateEmbedding(text) {
21
+ const ext = await getExtractor();
22
+ const output = await ext(text, { pooling: "mean", normalize: true });
23
+ return Array.from(output.data);
24
+ }
25
+ export function isEmbedderReady() {
26
+ return extractor !== null;
27
+ }
@@ -0,0 +1,11 @@
1
+ import { PGlite } from "@electric-sql/pglite";
2
+ export declare function getEmbeddingDb(): Promise<PGlite>;
3
+ export declare function upsertEmbedding(documentId: string, embedding: number[]): Promise<void>;
4
+ export declare function searchSimilar(queryEmbedding: number[], limit?: number): Promise<Array<{
5
+ documentId: string;
6
+ similarity: number;
7
+ }>>;
8
+ export declare function getEmbedding(documentId: string): Promise<number[] | null>;
9
+ export declare function deleteEmbedding(documentId: string): Promise<void>;
10
+ export declare function closeEmbeddingDb(): Promise<void>;
11
+ //# sourceMappingURL=embedding-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding-store.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/embedding-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAK9C,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CA2BtD;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,aAAa,CACjC,cAAc,EAAE,MAAM,EAAE,EACxB,KAAK,SAAK,GACT,OAAO,CAAC,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAmB5D;AAED,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAa1B;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvE;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKtD"}
@@ -0,0 +1,70 @@
1
+ import { PGlite } from "@electric-sql/pglite";
2
+ import { vector } from "@electric-sql/pglite/vector";
3
+ let db = null;
4
+ export async function getEmbeddingDb() {
5
+ if (db)
6
+ return db;
7
+ const dataDir = typeof window !== "undefined"
8
+ ? "idb://knowledge-embeddings"
9
+ : "./.ph/knowledge-embeddings";
10
+ db = new PGlite({
11
+ dataDir,
12
+ extensions: { vector },
13
+ });
14
+ await db.exec("CREATE EXTENSION IF NOT EXISTS vector;");
15
+ await db.exec(`
16
+ CREATE TABLE IF NOT EXISTS note_embeddings (
17
+ document_id VARCHAR(255) PRIMARY KEY,
18
+ embedding vector(384) NOT NULL,
19
+ updated_at VARCHAR(50) NOT NULL
20
+ );
21
+ `);
22
+ await db.exec(`
23
+ CREATE INDEX IF NOT EXISTS idx_note_embeddings_hnsw
24
+ ON note_embeddings USING hnsw (embedding vector_cosine_ops);
25
+ `);
26
+ return db;
27
+ }
28
+ export async function upsertEmbedding(documentId, embedding) {
29
+ const store = await getEmbeddingDb();
30
+ const now = new Date().toISOString();
31
+ const embeddingStr = `[${embedding.join(",")}]`;
32
+ await store.query(`INSERT INTO note_embeddings (document_id, embedding, updated_at)
33
+ VALUES ($1, $2::vector, $3)
34
+ ON CONFLICT (document_id) DO UPDATE SET
35
+ embedding = EXCLUDED.embedding,
36
+ updated_at = EXCLUDED.updated_at`, [documentId, embeddingStr, now]);
37
+ }
38
+ export async function searchSimilar(queryEmbedding, limit = 10) {
39
+ const store = await getEmbeddingDb();
40
+ const embeddingStr = `[${queryEmbedding.join(",")}]`;
41
+ const result = await store.query(`SELECT document_id, embedding <=> $1::vector AS distance
42
+ FROM note_embeddings
43
+ ORDER BY distance ASC
44
+ LIMIT $2`, [embeddingStr, limit]);
45
+ return (result.rows ?? []).map((row) => ({
46
+ documentId: row.document_id,
47
+ similarity: 1 - row.distance,
48
+ }));
49
+ }
50
+ export async function getEmbedding(documentId) {
51
+ const store = await getEmbeddingDb();
52
+ const result = await store.query(`SELECT embedding::text FROM note_embeddings WHERE document_id = $1`, [documentId]);
53
+ if (!result.rows || result.rows.length === 0)
54
+ return null;
55
+ // Parse "[0.1,0.2,...]" string back to number[]
56
+ const raw = result.rows[0].embedding;
57
+ return JSON.parse(raw);
58
+ }
59
+ export async function deleteEmbedding(documentId) {
60
+ const store = await getEmbeddingDb();
61
+ await store.query(`DELETE FROM note_embeddings WHERE document_id = $1`, [
62
+ documentId,
63
+ ]);
64
+ }
65
+ export async function closeEmbeddingDb() {
66
+ if (db) {
67
+ await db.close();
68
+ db = null;
69
+ }
70
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAqGV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAUrB,UAAU;CAoBzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAItC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAmKV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,UAAU;CA8BzB"}
@@ -1,5 +1,7 @@
1
1
  import { RelationalDbProcessor } from "@powerhousedao/shared/processors";
2
2
  import { up } from "./migrations.js";
3
+ import { generateEmbedding } from "./embedder.js";
4
+ import { upsertEmbedding, deleteEmbedding } from "./embedding-store.js";
3
5
  export class GraphIndexerProcessor extends RelationalDbProcessor {
4
6
  static getNamespace(driveId) {
5
7
  return super.getNamespace(driveId);
@@ -41,6 +43,8 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
41
43
  // resultingState may be wrapped in { global: ... } or be the global state directly
42
44
  const global = (parsed.global ?? parsed);
43
45
  const now = new Date().toISOString();
46
+ // Extract provenance
47
+ const provenance = global.provenance;
44
48
  // Upsert node
45
49
  await this.relationalDb
46
50
  .insertInto("graph_nodes")
@@ -51,6 +55,10 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
51
55
  description: global.description ?? null,
52
56
  note_type: global.noteType ?? null,
53
57
  status: global.status ?? "DRAFT",
58
+ content: global.content ?? null,
59
+ author: provenance?.author ?? null,
60
+ source_origin: provenance?.sourceOrigin ?? null,
61
+ created_at: provenance?.createdAt ?? null,
54
62
  updated_at: now,
55
63
  })
56
64
  .onConflict((oc) => oc.column("document_id").doUpdateSet({
@@ -58,9 +66,35 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
58
66
  description: global.description ?? null,
59
67
  note_type: global.noteType ?? null,
60
68
  status: global.status ?? "DRAFT",
69
+ content: global.content ?? null,
70
+ author: provenance?.author ?? null,
71
+ source_origin: provenance?.sourceOrigin ?? null,
72
+ created_at: provenance?.createdAt ?? null,
61
73
  updated_at: now,
62
74
  }))
63
75
  .execute();
76
+ // Reconcile topics: delete old, insert new
77
+ await this.relationalDb
78
+ .deleteFrom("graph_topics")
79
+ .where("document_id", "=", documentId)
80
+ .execute();
81
+ const topics = global.topics ?? [];
82
+ if (topics.length > 0) {
83
+ await this.relationalDb
84
+ .insertInto("graph_topics")
85
+ .values(topics.map((topic, idx) => {
86
+ const name = typeof topic === "string"
87
+ ? topic
88
+ : (topic.name ?? "");
89
+ return {
90
+ id: `${documentId}-topic-${idx}`,
91
+ document_id: documentId,
92
+ name,
93
+ updated_at: now,
94
+ };
95
+ }))
96
+ .execute();
97
+ }
64
98
  // Reconcile edges: delete old, insert new
65
99
  await this.relationalDb
66
100
  .deleteFrom("graph_edges")
@@ -82,6 +116,15 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
82
116
  .execute();
83
117
  }
84
118
  console.log(`[GraphIndexer] Reconciled ${documentId}: ${links.length} edges`);
119
+ // Fire-and-forget embedding generation (don't block operation processing)
120
+ const text = [global.title, global.description, global.content]
121
+ .filter(Boolean)
122
+ .join(" ");
123
+ if (text.length > 0) {
124
+ generateEmbedding(text)
125
+ .then((emb) => upsertEmbedding(documentId, emb))
126
+ .catch((err) => console.warn(`[GraphIndexer] Embedding failed for ${documentId}:`, err));
127
+ }
85
128
  }
86
129
  catch (err) {
87
130
  console.error(`[GraphIndexer] Error reconciling document ${documentId}:`, err);
@@ -89,17 +132,18 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
89
132
  }
90
133
  }
91
134
  async onDisconnect() {
92
- try {
93
- await this.relationalDb.deleteFrom("graph_edges").execute();
94
- await this.relationalDb.deleteFrom("graph_nodes").execute();
95
- console.log(`[GraphIndexer] Cleaned up namespace: ${this.namespace}`);
96
- }
97
- catch (err) {
98
- console.error(`[GraphIndexer] Error cleaning up:`, err);
99
- }
135
+ // Intentionally no-op: preserve indexed data across restarts.
136
+ // The reactor does not replay historical operations on reconnect,
137
+ // so wiping tables here would leave the index permanently empty
138
+ // until new operations arrive. Use knowledgeGraphReindex mutation
139
+ // to rebuild if needed.
100
140
  }
101
141
  async deleteNode(documentId) {
102
142
  try {
143
+ await this.relationalDb
144
+ .deleteFrom("graph_topics")
145
+ .where("document_id", "=", documentId)
146
+ .execute();
103
147
  await this.relationalDb
104
148
  .deleteFrom("graph_edges")
105
149
  .where((eb) => eb.or([
@@ -111,6 +155,7 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
111
155
  .deleteFrom("graph_nodes")
112
156
  .where("document_id", "=", documentId)
113
157
  .execute();
158
+ deleteEmbedding(documentId).catch((err) => console.warn(`[GraphIndexer] Embedding delete failed for ${documentId}:`, err));
114
159
  console.log(`[GraphIndexer] Deleted node ${documentId}`);
115
160
  }
116
161
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,wBAAsB,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA6C9D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhE"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,wBAAsB,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF9D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE"}
@@ -39,8 +39,43 @@ export async function up(db) {
39
39
  .column("status")
40
40
  .ifNotExists()
41
41
  .execute();
42
+ // --- Phase 1: topics, content, provenance ---
43
+ // Add new columns to graph_nodes (wrap each in try/catch for idempotency)
44
+ for (const col of ["content", "author", "source_origin", "created_at"]) {
45
+ try {
46
+ await db.schema
47
+ .alterTable("graph_nodes")
48
+ .addColumn(col, col === "content" ? "text" : "varchar(1024)")
49
+ .execute();
50
+ }
51
+ catch {
52
+ // column likely already exists — ignore
53
+ }
54
+ }
55
+ // Create graph_topics table
56
+ await db.schema
57
+ .createTable("graph_topics")
58
+ .addColumn("id", "varchar(255)", (col) => col.primaryKey())
59
+ .addColumn("document_id", "varchar(255)", (col) => col.notNull())
60
+ .addColumn("name", "varchar(512)", (col) => col.notNull())
61
+ .addColumn("updated_at", "varchar(50)", (col) => col.notNull())
62
+ .ifNotExists()
63
+ .execute();
64
+ await db.schema
65
+ .createIndex("idx_graph_topics_document_id")
66
+ .on("graph_topics")
67
+ .column("document_id")
68
+ .ifNotExists()
69
+ .execute();
70
+ await db.schema
71
+ .createIndex("idx_graph_topics_name")
72
+ .on("graph_topics")
73
+ .column("name")
74
+ .ifNotExists()
75
+ .execute();
42
76
  }
43
77
  export async function down(db) {
78
+ await db.schema.dropTable("graph_topics").ifExists().execute();
44
79
  await db.schema.dropTable("graph_edges").ifExists().execute();
45
80
  await db.schema.dropTable("graph_nodes").ifExists().execute();
46
81
  }
@@ -7,6 +7,10 @@ export interface GraphNodeResult {
7
7
  description: string | null;
8
8
  noteType: string | null;
9
9
  status: string | null;
10
+ content: string | null;
11
+ author: string | null;
12
+ sourceOrigin: string | null;
13
+ createdAt: string | null;
10
14
  updatedAt: string;
11
15
  }
12
16
  export interface GraphEdgeResult {
@@ -27,6 +31,15 @@ export interface ConnectionResult {
27
31
  depth: number;
28
32
  viaLinkType: string | null;
29
33
  }
34
+ export interface TopicStatsResult {
35
+ name: string;
36
+ noteCount: number;
37
+ }
38
+ export interface RelatedByTopicResult {
39
+ node: GraphNodeResult;
40
+ sharedTopics: string[];
41
+ sharedTopicCount: number;
42
+ }
30
43
  export declare function createGraphQuery(db: Kysely<DB>): {
31
44
  allNodes(): Promise<GraphNodeResult[]>;
32
45
  allEdges(): Promise<GraphEdgeResult[]>;
@@ -45,5 +58,13 @@ export declare function createGraphQuery(db: Kysely<DB>): {
45
58
  sharedTarget: GraphNodeResult;
46
59
  }>>;
47
60
  bridges(): Promise<GraphNodeResult[]>;
61
+ topicStats(): Promise<TopicStatsResult[]>;
62
+ topicsForNode(documentId: string): Promise<string[]>;
63
+ nodesByTopic(topic: string): Promise<GraphNodeResult[]>;
64
+ relatedByTopic(documentId: string, limit?: number): Promise<RelatedByTopicResult[]>;
65
+ fullSearch(query: string, limit?: number): Promise<GraphNodeResult[]>;
66
+ nodesByAuthor(author: string): Promise<GraphNodeResult[]>;
67
+ nodesByOrigin(origin: string): Promise<GraphNodeResult[]>;
68
+ recentNodes(limit?: number, since?: string): Promise<GraphNodeResult[]>;
48
69
  };
49
70
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,EAAE,EAAwB,MAAM,aAAa,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAyBD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;gBAEzB,OAAO,CAAC,eAAe,EAAE,CAAC;gBAK1B,OAAO,CAAC,eAAe,EAAE,CAAC;iCAM9B,MAAM,GACjB,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;0BASX,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;mBAS1C,OAAO,CAAC,eAAe,EAAE,CAAC;aAchC,OAAO,CAAC,gBAAgB,CAAC;4BAiB1B,MAAM,sBAEjB,OAAO,CAAC,gBAAgB,EAAE,CAAC;0BA4CF,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;eAS9C,OAAO,CAAC,MAAM,CAAC;uBAOP,MAAM,mBAAe,OAAO,CAAC,eAAe,EAAE,CAAC;6BAgBzC,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;+BASrC,OAAO,CAClC,KAAK,CAAC;QACJ,CAAC,EAAE,eAAe,CAAC;QACnB,CAAC,EAAE,eAAe,CAAC;QACnB,YAAY,EAAE,eAAe,CAAC;KAC/B,CAAC,CACH;eAuDgB,OAAO,CAAC,eAAe,EAAE,CAAC;EA6D9C"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/query.ts"],"names":[],"mappings":"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"}