Merge pull request #1273 from danielaskdd/merge-edge-thickness
Browse files- README.md +1 -1
- lightrag/api/__init__.py +1 -1
- lightrag/api/webui/assets/{index-D4h_QQ3K.js → index-CaPD9lR_.js} +0 -0
- lightrag/api/webui/index.html +0 -0
- lightrag_webui/src/components/graph/GraphControl.tsx +47 -0
- lightrag_webui/src/components/graph/Settings.tsx +49 -0
- lightrag_webui/src/hooks/useLightragGraph.tsx +43 -2
- lightrag_webui/src/locales/ar.json +1 -0
- lightrag_webui/src/locales/en.json +1 -0
- lightrag_webui/src/locales/fr.json +1 -0
- lightrag_webui/src/locales/zh.json +1 -0
- lightrag_webui/src/stores/settings.ts +18 -1
README.md
CHANGED
@@ -655,7 +655,7 @@ The `apipeline_enqueue_documents` and `apipeline_process_enqueue_documents` func
|
|
655 |
|
656 |
This is useful for scenarios where you want to process documents in the background while still allowing the main thread to continue executing.
|
657 |
|
658 |
-
And using a routine to process
|
659 |
|
660 |
```python
|
661 |
rag = LightRAG(..)
|
|
|
655 |
|
656 |
This is useful for scenarios where you want to process documents in the background while still allowing the main thread to continue executing.
|
657 |
|
658 |
+
And using a routine to process new documents.
|
659 |
|
660 |
```python
|
661 |
rag = LightRAG(..)
|
lightrag/api/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1 |
-
__api_version__ = "
|
|
|
1 |
+
__api_version__ = "0135"
|
lightrag/api/webui/assets/{index-D4h_QQ3K.js → index-CaPD9lR_.js}
RENAMED
Binary files a/lightrag/api/webui/assets/index-D4h_QQ3K.js and b/lightrag/api/webui/assets/index-CaPD9lR_.js differ
|
|
lightrag/api/webui/index.html
CHANGED
Binary files a/lightrag/api/webui/index.html and b/lightrag/api/webui/index.html differ
|
|
lightrag_webui/src/components/graph/GraphControl.tsx
CHANGED
@@ -36,6 +36,8 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
|
|
36 |
const enableEdgeEvents = useSettingsStore.use.enableEdgeEvents()
|
37 |
const renderEdgeLabels = useSettingsStore.use.showEdgeLabel()
|
38 |
const renderLabels = useSettingsStore.use.showNodeLabel()
|
|
|
|
|
39 |
const selectedNode = useGraphStore.use.selectedNode()
|
40 |
const focusedNode = useGraphStore.use.focusedNode()
|
41 |
const selectedEdge = useGraphStore.use.selectedEdge()
|
@@ -136,6 +138,51 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
|
|
136 |
registerEvents(events)
|
137 |
}, [registerEvents, enableEdgeEvents])
|
138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
/**
|
140 |
* When component mount or hovered node change
|
141 |
* => Setting the sigma reducers
|
|
|
36 |
const enableEdgeEvents = useSettingsStore.use.enableEdgeEvents()
|
37 |
const renderEdgeLabels = useSettingsStore.use.showEdgeLabel()
|
38 |
const renderLabels = useSettingsStore.use.showNodeLabel()
|
39 |
+
const minEdgeSize = useSettingsStore.use.minEdgeSize()
|
40 |
+
const maxEdgeSize = useSettingsStore.use.maxEdgeSize()
|
41 |
const selectedNode = useGraphStore.use.selectedNode()
|
42 |
const focusedNode = useGraphStore.use.focusedNode()
|
43 |
const selectedEdge = useGraphStore.use.selectedEdge()
|
|
|
138 |
registerEvents(events)
|
139 |
}, [registerEvents, enableEdgeEvents])
|
140 |
|
141 |
+
/**
|
142 |
+
* When edge size settings change, recalculate edge sizes and refresh the sigma instance
|
143 |
+
* to ensure changes take effect immediately
|
144 |
+
*/
|
145 |
+
useEffect(() => {
|
146 |
+
if (sigma && sigmaGraph) {
|
147 |
+
// Get the graph from sigma
|
148 |
+
const graph = sigma.getGraph()
|
149 |
+
|
150 |
+
// Find min and max weight values
|
151 |
+
let minWeight = Number.MAX_SAFE_INTEGER
|
152 |
+
let maxWeight = 0
|
153 |
+
|
154 |
+
graph.forEachEdge(edge => {
|
155 |
+
// Get original weight (before scaling)
|
156 |
+
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
|
157 |
+
if (typeof weight === 'number') {
|
158 |
+
minWeight = Math.min(minWeight, weight)
|
159 |
+
maxWeight = Math.max(maxWeight, weight)
|
160 |
+
}
|
161 |
+
})
|
162 |
+
|
163 |
+
// Scale edge sizes based on weight range and current min/max edge size settings
|
164 |
+
const weightRange = maxWeight - minWeight
|
165 |
+
if (weightRange > 0) {
|
166 |
+
const sizeScale = maxEdgeSize - minEdgeSize
|
167 |
+
graph.forEachEdge(edge => {
|
168 |
+
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
|
169 |
+
if (typeof weight === 'number') {
|
170 |
+
const scaledSize = minEdgeSize + sizeScale * Math.pow((weight - minWeight) / weightRange, 0.5)
|
171 |
+
graph.setEdgeAttribute(edge, 'size', scaledSize)
|
172 |
+
}
|
173 |
+
})
|
174 |
+
} else {
|
175 |
+
// If all weights are the same, use default size
|
176 |
+
graph.forEachEdge(edge => {
|
177 |
+
graph.setEdgeAttribute(edge, 'size', minEdgeSize)
|
178 |
+
})
|
179 |
+
}
|
180 |
+
|
181 |
+
// Refresh the sigma instance to apply changes
|
182 |
+
sigma.refresh()
|
183 |
+
}
|
184 |
+
}, [sigma, sigmaGraph, minEdgeSize, maxEdgeSize])
|
185 |
+
|
186 |
/**
|
187 |
* When component mount or hovered node change
|
188 |
* => Setting the sigma reducers
|
lightrag_webui/src/components/graph/Settings.tsx
CHANGED
@@ -144,6 +144,8 @@ export default function Settings() {
|
|
144 |
const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
|
145 |
const enableHideUnselectedEdges = useSettingsStore.use.enableHideUnselectedEdges()
|
146 |
const showEdgeLabel = useSettingsStore.use.showEdgeLabel()
|
|
|
|
|
147 |
const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth()
|
148 |
const graphMaxNodes = useSettingsStore.use.graphMaxNodes()
|
149 |
const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations()
|
@@ -292,6 +294,53 @@ export default function Settings() {
|
|
292 |
label={t('graphPanel.sideBar.settings.edgeEvents')}
|
293 |
/>
|
294 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
295 |
<Separator />
|
296 |
<LabeledNumberInput
|
297 |
label={t('graphPanel.sideBar.settings.maxQueryDepth')}
|
|
|
144 |
const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
|
145 |
const enableHideUnselectedEdges = useSettingsStore.use.enableHideUnselectedEdges()
|
146 |
const showEdgeLabel = useSettingsStore.use.showEdgeLabel()
|
147 |
+
const minEdgeSize = useSettingsStore.use.minEdgeSize()
|
148 |
+
const maxEdgeSize = useSettingsStore.use.maxEdgeSize()
|
149 |
const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth()
|
150 |
const graphMaxNodes = useSettingsStore.use.graphMaxNodes()
|
151 |
const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations()
|
|
|
294 |
label={t('graphPanel.sideBar.settings.edgeEvents')}
|
295 |
/>
|
296 |
|
297 |
+
<div className="flex flex-col gap-2">
|
298 |
+
<label className="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
299 |
+
{t('graphPanel.sideBar.settings.edgeSizeRange')}
|
300 |
+
</label>
|
301 |
+
<div className="flex items-center gap-2">
|
302 |
+
<Input
|
303 |
+
type="number"
|
304 |
+
value={minEdgeSize}
|
305 |
+
onChange={(e) => {
|
306 |
+
const newValue = Number(e.target.value);
|
307 |
+
if (!isNaN(newValue) && newValue >= 1 && newValue <= maxEdgeSize) {
|
308 |
+
useSettingsStore.setState({ minEdgeSize: newValue });
|
309 |
+
}
|
310 |
+
}}
|
311 |
+
className="h-6 w-16 min-w-0 pr-1"
|
312 |
+
min={1}
|
313 |
+
max={Math.min(maxEdgeSize, 10)}
|
314 |
+
/>
|
315 |
+
<span>-</span>
|
316 |
+
<div className="flex items-center gap-1">
|
317 |
+
<Input
|
318 |
+
type="number"
|
319 |
+
value={maxEdgeSize}
|
320 |
+
onChange={(e) => {
|
321 |
+
const newValue = Number(e.target.value);
|
322 |
+
if (!isNaN(newValue) && newValue >= minEdgeSize && newValue >= 1 && newValue <= 10) {
|
323 |
+
useSettingsStore.setState({ maxEdgeSize: newValue });
|
324 |
+
}
|
325 |
+
}}
|
326 |
+
className="h-6 w-16 min-w-0 pr-1"
|
327 |
+
min={minEdgeSize}
|
328 |
+
max={10}
|
329 |
+
/>
|
330 |
+
<Button
|
331 |
+
variant="ghost"
|
332 |
+
size="icon"
|
333 |
+
className="h-6 w-6 flex-shrink-0 hover:bg-muted text-muted-foreground hover:text-foreground"
|
334 |
+
onClick={() => useSettingsStore.setState({ minEdgeSize: 1, maxEdgeSize: 5 })}
|
335 |
+
type="button"
|
336 |
+
title={t('graphPanel.sideBar.settings.resetToDefault')}
|
337 |
+
>
|
338 |
+
<Undo2 className="h-3.5 w-3.5" />
|
339 |
+
</Button>
|
340 |
+
</div>
|
341 |
+
</div>
|
342 |
+
</div>
|
343 |
+
|
344 |
<Separator />
|
345 |
<LabeledNumberInput
|
346 |
label={t('graphPanel.sideBar.settings.maxQueryDepth')}
|
lightrag_webui/src/hooks/useLightragGraph.tsx
CHANGED
@@ -68,7 +68,13 @@ export type NodeType = {
|
|
68 |
color: string
|
69 |
highlighted?: boolean
|
70 |
}
|
71 |
-
export type EdgeType = {
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
const fetchGraph = async (label: string, maxDepth: number, maxNodes: number) => {
|
74 |
let rawData: any = null;
|
@@ -174,6 +180,9 @@ const fetchGraph = async (label: string, maxDepth: number, maxNodes: number) =>
|
|
174 |
|
175 |
// Create a new graph instance with the raw graph data
|
176 |
const createSigmaGraph = (rawGraph: RawGraph | null) => {
|
|
|
|
|
|
|
177 |
// Skip graph creation if no data or empty nodes
|
178 |
if (!rawGraph || !rawGraph.nodes.length) {
|
179 |
console.log('No graph data available, skipping sigma graph creation');
|
@@ -204,8 +213,40 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
|
|
204 |
|
205 |
// Add edges from raw graph data
|
206 |
for (const rawEdge of rawGraph?.edges ?? []) {
|
|
|
|
|
|
|
207 |
rawEdge.dynamicId = graph.addDirectedEdge(rawEdge.source, rawEdge.target, {
|
208 |
-
label: rawEdge.properties?.keywords || undefined
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
})
|
210 |
}
|
211 |
|
|
|
68 |
color: string
|
69 |
highlighted?: boolean
|
70 |
}
|
71 |
+
export type EdgeType = {
|
72 |
+
label: string
|
73 |
+
originalWeight?: number
|
74 |
+
size?: number
|
75 |
+
color?: string
|
76 |
+
hidden?: boolean
|
77 |
+
}
|
78 |
|
79 |
const fetchGraph = async (label: string, maxDepth: number, maxNodes: number) => {
|
80 |
let rawData: any = null;
|
|
|
180 |
|
181 |
// Create a new graph instance with the raw graph data
|
182 |
const createSigmaGraph = (rawGraph: RawGraph | null) => {
|
183 |
+
// Get edge size settings from store
|
184 |
+
const minEdgeSize = useSettingsStore.getState().minEdgeSize
|
185 |
+
const maxEdgeSize = useSettingsStore.getState().maxEdgeSize
|
186 |
// Skip graph creation if no data or empty nodes
|
187 |
if (!rawGraph || !rawGraph.nodes.length) {
|
188 |
console.log('No graph data available, skipping sigma graph creation');
|
|
|
213 |
|
214 |
// Add edges from raw graph data
|
215 |
for (const rawEdge of rawGraph?.edges ?? []) {
|
216 |
+
// Get weight from edge properties or default to 1
|
217 |
+
const weight = rawEdge.properties?.weight !== undefined ? Number(rawEdge.properties.weight) : 1
|
218 |
+
|
219 |
rawEdge.dynamicId = graph.addDirectedEdge(rawEdge.source, rawEdge.target, {
|
220 |
+
label: rawEdge.properties?.keywords || undefined,
|
221 |
+
size: weight, // Set initial size based on weight
|
222 |
+
originalWeight: weight, // Store original weight for recalculation
|
223 |
+
})
|
224 |
+
}
|
225 |
+
|
226 |
+
// Calculate edge size based on weight range, similar to node size calculation
|
227 |
+
let minWeight = Number.MAX_SAFE_INTEGER
|
228 |
+
let maxWeight = 0
|
229 |
+
|
230 |
+
// Find min and max weight values
|
231 |
+
graph.forEachEdge(edge => {
|
232 |
+
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
|
233 |
+
minWeight = Math.min(minWeight, weight)
|
234 |
+
maxWeight = Math.max(maxWeight, weight)
|
235 |
+
})
|
236 |
+
|
237 |
+
// Scale edge sizes based on weight range
|
238 |
+
const weightRange = maxWeight - minWeight
|
239 |
+
if (weightRange > 0) {
|
240 |
+
const sizeScale = maxEdgeSize - minEdgeSize
|
241 |
+
graph.forEachEdge(edge => {
|
242 |
+
const weight = graph.getEdgeAttribute(edge, 'originalWeight') || 1
|
243 |
+
const scaledSize = minEdgeSize + sizeScale * Math.pow((weight - minWeight) / weightRange, 0.5)
|
244 |
+
graph.setEdgeAttribute(edge, 'size', scaledSize)
|
245 |
+
})
|
246 |
+
} else {
|
247 |
+
// If all weights are the same, use default size
|
248 |
+
graph.forEachEdge(edge => {
|
249 |
+
graph.setEdgeAttribute(edge, 'size', minEdgeSize)
|
250 |
})
|
251 |
}
|
252 |
|
lightrag_webui/src/locales/ar.json
CHANGED
@@ -157,6 +157,7 @@
|
|
157 |
"maxNodes": "الحد الأقصى للعقد",
|
158 |
"maxLayoutIterations": "أقصى تكرارات التخطيط",
|
159 |
"resetToDefault": "إعادة التعيين إلى الافتراضي",
|
|
|
160 |
"depth": "D",
|
161 |
"max": "Max",
|
162 |
"degree": "الدرجة",
|
|
|
157 |
"maxNodes": "الحد الأقصى للعقد",
|
158 |
"maxLayoutIterations": "أقصى تكرارات التخطيط",
|
159 |
"resetToDefault": "إعادة التعيين إلى الافتراضي",
|
160 |
+
"edgeSizeRange": "نطاق حجم الحافة",
|
161 |
"depth": "D",
|
162 |
"max": "Max",
|
163 |
"degree": "الدرجة",
|
lightrag_webui/src/locales/en.json
CHANGED
@@ -157,6 +157,7 @@
|
|
157 |
"maxNodes": "Max Nodes",
|
158 |
"maxLayoutIterations": "Max Layout Iterations",
|
159 |
"resetToDefault": "Reset to default",
|
|
|
160 |
"depth": "D",
|
161 |
"max": "Max",
|
162 |
"degree": "Degree",
|
|
|
157 |
"maxNodes": "Max Nodes",
|
158 |
"maxLayoutIterations": "Max Layout Iterations",
|
159 |
"resetToDefault": "Reset to default",
|
160 |
+
"edgeSizeRange": "Edge Size Range",
|
161 |
"depth": "D",
|
162 |
"max": "Max",
|
163 |
"degree": "Degree",
|
lightrag_webui/src/locales/fr.json
CHANGED
@@ -157,6 +157,7 @@
|
|
157 |
"maxNodes": "Nombre maximum de nœuds",
|
158 |
"maxLayoutIterations": "Itérations maximales de mise en page",
|
159 |
"resetToDefault": "Réinitialiser par défaut",
|
|
|
160 |
"depth": "D",
|
161 |
"max": "Max",
|
162 |
"degree": "Degré",
|
|
|
157 |
"maxNodes": "Nombre maximum de nœuds",
|
158 |
"maxLayoutIterations": "Itérations maximales de mise en page",
|
159 |
"resetToDefault": "Réinitialiser par défaut",
|
160 |
+
"edgeSizeRange": "Plage de taille des arêtes",
|
161 |
"depth": "D",
|
162 |
"max": "Max",
|
163 |
"degree": "Degré",
|
lightrag_webui/src/locales/zh.json
CHANGED
@@ -157,6 +157,7 @@
|
|
157 |
"maxNodes": "最大返回节点数",
|
158 |
"maxLayoutIterations": "最大布局迭代次数",
|
159 |
"resetToDefault": "重置为默认值",
|
|
|
160 |
"depth": "深",
|
161 |
"max": "Max",
|
162 |
"degree": "邻边",
|
|
|
157 |
"maxNodes": "最大返回节点数",
|
158 |
"maxLayoutIterations": "最大布局迭代次数",
|
159 |
"resetToDefault": "重置为默认值",
|
160 |
+
"edgeSizeRange": "边粗细范围",
|
161 |
"depth": "深",
|
162 |
"max": "Max",
|
163 |
"degree": "邻边",
|
lightrag_webui/src/stores/settings.ts
CHANGED
@@ -24,6 +24,12 @@ interface SettingsState {
|
|
24 |
enableHideUnselectedEdges: boolean
|
25 |
enableEdgeEvents: boolean
|
26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
graphQueryMaxDepth: number
|
28 |
setGraphQueryMaxDepth: (depth: number) => void
|
29 |
|
@@ -76,6 +82,9 @@ const useSettingsStoreBase = create<SettingsState>()(
|
|
76 |
enableHideUnselectedEdges: true,
|
77 |
enableEdgeEvents: false,
|
78 |
|
|
|
|
|
|
|
79 |
graphQueryMaxDepth: 3,
|
80 |
graphMaxNodes: 1000,
|
81 |
graphLayoutMaxIterations: 15,
|
@@ -132,6 +141,10 @@ const useSettingsStoreBase = create<SettingsState>()(
|
|
132 |
|
133 |
setGraphMaxNodes: (nodes: number) => set({ graphMaxNodes: nodes }),
|
134 |
|
|
|
|
|
|
|
|
|
135 |
setEnableHealthCheck: (enable: boolean) => set({ enableHealthCheck: enable }),
|
136 |
|
137 |
setApiKey: (apiKey: string | null) => set({ apiKey }),
|
@@ -150,7 +163,7 @@ const useSettingsStoreBase = create<SettingsState>()(
|
|
150 |
{
|
151 |
name: 'settings-storage',
|
152 |
storage: createJSONStorage(() => localStorage),
|
153 |
-
version:
|
154 |
migrate: (state: any, version: number) => {
|
155 |
if (version < 2) {
|
156 |
state.showEdgeLabel = false
|
@@ -200,6 +213,10 @@ const useSettingsStoreBase = create<SettingsState>()(
|
|
200 |
delete state.graphMinDegree // 删除废弃参数
|
201 |
state.graphMaxNodes = 1000 // 添加新参数
|
202 |
}
|
|
|
|
|
|
|
|
|
203 |
return state
|
204 |
}
|
205 |
}
|
|
|
24 |
enableHideUnselectedEdges: boolean
|
25 |
enableEdgeEvents: boolean
|
26 |
|
27 |
+
minEdgeSize: number
|
28 |
+
setMinEdgeSize: (size: number) => void
|
29 |
+
|
30 |
+
maxEdgeSize: number
|
31 |
+
setMaxEdgeSize: (size: number) => void
|
32 |
+
|
33 |
graphQueryMaxDepth: number
|
34 |
setGraphQueryMaxDepth: (depth: number) => void
|
35 |
|
|
|
82 |
enableHideUnselectedEdges: true,
|
83 |
enableEdgeEvents: false,
|
84 |
|
85 |
+
minEdgeSize: 1,
|
86 |
+
maxEdgeSize: 1,
|
87 |
+
|
88 |
graphQueryMaxDepth: 3,
|
89 |
graphMaxNodes: 1000,
|
90 |
graphLayoutMaxIterations: 15,
|
|
|
141 |
|
142 |
setGraphMaxNodes: (nodes: number) => set({ graphMaxNodes: nodes }),
|
143 |
|
144 |
+
setMinEdgeSize: (size: number) => set({ minEdgeSize: size }),
|
145 |
+
|
146 |
+
setMaxEdgeSize: (size: number) => set({ maxEdgeSize: size }),
|
147 |
+
|
148 |
setEnableHealthCheck: (enable: boolean) => set({ enableHealthCheck: enable }),
|
149 |
|
150 |
setApiKey: (apiKey: string | null) => set({ apiKey }),
|
|
|
163 |
{
|
164 |
name: 'settings-storage',
|
165 |
storage: createJSONStorage(() => localStorage),
|
166 |
+
version: 11,
|
167 |
migrate: (state: any, version: number) => {
|
168 |
if (version < 2) {
|
169 |
state.showEdgeLabel = false
|
|
|
213 |
delete state.graphMinDegree // 删除废弃参数
|
214 |
state.graphMaxNodes = 1000 // 添加新参数
|
215 |
}
|
216 |
+
if (version < 11) {
|
217 |
+
state.minEdgeSize = 1
|
218 |
+
state.maxEdgeSize = 1
|
219 |
+
}
|
220 |
return state
|
221 |
}
|
222 |
}
|