@powerhousedao/knowledge-note 1.0.5 → 1.0.6
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/dist/editors/knowledge-vault/components/ActivityView.d.ts +2 -0
- package/dist/editors/knowledge-vault/components/ActivityView.d.ts.map +1 -0
- package/dist/editors/knowledge-vault/components/ActivityView.js +188 -0
- package/dist/editors/knowledge-vault/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/knowledge-vault/components/DriveExplorer.js +7 -1
- package/dist/editors/knowledge-vault/components/SearchView.d.ts.map +1 -1
- package/dist/editors/knowledge-vault/components/SearchView.js +18 -11
- package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts +2 -1
- package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts.map +1 -1
- package/dist/editors/knowledge-vault/hooks/use-graph-search.js +33 -2
- package/dist/package.json +1 -1
- package/dist/processors/graph-indexer/index.d.ts.map +1 -1
- package/dist/processors/graph-indexer/index.js +80 -0
- package/dist/processors/graph-indexer/migrations.d.ts.map +1 -1
- package/dist/processors/graph-indexer/migrations.js +46 -0
- package/dist/processors/graph-indexer/query.d.ts +29 -0
- package/dist/processors/graph-indexer/query.d.ts.map +1 -1
- package/dist/processors/graph-indexer/query.js +104 -0
- package/dist/processors/graph-indexer/schema.d.ts +13 -0
- package/dist/processors/graph-indexer/schema.d.ts.map +1 -1
- package/dist/style.css +7 -0
- package/dist/subgraphs/knowledge-graph/subgraph.d.ts +55 -0
- package/dist/subgraphs/knowledge-graph/subgraph.d.ts.map +1 -1
- package/dist/subgraphs/knowledge-graph/subgraph.js +72 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/ActivityView.tsx"],"names":[],"mappings":"AAuHA,wBAAgB,YAAY,4CAkJ3B"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useMemo, useCallback } from "react";
|
|
3
|
+
import { setSelectedNode, useSelectedDriveId, } from "@powerhousedao/reactor-browser";
|
|
4
|
+
const FILTER_TYPES = {
|
|
5
|
+
all: { label: "All", ops: [] },
|
|
6
|
+
content: {
|
|
7
|
+
label: "Content",
|
|
8
|
+
ops: ["SET_TITLE", "SET_DESCRIPTION", "SET_CONTENT", "SET_NOTE_TYPE"],
|
|
9
|
+
},
|
|
10
|
+
links: {
|
|
11
|
+
label: "Links",
|
|
12
|
+
ops: ["ADD_LINK", "REMOVE_LINK", "UPDATE_LINK_TYPE"],
|
|
13
|
+
},
|
|
14
|
+
lifecycle: {
|
|
15
|
+
label: "Lifecycle",
|
|
16
|
+
ops: [
|
|
17
|
+
"SET_STATUS",
|
|
18
|
+
"SUBMIT_FOR_REVIEW",
|
|
19
|
+
"APPROVE_NOTE",
|
|
20
|
+
"REJECT_NOTE",
|
|
21
|
+
"ARCHIVE_NOTE",
|
|
22
|
+
"RESTORE_NOTE",
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
topics: { label: "Topics", ops: ["ADD_TOPIC", "REMOVE_TOPIC"] },
|
|
26
|
+
};
|
|
27
|
+
const OP_ICONS = {
|
|
28
|
+
SET_TITLE: "T",
|
|
29
|
+
SET_DESCRIPTION: "D",
|
|
30
|
+
SET_CONTENT: "C",
|
|
31
|
+
SET_NOTE_TYPE: "N",
|
|
32
|
+
ADD_LINK: "+L",
|
|
33
|
+
REMOVE_LINK: "-L",
|
|
34
|
+
ADD_TOPIC: "+#",
|
|
35
|
+
REMOVE_TOPIC: "-#",
|
|
36
|
+
SET_STATUS: "S",
|
|
37
|
+
SUBMIT_FOR_REVIEW: "R",
|
|
38
|
+
APPROVE_NOTE: "A",
|
|
39
|
+
REJECT_NOTE: "X",
|
|
40
|
+
ARCHIVE_NOTE: "Z",
|
|
41
|
+
RESTORE_NOTE: "U",
|
|
42
|
+
SET_PROVENANCE: "P",
|
|
43
|
+
SET_METADATA_FIELD: "M",
|
|
44
|
+
};
|
|
45
|
+
/* ------------------------------------------------------------------ */
|
|
46
|
+
/* GraphQL */
|
|
47
|
+
/* ------------------------------------------------------------------ */
|
|
48
|
+
const SUBGRAPH_PATH = "/graphql/knowledgeGraph";
|
|
49
|
+
function getEndpoint() {
|
|
50
|
+
const envUrl = typeof import.meta !== "undefined" &&
|
|
51
|
+
import.meta.env?.VITE_SUBGRAPH_URL;
|
|
52
|
+
if (envUrl)
|
|
53
|
+
return envUrl;
|
|
54
|
+
const port = globalThis.window?.location?.port;
|
|
55
|
+
if (port === "3000" || port === "3001") {
|
|
56
|
+
return `http://localhost:4001${SUBGRAPH_PATH}`;
|
|
57
|
+
}
|
|
58
|
+
return SUBGRAPH_PATH;
|
|
59
|
+
}
|
|
60
|
+
const ACTIVITY_QUERY = `
|
|
61
|
+
query Activity($driveId: ID!, $limit: Int) {
|
|
62
|
+
knowledgeGraphActivity(driveId: $driveId, limit: $limit) {
|
|
63
|
+
id documentId operationType timestamp index summary signerAddress signerApp
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
async function fetchActivity(driveId, limit) {
|
|
68
|
+
try {
|
|
69
|
+
const res = await fetch(getEndpoint(), {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: { "Content-Type": "application/json" },
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
query: ACTIVITY_QUERY,
|
|
74
|
+
variables: { driveId, limit },
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok)
|
|
78
|
+
return [];
|
|
79
|
+
const json = (await res.json());
|
|
80
|
+
return json.data?.knowledgeGraphActivity ?? [];
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/* ------------------------------------------------------------------ */
|
|
87
|
+
/* Component */
|
|
88
|
+
/* ------------------------------------------------------------------ */
|
|
89
|
+
export function ActivityView() {
|
|
90
|
+
const driveId = useSelectedDriveId();
|
|
91
|
+
const [operations, setOperations] = useState([]);
|
|
92
|
+
const [loading, setLoading] = useState(true);
|
|
93
|
+
const [filter, setFilter] = useState("all");
|
|
94
|
+
const endpoint = useMemo(() => getEndpoint(), []);
|
|
95
|
+
const loadActivity = useCallback(async () => {
|
|
96
|
+
if (!driveId)
|
|
97
|
+
return;
|
|
98
|
+
setLoading(true);
|
|
99
|
+
const data = await fetchActivity(driveId, 200);
|
|
100
|
+
setOperations(data);
|
|
101
|
+
setLoading(false);
|
|
102
|
+
}, [driveId]);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
void loadActivity();
|
|
105
|
+
}, [loadActivity]);
|
|
106
|
+
const filtered = useMemo(() => {
|
|
107
|
+
if (filter === "all")
|
|
108
|
+
return operations;
|
|
109
|
+
const allowedOps = FILTER_TYPES[filter].ops;
|
|
110
|
+
return operations.filter((op) => allowedOps.includes(op.operationType));
|
|
111
|
+
}, [operations, filter]);
|
|
112
|
+
// Group by date
|
|
113
|
+
const grouped = useMemo(() => {
|
|
114
|
+
const groups = new Map();
|
|
115
|
+
for (const op of filtered) {
|
|
116
|
+
const date = op.timestamp.slice(0, 10);
|
|
117
|
+
const existing = groups.get(date) ?? [];
|
|
118
|
+
existing.push(op);
|
|
119
|
+
groups.set(date, existing);
|
|
120
|
+
}
|
|
121
|
+
return groups;
|
|
122
|
+
}, [filtered]);
|
|
123
|
+
if (loading) {
|
|
124
|
+
return (_jsx("div", { className: "flex h-full items-center justify-center text-sm", style: { color: "var(--bai-text-muted)" }, children: "Loading activity..." }));
|
|
125
|
+
}
|
|
126
|
+
if (operations.length === 0) {
|
|
127
|
+
return (_jsxs("div", { className: "flex h-full flex-col items-center justify-center", children: [_jsxs("svg", { className: "mb-4 h-12 w-12", style: { color: "var(--bai-text-faint)", opacity: 0.5 }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 6v6l4 2" })] }), _jsx("p", { className: "text-sm", style: { color: "var(--bai-text-muted)" }, children: "No activity recorded yet" }), _jsx("p", { className: "mt-1 text-xs", style: { color: "var(--bai-text-faint)" }, children: "Edit notes to start tracking changes" })] }));
|
|
128
|
+
}
|
|
129
|
+
return (_jsxs("div", { className: "flex h-full flex-col p-4", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold", style: { color: "var(--bai-text)" }, children: "Vault Activity" }), _jsxs("p", { className: "text-xs", style: { color: "var(--bai-text-muted)" }, children: [filtered.length, " operation", filtered.length !== 1 ? "s" : "", filter !== "all" ? ` (${FILTER_TYPES[filter].label})` : ""] })] }), _jsx("div", { className: "flex items-center gap-2", children: _jsx("button", { type: "button", onClick: () => void loadActivity(), className: "rounded-md border px-3 py-1.5 text-xs transition-colors", style: {
|
|
130
|
+
borderColor: "var(--bai-border)",
|
|
131
|
+
color: "var(--bai-text-muted)",
|
|
132
|
+
backgroundColor: "var(--bai-surface)",
|
|
133
|
+
}, children: "Refresh" }) })] }), _jsx("div", { className: "mb-4 flex flex-wrap gap-1.5", children: Object.keys(FILTER_TYPES).map((key) => (_jsx("button", { type: "button", onClick: () => setFilter(key), className: "rounded-full px-3 py-1 text-xs transition-colors", style: {
|
|
134
|
+
backgroundColor: filter === key ? "var(--bai-accent)" : "var(--bai-hover)",
|
|
135
|
+
color: filter === key ? "#fff" : "var(--bai-text-muted)",
|
|
136
|
+
}, children: FILTER_TYPES[key].label }, key))) }), _jsx("div", { className: "flex-1 overflow-auto", children: [...grouped.entries()].map(([date, ops]) => (_jsxs("div", { className: "mb-6", children: [_jsx("h3", { className: "sticky top-0 z-10 mb-2 py-1 text-xs font-semibold uppercase tracking-wider", style: {
|
|
137
|
+
color: "var(--bai-text-muted)",
|
|
138
|
+
backgroundColor: "var(--bai-bg)",
|
|
139
|
+
}, children: formatDate(date) }), _jsx("div", { className: "space-y-1", children: ops.map((op) => (_jsx(OperationRow, { op: op }, op.id))) })] }, date))) })] }));
|
|
140
|
+
}
|
|
141
|
+
/* ------------------------------------------------------------------ */
|
|
142
|
+
/* Operation row */
|
|
143
|
+
/* ------------------------------------------------------------------ */
|
|
144
|
+
function OperationRow({ op }) {
|
|
145
|
+
const icon = OP_ICONS[op.operationType] ?? "?";
|
|
146
|
+
const time = op.timestamp.slice(11, 16);
|
|
147
|
+
return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(op.documentId), className: "group flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left transition-colors hover:bg-[var(--bai-hover)]", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[10px] font-bold", style: {
|
|
148
|
+
backgroundColor: opColor(op.operationType) + "20",
|
|
149
|
+
color: opColor(op.operationType),
|
|
150
|
+
}, children: icon }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-xs", style: { color: "var(--bai-text)" }, children: op.summary ?? op.operationType }), _jsxs("p", { className: "truncate text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [op.documentId.slice(0, 8), "...", op.signerAddress || op.signerApp ? (_jsxs("span", { title: op.signerAddress ?? op.signerApp ?? "", className: "cursor-pointer", style: { color: "var(--bai-accent)", opacity: 0.7 }, onClick: (e) => {
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
const text = op.signerAddress ?? op.signerApp ?? "";
|
|
153
|
+
void navigator.clipboard.writeText(text);
|
|
154
|
+
}, children: [" by ", op.signerAddress
|
|
155
|
+
? `${op.signerAddress.slice(0, 6)}...${op.signerAddress.slice(-4)}`
|
|
156
|
+
: op.signerApp, " ", _jsxs("svg", { className: "inline-block h-2.5 w-2.5 opacity-0 group-hover:opacity-50", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })] })] })) : null] })] }), _jsx("span", { className: "shrink-0 text-[10px] font-mono", style: { color: "var(--bai-text-faint)" }, children: time }), _jsx("svg", { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-50", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
|
|
157
|
+
}
|
|
158
|
+
/* ------------------------------------------------------------------ */
|
|
159
|
+
/* Helpers */
|
|
160
|
+
/* ------------------------------------------------------------------ */
|
|
161
|
+
function opColor(type) {
|
|
162
|
+
if (type.startsWith("SET_TITLE") || type.startsWith("SET_CONTENT"))
|
|
163
|
+
return "#f59e0b";
|
|
164
|
+
if (type.startsWith("ADD_LINK") || type.startsWith("REMOVE_LINK"))
|
|
165
|
+
return "#3b82f6";
|
|
166
|
+
if (type.startsWith("ADD_TOPIC") || type.startsWith("REMOVE_TOPIC"))
|
|
167
|
+
return "#10b981";
|
|
168
|
+
if (type === "SUBMIT_FOR_REVIEW" ||
|
|
169
|
+
type === "APPROVE_NOTE" ||
|
|
170
|
+
type === "REJECT_NOTE")
|
|
171
|
+
return "#a855f7";
|
|
172
|
+
if (type === "ARCHIVE_NOTE" || type === "RESTORE_NOTE")
|
|
173
|
+
return "#6b7280";
|
|
174
|
+
return "#64748b";
|
|
175
|
+
}
|
|
176
|
+
function formatDate(dateStr) {
|
|
177
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
178
|
+
const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
|
|
179
|
+
if (dateStr === today)
|
|
180
|
+
return "Today";
|
|
181
|
+
if (dateStr === yesterday)
|
|
182
|
+
return "Yesterday";
|
|
183
|
+
return new Date(dateStr + "T00:00:00").toLocaleDateString("en-US", {
|
|
184
|
+
weekday: "short",
|
|
185
|
+
month: "short",
|
|
186
|
+
day: "numeric",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAmClD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAmXtD"}
|
|
@@ -8,6 +8,7 @@ import { NoteList } from "./NoteList.js";
|
|
|
8
8
|
import { SourceList } from "./SourceList.js";
|
|
9
9
|
import { HealthDashboard } from "./HealthDashboard.js";
|
|
10
10
|
import { SearchView } from "./SearchView.js";
|
|
11
|
+
import { ActivityView } from "./ActivityView.js";
|
|
11
12
|
import { GettingStartedButton } from "./GettingStarted.js";
|
|
12
13
|
import { useKnowledgeNotes } from "../hooks/use-knowledge-notes.js";
|
|
13
14
|
import { useKnowledgeGraph, buildSyncPayload, } from "../hooks/use-knowledge-graph.js";
|
|
@@ -129,6 +130,11 @@ export function DriveExplorer({ children }) {
|
|
|
129
130
|
badge: sourceCount > 0 ? sourceCount : undefined,
|
|
130
131
|
icon: (_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M4 19.5A2.5 2.5 0 016.5 17H20" }), _jsx("path", { d: "M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" })] })),
|
|
131
132
|
},
|
|
133
|
+
{
|
|
134
|
+
key: "activity",
|
|
135
|
+
label: "Activity",
|
|
136
|
+
icon: (_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 6v6l4 2" })] })),
|
|
137
|
+
},
|
|
132
138
|
{
|
|
133
139
|
key: "pipeline",
|
|
134
140
|
label: "Pipeline",
|
|
@@ -161,7 +167,7 @@ export function DriveExplorer({ children }) {
|
|
|
161
167
|
}, children: tab.badge }))] }, tab.key))), showDocumentEditor && (_jsxs("span", { className: "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium", style: {
|
|
162
168
|
backgroundColor: "var(--bai-hover)",
|
|
163
169
|
color: "var(--bai-accent)",
|
|
164
|
-
}, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "search" ? (_jsx(SearchView, {})) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
|
|
170
|
+
}, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "search" ? (_jsx(SearchView, {})) : viewMode === "activity" ? (_jsx(ActivityView, {})) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
|
|
165
171
|
}
|
|
166
172
|
const CREATE_ITEMS = [
|
|
167
173
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/SearchView.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,UAAU,
|
|
1
|
+
{"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/SearchView.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,UAAU,4CAsGzB"}
|
|
@@ -43,19 +43,19 @@ export function SearchView() {
|
|
|
43
43
|
backgroundColor: "var(--bai-surface)",
|
|
44
44
|
borderColor: "var(--bai-border)",
|
|
45
45
|
color: "var(--bai-text)",
|
|
46
|
-
} }), query && (_jsx("button", { type: "button", onClick: () => setQuery(""), className: "absolute right-3 top-1/2 -translate-y-1/2 rounded p-0.5 transition-colors hover:opacity-80", style: { color: "var(--bai-text-muted)" }, children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }))] }), _jsx(ModeToggle, { mode: searchMode, onChange: setSearchMode })] }), query.trim() && (_jsxs("div", { className: "mb-3 flex items-center gap-2 text-xs", style: { color: "var(--bai-text-muted)" }, children: [loading ? (_jsx("span", { children: "Searching..." })) : (_jsxs(_Fragment, { children: [_jsxs("span", { children: [results.length, " result", results.length !== 1 ? "s" : ""] }), searchMode === "semantic"
|
|
46
|
+
} }), query && (_jsx("button", { type: "button", onClick: () => setQuery(""), className: "absolute right-3 top-1/2 -translate-y-1/2 rounded p-0.5 transition-colors hover:opacity-80", style: { color: "var(--bai-text-muted)" }, children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }))] }), _jsx(ModeToggle, { mode: searchMode, onChange: setSearchMode })] }), query.trim() && (_jsxs("div", { className: "mb-3 flex items-center gap-2 text-xs", style: { color: "var(--bai-text-muted)" }, children: [loading ? (_jsx("span", { children: "Searching..." })) : (_jsxs(_Fragment, { children: [_jsxs("span", { children: [results.length, " result", results.length !== 1 ? "s" : ""] }), (searchMode === "semantic" || searchMode === "hybrid") &&
|
|
47
|
+
results.length > 0 && (_jsxs("span", { style: { opacity: 0.6 }, children: ["\u2014 score shows relevance", searchMode === "hybrid"
|
|
48
|
+
? " combining keyword + semantic matching"
|
|
49
|
+
: " by meaning"] }))] })), error && _jsx("span", { style: { color: "#ef4444" }, children: error })] })), _jsx("div", { className: "flex-1 overflow-auto", children: !query.trim() ? (_jsx(EmptyState, { topics: topics, onTopicClick: setQuery })) : (_jsx(ResultList, { results: results, searchMode: searchMode })) })] }));
|
|
47
50
|
}
|
|
48
51
|
/* ------------------------------------------------------------------ */
|
|
49
52
|
/* Mode toggle */
|
|
50
53
|
/* ------------------------------------------------------------------ */
|
|
51
54
|
function ModeToggle({ mode, onChange, }) {
|
|
52
|
-
return (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
backgroundColor: mode === "keyword" ? "var(--bai-accent)" : "var(--bai-surface)",
|
|
57
|
-
color: mode === "keyword" ? "#fff" : "var(--bai-text-muted)",
|
|
58
|
-
}, children: "Keyword" })] }));
|
|
55
|
+
return (_jsx("div", { className: "flex shrink-0 overflow-hidden rounded-lg border text-xs", style: { borderColor: "var(--bai-border)" }, children: ["hybrid", "semantic", "keyword"].map((m) => (_jsx("button", { type: "button", onClick: () => onChange(m), className: "px-3 py-2 transition-colors capitalize", style: {
|
|
56
|
+
backgroundColor: mode === m ? "var(--bai-accent)" : "var(--bai-surface)",
|
|
57
|
+
color: mode === m ? "#fff" : "var(--bai-text-muted)",
|
|
58
|
+
}, children: m }, m))) }));
|
|
59
59
|
}
|
|
60
60
|
/* ------------------------------------------------------------------ */
|
|
61
61
|
/* Empty state */
|
|
@@ -78,7 +78,7 @@ function ResultList({ results, searchMode, }) {
|
|
|
78
78
|
if (results.length === 0) {
|
|
79
79
|
return (_jsx("div", { className: "py-12 text-center text-sm", style: { color: "var(--bai-text-muted)" }, children: "No results found" }));
|
|
80
80
|
}
|
|
81
|
-
return (_jsx("div", { className: "space-y-2", children: results.map((result) => (_jsx(ResultCard, { result: result, showSimilarity: searchMode
|
|
81
|
+
return (_jsx("div", { className: "space-y-2", children: results.map((result) => (_jsx(ResultCard, { result: result, showSimilarity: searchMode !== "keyword" }, result.documentId))) }));
|
|
82
82
|
}
|
|
83
83
|
/* ------------------------------------------------------------------ */
|
|
84
84
|
/* Result card */
|
|
@@ -86,11 +86,18 @@ function ResultList({ results, searchMode, }) {
|
|
|
86
86
|
function ResultCard({ result, showSimilarity, }) {
|
|
87
87
|
const status = result.status ?? "DRAFT";
|
|
88
88
|
const badgeStyle = STATUS_COLORS[status] ?? STATUS_COLORS.DRAFT;
|
|
89
|
-
return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(result.documentId), className: "group flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all border-[var(--bai-border)] bg-[var(--bai-surface)] hover:border-[var(--bai-accent)] hover:bg-[var(--bai-hover)]", children: [showSimilarity && result.similarity != null && (_jsxs("div", { className: "shrink-0 rounded-md px-2 py-1 text-center font-mono text-xs font-bold", title: "
|
|
89
|
+
return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(result.documentId), className: "group flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all border-[var(--bai-border)] bg-[var(--bai-surface)] hover:border-[var(--bai-accent)] hover:bg-[var(--bai-hover)]", children: [showSimilarity && result.similarity != null && (_jsxs("div", { className: "shrink-0 rounded-md px-2 py-1 text-center font-mono text-xs font-bold", title: "Relevance score \u2014 how closely this note matches your query (higher is better)", style: {
|
|
90
90
|
color: similarityColor(result.similarity),
|
|
91
91
|
backgroundColor: `${similarityColor(result.similarity)}15`,
|
|
92
92
|
}, children: [(result.similarity * 100).toFixed(0), "%"] })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "mb-1 flex items-start justify-between gap-2", children: [_jsx("h4", { className: "truncate text-sm font-semibold text-[var(--bai-text)] group-hover:text-[var(--bai-accent)]", children: result.title ?? "Untitled" }), _jsx("span", { className: "shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium", style: badgeStyle, children: status.replace("_", " ") })] }), result.description && (_jsx("p", { className: "mb-2 line-clamp-2 text-xs leading-relaxed", style: { color: "var(--bai-text-muted)" }, children: result.description })), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [result.noteType && (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[10px] font-medium", style: {
|
|
93
93
|
background: "var(--bai-hover)",
|
|
94
94
|
color: "var(--bai-text-muted)",
|
|
95
|
-
}, children: result.noteType })), result.topics?.slice(0, 4).map((topic) => (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-accent)", opacity: 0.6 }, children: ["#", topic] }, topic)))
|
|
95
|
+
}, children: result.noteType })), result.topics?.slice(0, 4).map((topic) => (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-accent)", opacity: 0.6 }, children: ["#", topic] }, topic))), result.matchedBy && result.matchedBy.length > 0 && (_jsx("span", { className: "ml-auto flex gap-1", children: result.matchedBy.map((m) => (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[9px] font-medium", style: {
|
|
96
|
+
background: m === "semantic"
|
|
97
|
+
? "rgba(139, 92, 246, 0.15)"
|
|
98
|
+
: "rgba(59, 130, 246, 0.15)",
|
|
99
|
+
color: m === "semantic"
|
|
100
|
+
? "rgba(167, 139, 250, 1)"
|
|
101
|
+
: "rgba(147, 197, 253, 1)",
|
|
102
|
+
}, children: m }, m))) }))] })] }), _jsx("svg", { className: "mt-1 h-4 w-4 shrink-0 opacity-0 transition-opacity group-hover:opacity-60", style: { color: "var(--bai-text-muted)" }, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M9 18l6-6-6-6" }) })] }));
|
|
96
103
|
}
|
|
@@ -6,12 +6,13 @@ export type SearchResult = {
|
|
|
6
6
|
status: string | null;
|
|
7
7
|
topics: string[];
|
|
8
8
|
similarity?: number;
|
|
9
|
+
matchedBy?: string[];
|
|
9
10
|
};
|
|
10
11
|
export type TopicInfo = {
|
|
11
12
|
name: string;
|
|
12
13
|
noteCount: number;
|
|
13
14
|
};
|
|
14
|
-
export type SearchMode = "semantic" | "keyword";
|
|
15
|
+
export type SearchMode = "hybrid" | "semantic" | "keyword";
|
|
15
16
|
export declare function useGraphSearch(): {
|
|
16
17
|
query: string;
|
|
17
18
|
setQuery: (q: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-graph-search.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-graph-search.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,YAAY,GAAG;IACzB,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,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"use-graph-search.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-graph-search.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,YAAY,GAAG;IACzB,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,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAkJ3D,wBAAgB,cAAc;;kBAgBtB,MAAM;;;;;;uBAQN,UAAU;EA4HjB"}
|
|
@@ -79,6 +79,15 @@ const KEYWORD_SEARCH_QUERY = `
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
`;
|
|
82
|
+
const HYBRID_SEARCH_QUERY = `
|
|
83
|
+
query HybridSearch($driveId: ID!, $query: String!, $limit: Int) {
|
|
84
|
+
knowledgeGraphHybridSearch(driveId: $driveId, query: $query, limit: $limit) {
|
|
85
|
+
node { documentId title description noteType status topics }
|
|
86
|
+
score
|
|
87
|
+
matchedBy
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
82
91
|
const TOPICS_QUERY = `
|
|
83
92
|
query Topics($driveId: ID!) {
|
|
84
93
|
knowledgeGraphTopics(driveId: $driveId) { name noteCount }
|
|
@@ -93,9 +102,14 @@ function loadSearchState() {
|
|
|
93
102
|
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
94
103
|
if (raw) {
|
|
95
104
|
const parsed = JSON.parse(raw);
|
|
105
|
+
const mode = parsed.mode;
|
|
96
106
|
return {
|
|
97
107
|
query: parsed.query ?? "",
|
|
98
|
-
mode:
|
|
108
|
+
mode: mode === "keyword"
|
|
109
|
+
? "keyword"
|
|
110
|
+
: mode === "semantic"
|
|
111
|
+
? "semantic"
|
|
112
|
+
: "hybrid",
|
|
99
113
|
};
|
|
100
114
|
}
|
|
101
115
|
}
|
|
@@ -151,7 +165,24 @@ export function useGraphSearch() {
|
|
|
151
165
|
}
|
|
152
166
|
setLoading(true);
|
|
153
167
|
setError(null);
|
|
154
|
-
if (mode === "
|
|
168
|
+
if (mode === "hybrid") {
|
|
169
|
+
const data = await graphqlFetch(endpoint, HYBRID_SEARCH_QUERY, { driveId, query: q, limit: 20 });
|
|
170
|
+
if (data?.knowledgeGraphHybridSearch) {
|
|
171
|
+
const raw = data.knowledgeGraphHybridSearch;
|
|
172
|
+
// Normalize RRF scores to 0-1 range (top result = 1.0)
|
|
173
|
+
const maxScore = raw.length > 0 ? raw[0].score : 1;
|
|
174
|
+
setResults(raw.map((r) => ({
|
|
175
|
+
...r.node,
|
|
176
|
+
similarity: maxScore > 0 ? r.score / maxScore : 0,
|
|
177
|
+
matchedBy: r.matchedBy,
|
|
178
|
+
})));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
setResults([]);
|
|
182
|
+
setError("Hybrid search unavailable. Try keyword mode.");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (mode === "semantic") {
|
|
155
186
|
const data = await graphqlFetch(endpoint, SEMANTIC_SEARCH_QUERY, { driveId, query: q, limit: 20 });
|
|
156
187
|
if (data?.knowledgeGraphSemanticSearch) {
|
|
157
188
|
setResults(data.knowledgeGraphSemanticSearch.map((r) => ({
|
package/dist/package.json
CHANGED
|
@@ -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;
|
|
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;AA+DtC,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;IAgMV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,UAAU;CA8BzB"}
|
|
@@ -2,6 +2,62 @@ import { RelationalDbProcessor } from "@powerhousedao/shared/processors";
|
|
|
2
2
|
import { up } from "./migrations.js";
|
|
3
3
|
import { generateEmbedding } from "./embedder.js";
|
|
4
4
|
import { upsertEmbedding, deleteEmbedding } from "./embedding-store.js";
|
|
5
|
+
function summarizeOperation(type, input) {
|
|
6
|
+
switch (type) {
|
|
7
|
+
case "SET_TITLE":
|
|
8
|
+
return `Title changed to "${truncate(input.title)}"`;
|
|
9
|
+
case "SET_DESCRIPTION":
|
|
10
|
+
return `Description updated`;
|
|
11
|
+
case "SET_CONTENT": {
|
|
12
|
+
const len = typeof input.content === "string" ? input.content.length : 0;
|
|
13
|
+
return `Content updated (${len} chars)`;
|
|
14
|
+
}
|
|
15
|
+
case "SET_NOTE_TYPE":
|
|
16
|
+
return `Type set to ${String(input.noteType)}`;
|
|
17
|
+
case "SET_STATUS":
|
|
18
|
+
return `Status changed to ${String(input.status)}`;
|
|
19
|
+
case "ADD_LINK":
|
|
20
|
+
return `Linked to "${truncate(input.targetTitle)}" (${s(input.linkType, "RELATES_TO")})`;
|
|
21
|
+
case "REMOVE_LINK":
|
|
22
|
+
return `Removed link ${s(input.id)}`;
|
|
23
|
+
case "UPDATE_LINK_TYPE":
|
|
24
|
+
return `Link type changed to ${s(input.linkType)}`;
|
|
25
|
+
case "ADD_TOPIC":
|
|
26
|
+
return `Added topic #${s(input.name)}`;
|
|
27
|
+
case "REMOVE_TOPIC":
|
|
28
|
+
return `Removed topic`;
|
|
29
|
+
case "SET_PROVENANCE":
|
|
30
|
+
return `Provenance set: ${s(input.author, "unknown")}, ${s(input.sourceOrigin)}`;
|
|
31
|
+
case "SUBMIT_FOR_REVIEW":
|
|
32
|
+
return `Submitted for review`;
|
|
33
|
+
case "APPROVE_NOTE":
|
|
34
|
+
return `Approved by ${s(input.actor, "unknown")}`;
|
|
35
|
+
case "REJECT_NOTE":
|
|
36
|
+
return `Rejected: ${truncate(input.comment)}`;
|
|
37
|
+
case "ARCHIVE_NOTE":
|
|
38
|
+
return `Archived`;
|
|
39
|
+
case "RESTORE_NOTE":
|
|
40
|
+
return `Restored from archive`;
|
|
41
|
+
case "SET_METADATA_FIELD":
|
|
42
|
+
return `Metadata: ${String(input.field)} = ${truncate(input.value)}`;
|
|
43
|
+
default:
|
|
44
|
+
return type;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Safely stringify an unknown value with optional fallback */
|
|
48
|
+
function s(val, fallback = "") {
|
|
49
|
+
if (val == null)
|
|
50
|
+
return fallback;
|
|
51
|
+
if (typeof val === "string")
|
|
52
|
+
return val;
|
|
53
|
+
if (typeof val === "number" || typeof val === "boolean")
|
|
54
|
+
return String(val);
|
|
55
|
+
return fallback;
|
|
56
|
+
}
|
|
57
|
+
function truncate(val, max = 60) {
|
|
58
|
+
const str = s(val);
|
|
59
|
+
return str.length > max ? str.slice(0, max) + "..." : str;
|
|
60
|
+
}
|
|
5
61
|
export class GraphIndexerProcessor extends RelationalDbProcessor {
|
|
6
62
|
static getNamespace(driveId) {
|
|
7
63
|
return super.getNamespace(driveId);
|
|
@@ -28,6 +84,30 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
|
|
|
28
84
|
// Only process knowledge-note documents
|
|
29
85
|
if (context.documentType !== "bai/knowledge-note")
|
|
30
86
|
continue;
|
|
87
|
+
// Index operation for history tracking
|
|
88
|
+
try {
|
|
89
|
+
const input = operation.action.input;
|
|
90
|
+
const signer = operation.action.context?.signer;
|
|
91
|
+
await this.relationalDb
|
|
92
|
+
.insertInto("graph_operations")
|
|
93
|
+
.values({
|
|
94
|
+
id: `${documentId}-${operation.index}`,
|
|
95
|
+
document_id: documentId,
|
|
96
|
+
operation_type: operation.action.type,
|
|
97
|
+
timestamp: operation.timestampUtcMs ?? new Date().toISOString(),
|
|
98
|
+
index: operation.index,
|
|
99
|
+
scope: context.scope ?? "global",
|
|
100
|
+
summary: summarizeOperation(operation.action.type, input),
|
|
101
|
+
input_json: JSON.stringify(input),
|
|
102
|
+
signer_address: signer?.user?.address || null,
|
|
103
|
+
signer_app: signer?.app?.name || null,
|
|
104
|
+
})
|
|
105
|
+
.onConflict((oc) => oc.column("id").doNothing())
|
|
106
|
+
.execute();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// non-critical — don't block state reconciliation
|
|
110
|
+
}
|
|
31
111
|
// Collect last state per document
|
|
32
112
|
if (context.resultingState) {
|
|
33
113
|
lastByDocument.set(documentId, entry);
|
|
@@ -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,
|
|
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,CAqI9D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhE"}
|
|
@@ -73,8 +73,54 @@ export async function up(db) {
|
|
|
73
73
|
.column("name")
|
|
74
74
|
.ifNotExists()
|
|
75
75
|
.execute();
|
|
76
|
+
// --- Operation history ---
|
|
77
|
+
await db.schema
|
|
78
|
+
.createTable("graph_operations")
|
|
79
|
+
.addColumn("id", "varchar(255)", (col) => col.primaryKey())
|
|
80
|
+
.addColumn("document_id", "varchar(255)", (col) => col.notNull())
|
|
81
|
+
.addColumn("operation_type", "varchar(100)", (col) => col.notNull())
|
|
82
|
+
.addColumn("timestamp", "varchar(50)", (col) => col.notNull())
|
|
83
|
+
.addColumn("index", "integer", (col) => col.notNull())
|
|
84
|
+
.addColumn("scope", "varchar(20)", (col) => col.notNull())
|
|
85
|
+
.addColumn("summary", "text")
|
|
86
|
+
.addColumn("input_json", "text")
|
|
87
|
+
.addColumn("signer_address", "varchar(255)")
|
|
88
|
+
.addColumn("signer_app", "varchar(255)")
|
|
89
|
+
.ifNotExists()
|
|
90
|
+
.execute();
|
|
91
|
+
await db.schema
|
|
92
|
+
.createIndex("idx_graph_ops_doc")
|
|
93
|
+
.on("graph_operations")
|
|
94
|
+
.column("document_id")
|
|
95
|
+
.ifNotExists()
|
|
96
|
+
.execute();
|
|
97
|
+
await db.schema
|
|
98
|
+
.createIndex("idx_graph_ops_timestamp")
|
|
99
|
+
.on("graph_operations")
|
|
100
|
+
.column("timestamp")
|
|
101
|
+
.ifNotExists()
|
|
102
|
+
.execute();
|
|
103
|
+
await db.schema
|
|
104
|
+
.createIndex("idx_graph_ops_type")
|
|
105
|
+
.on("graph_operations")
|
|
106
|
+
.column("operation_type")
|
|
107
|
+
.ifNotExists()
|
|
108
|
+
.execute();
|
|
109
|
+
// Add signer columns to graph_operations (idempotent)
|
|
110
|
+
for (const col of ["signer_address", "signer_app"]) {
|
|
111
|
+
try {
|
|
112
|
+
await db.schema
|
|
113
|
+
.alterTable("graph_operations")
|
|
114
|
+
.addColumn(col, "varchar(255)")
|
|
115
|
+
.execute();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// column likely already exists — ignore
|
|
119
|
+
}
|
|
120
|
+
}
|
|
76
121
|
}
|
|
77
122
|
export async function down(db) {
|
|
123
|
+
await db.schema.dropTable("graph_operations").ifExists().execute();
|
|
78
124
|
await db.schema.dropTable("graph_topics").ifExists().execute();
|
|
79
125
|
await db.schema.dropTable("graph_edges").ifExists().execute();
|
|
80
126
|
await db.schema.dropTable("graph_nodes").ifExists().execute();
|
|
@@ -40,6 +40,22 @@ export interface RelatedByTopicResult {
|
|
|
40
40
|
sharedTopics: string[];
|
|
41
41
|
sharedTopicCount: number;
|
|
42
42
|
}
|
|
43
|
+
export interface HybridSearchResult {
|
|
44
|
+
node: GraphNodeResult;
|
|
45
|
+
score: number;
|
|
46
|
+
matchedBy: string[];
|
|
47
|
+
}
|
|
48
|
+
export interface OperationRecord {
|
|
49
|
+
id: string;
|
|
50
|
+
documentId: string;
|
|
51
|
+
operationType: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
index: number;
|
|
54
|
+
scope: string;
|
|
55
|
+
summary: string | null;
|
|
56
|
+
signerAddress: string | null;
|
|
57
|
+
signerApp: string | null;
|
|
58
|
+
}
|
|
43
59
|
export declare function createGraphQuery(db: Kysely<DB>): {
|
|
44
60
|
allNodes(): Promise<GraphNodeResult[]>;
|
|
45
61
|
allEdges(): Promise<GraphEdgeResult[]>;
|
|
@@ -66,5 +82,18 @@ export declare function createGraphQuery(db: Kysely<DB>): {
|
|
|
66
82
|
nodesByAuthor(author: string): Promise<GraphNodeResult[]>;
|
|
67
83
|
nodesByOrigin(origin: string): Promise<GraphNodeResult[]>;
|
|
68
84
|
recentNodes(limit?: number, since?: string): Promise<GraphNodeResult[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Hybrid search: run keyword + semantic in parallel, merge via
|
|
87
|
+
* Reciprocal Rank Fusion (RRF). semanticResults must be pre-fetched
|
|
88
|
+
* from the embedding store by the caller.
|
|
89
|
+
*/
|
|
90
|
+
hybridSearch(keywordQuery: string, semanticResults: Array<{
|
|
91
|
+
documentId: string;
|
|
92
|
+
similarity: number;
|
|
93
|
+
}>, limit?: number): Promise<HybridSearchResult[]>;
|
|
94
|
+
history(documentId: string, limit?: number): Promise<OperationRecord[]>;
|
|
95
|
+
activity(limit?: number, since?: string): Promise<OperationRecord[]>;
|
|
96
|
+
activityByType(operationType: string, limit?: number): Promise<OperationRecord[]>;
|
|
97
|
+
staleNodes(since: string, limit?: number): Promise<GraphNodeResult[]>;
|
|
69
98
|
};
|
|
70
99
|
//# 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;AAErC,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,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,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;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,eAAe,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/query.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,19 @@ function rowToEdge(row) {
|
|
|
24
24
|
updatedAt: row.updated_at,
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
+
function rowToOperation(row) {
|
|
28
|
+
return {
|
|
29
|
+
id: row.id,
|
|
30
|
+
documentId: row.document_id,
|
|
31
|
+
operationType: row.operation_type,
|
|
32
|
+
timestamp: row.timestamp,
|
|
33
|
+
index: row.index,
|
|
34
|
+
scope: row.scope,
|
|
35
|
+
summary: row.summary,
|
|
36
|
+
signerAddress: row.signer_address,
|
|
37
|
+
signerApp: row.signer_app,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
27
40
|
export function createGraphQuery(db) {
|
|
28
41
|
return {
|
|
29
42
|
async allNodes() {
|
|
@@ -372,5 +385,96 @@ export function createGraphQuery(db) {
|
|
|
372
385
|
const rows = await query.execute();
|
|
373
386
|
return rows.map(rowToNode);
|
|
374
387
|
},
|
|
388
|
+
/**
|
|
389
|
+
* Hybrid search: run keyword + semantic in parallel, merge via
|
|
390
|
+
* Reciprocal Rank Fusion (RRF). semanticResults must be pre-fetched
|
|
391
|
+
* from the embedding store by the caller.
|
|
392
|
+
*/
|
|
393
|
+
async hybridSearch(keywordQuery, semanticResults, limit = 20) {
|
|
394
|
+
const K = 60;
|
|
395
|
+
const scores = new Map();
|
|
396
|
+
// Keyword leg
|
|
397
|
+
const keywordResults = await this.fullSearch(keywordQuery, limit * 2);
|
|
398
|
+
keywordResults.forEach((node, rank) => {
|
|
399
|
+
const existing = scores.get(node.documentId) ?? {
|
|
400
|
+
score: 0,
|
|
401
|
+
matchedBy: [],
|
|
402
|
+
};
|
|
403
|
+
existing.score += 1 / (K + rank);
|
|
404
|
+
existing.matchedBy.push("keyword");
|
|
405
|
+
existing.node = node;
|
|
406
|
+
scores.set(node.documentId, existing);
|
|
407
|
+
});
|
|
408
|
+
// Semantic leg
|
|
409
|
+
for (let rank = 0; rank < semanticResults.length; rank++) {
|
|
410
|
+
const sr = semanticResults[rank];
|
|
411
|
+
const existing = scores.get(sr.documentId) ?? {
|
|
412
|
+
score: 0,
|
|
413
|
+
matchedBy: [],
|
|
414
|
+
};
|
|
415
|
+
existing.score += 1 / (K + rank);
|
|
416
|
+
if (!existing.matchedBy.includes("semantic")) {
|
|
417
|
+
existing.matchedBy.push("semantic");
|
|
418
|
+
}
|
|
419
|
+
// Fetch node data if we don't have it from keyword results
|
|
420
|
+
if (!existing.node) {
|
|
421
|
+
existing.node = await this.nodeByDocumentId(sr.documentId);
|
|
422
|
+
}
|
|
423
|
+
scores.set(sr.documentId, existing);
|
|
424
|
+
}
|
|
425
|
+
return [...scores.values()]
|
|
426
|
+
.filter((e) => !!e.node)
|
|
427
|
+
.sort((a, b) => b.score - a.score)
|
|
428
|
+
.slice(0, limit)
|
|
429
|
+
.map(({ score, matchedBy, node }) => ({ node, score, matchedBy }));
|
|
430
|
+
},
|
|
431
|
+
// ---- Operation history queries ----
|
|
432
|
+
async history(documentId, limit = 50) {
|
|
433
|
+
const rows = await db
|
|
434
|
+
.selectFrom("graph_operations")
|
|
435
|
+
.where("document_id", "=", documentId)
|
|
436
|
+
.orderBy("index", "desc")
|
|
437
|
+
.selectAll()
|
|
438
|
+
.limit(limit)
|
|
439
|
+
.execute();
|
|
440
|
+
return rows.map(rowToOperation);
|
|
441
|
+
},
|
|
442
|
+
async activity(limit = 50, since) {
|
|
443
|
+
let query = db
|
|
444
|
+
.selectFrom("graph_operations")
|
|
445
|
+
.orderBy("timestamp", "desc")
|
|
446
|
+
.selectAll()
|
|
447
|
+
.limit(limit);
|
|
448
|
+
if (since) {
|
|
449
|
+
query = query.where("timestamp", ">", since);
|
|
450
|
+
}
|
|
451
|
+
const rows = await query.execute();
|
|
452
|
+
return rows.map(rowToOperation);
|
|
453
|
+
},
|
|
454
|
+
async activityByType(operationType, limit = 50) {
|
|
455
|
+
const rows = await db
|
|
456
|
+
.selectFrom("graph_operations")
|
|
457
|
+
.where("operation_type", "=", operationType)
|
|
458
|
+
.orderBy("timestamp", "desc")
|
|
459
|
+
.selectAll()
|
|
460
|
+
.limit(limit)
|
|
461
|
+
.execute();
|
|
462
|
+
return rows.map(rowToOperation);
|
|
463
|
+
},
|
|
464
|
+
async staleNodes(since, limit = 50) {
|
|
465
|
+
// Nodes with no operations after `since` but that have neighbors
|
|
466
|
+
// who DO have operations after `since`
|
|
467
|
+
const staleRows = await db
|
|
468
|
+
.selectFrom("graph_nodes")
|
|
469
|
+
.where("document_id", "not in", db
|
|
470
|
+
.selectFrom("graph_operations")
|
|
471
|
+
.where("timestamp", ">", since)
|
|
472
|
+
.select("document_id"))
|
|
473
|
+
.where("updated_at", "<", since)
|
|
474
|
+
.selectAll()
|
|
475
|
+
.limit(limit)
|
|
476
|
+
.execute();
|
|
477
|
+
return staleRows.map(rowToNode);
|
|
478
|
+
},
|
|
375
479
|
};
|
|
376
480
|
}
|
|
@@ -25,9 +25,22 @@ export interface GraphEdge {
|
|
|
25
25
|
target_title: string | null;
|
|
26
26
|
updated_at: string;
|
|
27
27
|
}
|
|
28
|
+
export interface GraphOperation {
|
|
29
|
+
id: string;
|
|
30
|
+
document_id: string;
|
|
31
|
+
operation_type: string;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
index: number;
|
|
34
|
+
scope: string;
|
|
35
|
+
summary: string | null;
|
|
36
|
+
input_json: string | null;
|
|
37
|
+
signer_address: string | null;
|
|
38
|
+
signer_app: string | null;
|
|
39
|
+
}
|
|
28
40
|
export interface DB {
|
|
29
41
|
graph_nodes: GraphNode;
|
|
30
42
|
graph_edges: GraphEdge;
|
|
31
43
|
graph_topics: GraphTopic;
|
|
44
|
+
graph_operations: GraphOperation;
|
|
32
45
|
}
|
|
33
46
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,EAAE;IACjB,WAAW,EAAE,SAAS,CAAC;IACvB,WAAW,EAAE,SAAS,CAAC;IACvB,YAAY,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,EAAE;IACjB,WAAW,EAAE,SAAS,CAAC;IACvB,WAAW,EAAE,SAAS,CAAC;IACvB,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,cAAc,CAAC;CAClC"}
|
package/dist/style.css
CHANGED
|
@@ -1509,6 +1509,13 @@
|
|
|
1509
1509
|
}
|
|
1510
1510
|
}
|
|
1511
1511
|
}
|
|
1512
|
+
.group-hover\:opacity-50 {
|
|
1513
|
+
&:is(:where(.group):hover *) {
|
|
1514
|
+
@media (hover: hover) {
|
|
1515
|
+
opacity: 50%;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1512
1519
|
.group-hover\:opacity-60 {
|
|
1513
1520
|
&:is(:where(.group):hover *) {
|
|
1514
1521
|
@media (hover: hover) {
|
|
@@ -360,6 +360,61 @@ export declare class KnowledgeGraphSubgraph extends BaseSubgraph {
|
|
|
360
360
|
};
|
|
361
361
|
similarity: number;
|
|
362
362
|
}[]>;
|
|
363
|
+
knowledgeGraphHybridSearch: (_: unknown, args: {
|
|
364
|
+
driveId: string;
|
|
365
|
+
query: string;
|
|
366
|
+
limit?: number;
|
|
367
|
+
}) => Promise<{
|
|
368
|
+
node: {
|
|
369
|
+
_driveId: string;
|
|
370
|
+
id: string;
|
|
371
|
+
documentId: string;
|
|
372
|
+
title: string | null;
|
|
373
|
+
description: string | null;
|
|
374
|
+
noteType: string | null;
|
|
375
|
+
status: string | null;
|
|
376
|
+
content: string | null;
|
|
377
|
+
author: string | null;
|
|
378
|
+
sourceOrigin: string | null;
|
|
379
|
+
createdAt: string | null;
|
|
380
|
+
updatedAt: string;
|
|
381
|
+
};
|
|
382
|
+
score: number;
|
|
383
|
+
matchedBy: string[];
|
|
384
|
+
}[]>;
|
|
385
|
+
knowledgeGraphHistory: (_: unknown, args: {
|
|
386
|
+
driveId: string;
|
|
387
|
+
documentId: string;
|
|
388
|
+
limit?: number;
|
|
389
|
+
}) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
|
|
390
|
+
knowledgeGraphActivity: (_: unknown, args: {
|
|
391
|
+
driveId: string;
|
|
392
|
+
limit?: number;
|
|
393
|
+
since?: string;
|
|
394
|
+
}) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
|
|
395
|
+
knowledgeGraphActivityByType: (_: unknown, args: {
|
|
396
|
+
driveId: string;
|
|
397
|
+
operationType: string;
|
|
398
|
+
limit?: number;
|
|
399
|
+
}) => Promise<import("../../processors/graph-indexer/query.js").OperationRecord[]>;
|
|
400
|
+
knowledgeGraphStale: (_: unknown, args: {
|
|
401
|
+
driveId: string;
|
|
402
|
+
since: string;
|
|
403
|
+
limit?: number;
|
|
404
|
+
}) => Promise<{
|
|
405
|
+
_driveId: string;
|
|
406
|
+
id: string;
|
|
407
|
+
documentId: string;
|
|
408
|
+
title: string | null;
|
|
409
|
+
description: string | null;
|
|
410
|
+
noteType: string | null;
|
|
411
|
+
status: string | null;
|
|
412
|
+
content: string | null;
|
|
413
|
+
author: string | null;
|
|
414
|
+
sourceOrigin: string | null;
|
|
415
|
+
createdAt: string | null;
|
|
416
|
+
updatedAt: string;
|
|
417
|
+
}[]>;
|
|
363
418
|
knowledgeGraphDebug: (_: unknown, args: {
|
|
364
419
|
driveId: string;
|
|
365
420
|
}) => Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subgraph.d.ts","sourceRoot":"","sources":["../../../subgraphs/knowledge-graph/subgraph.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAa7E,qBAAa,sBAAuB,SAAQ,YAAY;IAC7C,IAAI,SAAoB;IAExB,QAAQ,
|
|
1
|
+
{"version":3,"file":"subgraph.d.ts","sourceRoot":"","sources":["../../../subgraphs/knowledge-graph/subgraph.ts"],"names":[],"mappings":"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"}
|
|
@@ -129,6 +129,32 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
|
|
|
129
129
|
documentId: String!
|
|
130
130
|
limit: Int
|
|
131
131
|
): [SemanticResult!]!
|
|
132
|
+
knowledgeGraphHybridSearch(
|
|
133
|
+
driveId: ID!
|
|
134
|
+
query: String!
|
|
135
|
+
limit: Int
|
|
136
|
+
): [HybridResult!]!
|
|
137
|
+
|
|
138
|
+
knowledgeGraphHistory(
|
|
139
|
+
driveId: ID!
|
|
140
|
+
documentId: String!
|
|
141
|
+
limit: Int
|
|
142
|
+
): [OperationRecord!]!
|
|
143
|
+
knowledgeGraphActivity(
|
|
144
|
+
driveId: ID!
|
|
145
|
+
limit: Int
|
|
146
|
+
since: String
|
|
147
|
+
): [OperationRecord!]!
|
|
148
|
+
knowledgeGraphActivityByType(
|
|
149
|
+
driveId: ID!
|
|
150
|
+
operationType: String!
|
|
151
|
+
limit: Int
|
|
152
|
+
): [OperationRecord!]!
|
|
153
|
+
knowledgeGraphStale(
|
|
154
|
+
driveId: ID!
|
|
155
|
+
since: String!
|
|
156
|
+
limit: Int
|
|
157
|
+
): [KnowledgeGraphNode!]!
|
|
132
158
|
|
|
133
159
|
"""
|
|
134
160
|
Debug: raw processor DB tables
|
|
@@ -147,6 +173,24 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
|
|
|
147
173
|
similarity: Float!
|
|
148
174
|
}
|
|
149
175
|
|
|
176
|
+
type HybridResult {
|
|
177
|
+
node: KnowledgeGraphNode!
|
|
178
|
+
score: Float!
|
|
179
|
+
matchedBy: [String!]!
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
type OperationRecord {
|
|
183
|
+
id: String!
|
|
184
|
+
documentId: String!
|
|
185
|
+
operationType: String!
|
|
186
|
+
timestamp: String!
|
|
187
|
+
index: Int!
|
|
188
|
+
scope: String!
|
|
189
|
+
summary: String
|
|
190
|
+
signerAddress: String
|
|
191
|
+
signerApp: String
|
|
192
|
+
}
|
|
193
|
+
|
|
150
194
|
type GraphDebugInfo {
|
|
151
195
|
rawNodeCount: Int!
|
|
152
196
|
rawEdgeCount: Int!
|
|
@@ -331,6 +375,34 @@ export class KnowledgeGraphSubgraph extends BaseSubgraph {
|
|
|
331
375
|
}
|
|
332
376
|
return semanticResults.slice(0, args.limit ?? 10);
|
|
333
377
|
},
|
|
378
|
+
knowledgeGraphHybridSearch: async (_, args) => {
|
|
379
|
+
const queryEmbedding = await generateEmbedding(args.query);
|
|
380
|
+
const semanticResults = await searchSimilar(queryEmbedding, (args.limit ?? 20) * 2);
|
|
381
|
+
const graphQuery = this.getQuery(args.driveId);
|
|
382
|
+
const hybridResults = await graphQuery.hybridSearch(args.query, semanticResults, args.limit ?? 20);
|
|
383
|
+
return hybridResults.map((r) => ({
|
|
384
|
+
node: { ...r.node, _driveId: args.driveId },
|
|
385
|
+
score: r.score,
|
|
386
|
+
matchedBy: r.matchedBy,
|
|
387
|
+
}));
|
|
388
|
+
},
|
|
389
|
+
knowledgeGraphHistory: async (_, args) => {
|
|
390
|
+
const query = this.getQuery(args.driveId);
|
|
391
|
+
return query.history(args.documentId, args.limit ?? 50);
|
|
392
|
+
},
|
|
393
|
+
knowledgeGraphActivity: async (_, args) => {
|
|
394
|
+
const query = this.getQuery(args.driveId);
|
|
395
|
+
return query.activity(args.limit ?? 50, args.since ?? undefined);
|
|
396
|
+
},
|
|
397
|
+
knowledgeGraphActivityByType: async (_, args) => {
|
|
398
|
+
const query = this.getQuery(args.driveId);
|
|
399
|
+
return query.activityByType(args.operationType, args.limit ?? 50);
|
|
400
|
+
},
|
|
401
|
+
knowledgeGraphStale: async (_, args) => {
|
|
402
|
+
const query = this.getQuery(args.driveId);
|
|
403
|
+
const nodes = await query.staleNodes(args.since, args.limit ?? 50);
|
|
404
|
+
return nodes.map((n) => ({ ...n, _driveId: args.driveId }));
|
|
405
|
+
},
|
|
334
406
|
knowledgeGraphDebug: async (_, args) => {
|
|
335
407
|
const namespace = GraphIndexerProcessor.getNamespace(args.driveId);
|
|
336
408
|
try {
|