yangdx
commited on
Commit
·
3ab8571
1
Parent(s):
f56f204
Fix duplicate api requuests for graph fetching
Browse files- Optimize graph data fetching conditions
- Add isFetching state to prevent duplicate requests
- Improve label selection handling
- lightrag/api/webui/assets/{index-B9TRs-Wk.js → index-fJflQM9b.js} +0 -0
- lightrag/api/webui/index.html +0 -0
- lightrag_webui/src/components/graph/GraphControl.tsx +8 -7
- lightrag_webui/src/components/graph/GraphLabels.tsx +5 -2
- lightrag_webui/src/hooks/useLightragGraph.tsx +37 -13
- lightrag_webui/src/stores/graph.ts +4 -0
lightrag/api/webui/assets/{index-B9TRs-Wk.js → index-fJflQM9b.js}
RENAMED
Binary files a/lightrag/api/webui/assets/index-B9TRs-Wk.js and b/lightrag/api/webui/assets/index-fJflQM9b.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
@@ -1,10 +1,11 @@
|
|
1 |
import { useLoadGraph, useRegisterEvents, useSetSettings, useSigma } from '@react-sigma/core'
|
|
|
2 |
// import { useLayoutCircular } from '@react-sigma/layout-circular'
|
3 |
import { useLayoutForceAtlas2 } from '@react-sigma/layout-forceatlas2'
|
4 |
import { useEffect } from 'react'
|
5 |
|
6 |
// import useRandomGraph, { EdgeType, NodeType } from '@/hooks/useRandomGraph'
|
7 |
-
import
|
8 |
import useTheme from '@/hooks/useTheme'
|
9 |
import * as Constants from '@/lib/constants'
|
10 |
|
@@ -21,7 +22,6 @@ const isButtonPressed = (ev: MouseEvent | TouchEvent) => {
|
|
21 |
}
|
22 |
|
23 |
const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) => {
|
24 |
-
const { lightrageGraph } = useLightragGraph()
|
25 |
const sigma = useSigma<NodeType, EdgeType>()
|
26 |
const registerEvents = useRegisterEvents<NodeType, EdgeType>()
|
27 |
const setSettings = useSetSettings<NodeType, EdgeType>()
|
@@ -38,17 +38,18 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
|
|
38 |
const focusedNode = useGraphStore.use.focusedNode()
|
39 |
const selectedEdge = useGraphStore.use.selectedEdge()
|
40 |
const focusedEdge = useGraphStore.use.focusedEdge()
|
|
|
41 |
|
42 |
/**
|
43 |
* When component mount or maxIterations changes
|
44 |
* => load the graph and apply layout
|
45 |
*/
|
46 |
useEffect(() => {
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
}, [assignLayout, loadGraph,
|
52 |
|
53 |
/**
|
54 |
* When component mount
|
|
|
1 |
import { useLoadGraph, useRegisterEvents, useSetSettings, useSigma } from '@react-sigma/core'
|
2 |
+
import Graph from 'graphology'
|
3 |
// import { useLayoutCircular } from '@react-sigma/layout-circular'
|
4 |
import { useLayoutForceAtlas2 } from '@react-sigma/layout-forceatlas2'
|
5 |
import { useEffect } from 'react'
|
6 |
|
7 |
// import useRandomGraph, { EdgeType, NodeType } from '@/hooks/useRandomGraph'
|
8 |
+
import { EdgeType, NodeType } from '@/hooks/useLightragGraph'
|
9 |
import useTheme from '@/hooks/useTheme'
|
10 |
import * as Constants from '@/lib/constants'
|
11 |
|
|
|
22 |
}
|
23 |
|
24 |
const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) => {
|
|
|
25 |
const sigma = useSigma<NodeType, EdgeType>()
|
26 |
const registerEvents = useRegisterEvents<NodeType, EdgeType>()
|
27 |
const setSettings = useSetSettings<NodeType, EdgeType>()
|
|
|
38 |
const focusedNode = useGraphStore.use.focusedNode()
|
39 |
const selectedEdge = useGraphStore.use.selectedEdge()
|
40 |
const focusedEdge = useGraphStore.use.focusedEdge()
|
41 |
+
const sigmaGraph = useGraphStore.use.sigmaGraph()
|
42 |
|
43 |
/**
|
44 |
* When component mount or maxIterations changes
|
45 |
* => load the graph and apply layout
|
46 |
*/
|
47 |
useEffect(() => {
|
48 |
+
if (sigmaGraph) {
|
49 |
+
loadGraph(sigmaGraph as unknown as Graph<NodeType, EdgeType>)
|
50 |
+
assignLayout()
|
51 |
+
}
|
52 |
+
}, [assignLayout, loadGraph, sigmaGraph, maxIterations])
|
53 |
|
54 |
/**
|
55 |
* When component mount
|
lightrag_webui/src/components/graph/GraphLabels.tsx
CHANGED
@@ -57,8 +57,11 @@ const GraphLabels = () => {
|
|
57 |
|
58 |
const currentLabel = useSettingsStore.getState().queryLabel
|
59 |
|
60 |
-
|
61 |
-
|
|
|
|
|
|
|
62 |
useSettingsStore.getState().setQueryLabel('*')
|
63 |
} else {
|
64 |
useSettingsStore.getState().setQueryLabel(newLabel)
|
|
|
57 |
|
58 |
const currentLabel = useSettingsStore.getState().queryLabel
|
59 |
|
60 |
+
if (newLabel === '*' && currentLabel === '*') {
|
61 |
+
// When reselecting '*', just set it again to trigger a new fetch
|
62 |
+
useSettingsStore.getState().setQueryLabel('*')
|
63 |
+
} else if (newLabel === currentLabel && newLabel !== '*') {
|
64 |
+
// When selecting the same label (except '*'), switch to '*'
|
65 |
useSettingsStore.getState().setQueryLabel('*')
|
66 |
} else {
|
67 |
useSettingsStore.getState().setQueryLabel(newLabel)
|
lightrag_webui/src/hooks/useLightragGraph.tsx
CHANGED
@@ -162,13 +162,32 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
|
|
162 |
}
|
163 |
|
164 |
const useLightrangeGraph = () => {
|
165 |
-
// Use useRef to maintain lastQueryLabel state between renders
|
166 |
-
const lastQueryLabelRef = useRef({ label: '', maxQueryDepth: 0, minDegree: 0 })
|
167 |
const queryLabel = useSettingsStore.use.queryLabel()
|
168 |
const rawGraph = useGraphStore.use.rawGraph()
|
169 |
const sigmaGraph = useGraphStore.use.sigmaGraph()
|
170 |
const maxQueryDepth = useSettingsStore.use.graphQueryMaxDepth()
|
171 |
const minDegree = useSettingsStore.use.graphMinDegree()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
const getNode = useCallback(
|
174 |
(nodeId: string) => {
|
@@ -186,17 +205,12 @@ const useLightrangeGraph = () => {
|
|
186 |
|
187 |
useEffect(() => {
|
188 |
if (queryLabel) {
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
label: queryLabel,
|
196 |
-
maxQueryDepth,
|
197 |
-
minDegree
|
198 |
-
}
|
199 |
-
|
200 |
fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => {
|
201 |
const state = useGraphStore.getState()
|
202 |
const newSigmaGraph = createSigmaGraph(data)
|
@@ -227,6 +241,16 @@ const useLightrangeGraph = () => {
|
|
227 |
// Ensure * is there eventhough there is no graph data
|
228 |
state.setGraphLabels(['*']);
|
229 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
})
|
231 |
}
|
232 |
} else {
|
|
|
162 |
}
|
163 |
|
164 |
const useLightrangeGraph = () => {
|
|
|
|
|
165 |
const queryLabel = useSettingsStore.use.queryLabel()
|
166 |
const rawGraph = useGraphStore.use.rawGraph()
|
167 |
const sigmaGraph = useGraphStore.use.sigmaGraph()
|
168 |
const maxQueryDepth = useSettingsStore.use.graphQueryMaxDepth()
|
169 |
const minDegree = useSettingsStore.use.graphMinDegree()
|
170 |
+
const isFetching = useGraphStore.use.isFetching()
|
171 |
+
|
172 |
+
// Use ref to track fetch status
|
173 |
+
const fetchStatusRef = useRef<Record<string, boolean>>({});
|
174 |
+
|
175 |
+
// Track previous parameters to detect actual changes
|
176 |
+
const prevParamsRef = useRef({ queryLabel, maxQueryDepth, minDegree });
|
177 |
+
|
178 |
+
// Reset fetch status only when parameters actually change
|
179 |
+
useEffect(() => {
|
180 |
+
const prevParams = prevParamsRef.current;
|
181 |
+
if (prevParams.queryLabel !== queryLabel ||
|
182 |
+
prevParams.maxQueryDepth !== maxQueryDepth ||
|
183 |
+
prevParams.minDegree !== minDegree) {
|
184 |
+
useGraphStore.getState().setIsFetching(false);
|
185 |
+
// Reset fetch status for new parameters
|
186 |
+
fetchStatusRef.current = {};
|
187 |
+
// Update previous parameters
|
188 |
+
prevParamsRef.current = { queryLabel, maxQueryDepth, minDegree };
|
189 |
+
}
|
190 |
+
}, [queryLabel, maxQueryDepth, minDegree])
|
191 |
|
192 |
const getNode = useCallback(
|
193 |
(nodeId: string) => {
|
|
|
205 |
|
206 |
useEffect(() => {
|
207 |
if (queryLabel) {
|
208 |
+
const fetchKey = `${queryLabel}-${maxQueryDepth}-${minDegree}`;
|
209 |
+
|
210 |
+
// Only fetch if we haven't fetched this combination in the current component lifecycle
|
211 |
+
if (!isFetching && !fetchStatusRef.current[fetchKey]) {
|
212 |
+
useGraphStore.getState().setIsFetching(true);
|
213 |
+
fetchStatusRef.current[fetchKey] = true;
|
|
|
|
|
|
|
|
|
|
|
214 |
fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => {
|
215 |
const state = useGraphStore.getState()
|
216 |
const newSigmaGraph = createSigmaGraph(data)
|
|
|
241 |
// Ensure * is there eventhough there is no graph data
|
242 |
state.setGraphLabels(['*']);
|
243 |
}
|
244 |
+
if (!data) {
|
245 |
+
// If data is invalid, remove the fetch flag to allow retry
|
246 |
+
delete fetchStatusRef.current[fetchKey];
|
247 |
+
}
|
248 |
+
// Reset fetching state after all updates are complete
|
249 |
+
state.setIsFetching(false);
|
250 |
+
}).catch(() => {
|
251 |
+
// Reset fetching state and remove flag in case of error
|
252 |
+
useGraphStore.getState().setIsFetching(false);
|
253 |
+
delete fetchStatusRef.current[fetchKey];
|
254 |
})
|
255 |
}
|
256 |
} else {
|
lightrag_webui/src/stores/graph.ts
CHANGED
@@ -68,6 +68,7 @@ interface GraphState {
|
|
68 |
graphLabels: string[]
|
69 |
|
70 |
moveToSelectedNode: boolean
|
|
|
71 |
|
72 |
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => void
|
73 |
setFocusedNode: (nodeId: string | null) => void
|
@@ -81,6 +82,7 @@ interface GraphState {
|
|
81 |
setRawGraph: (rawGraph: RawGraph | null) => void
|
82 |
setSigmaGraph: (sigmaGraph: DirectedGraph | null) => void
|
83 |
setGraphLabels: (labels: string[]) => void
|
|
|
84 |
}
|
85 |
|
86 |
const useGraphStoreBase = create<GraphState>()((set) => ({
|
@@ -90,11 +92,13 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
|
|
90 |
focusedEdge: null,
|
91 |
|
92 |
moveToSelectedNode: false,
|
|
|
93 |
|
94 |
rawGraph: null,
|
95 |
sigmaGraph: null,
|
96 |
graphLabels: ['*'],
|
97 |
|
|
|
98 |
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
|
99 |
set({ selectedNode: nodeId, moveToSelectedNode }),
|
100 |
setFocusedNode: (nodeId: string | null) => set({ focusedNode: nodeId }),
|
|
|
68 |
graphLabels: string[]
|
69 |
|
70 |
moveToSelectedNode: boolean
|
71 |
+
isFetching: boolean
|
72 |
|
73 |
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => void
|
74 |
setFocusedNode: (nodeId: string | null) => void
|
|
|
82 |
setRawGraph: (rawGraph: RawGraph | null) => void
|
83 |
setSigmaGraph: (sigmaGraph: DirectedGraph | null) => void
|
84 |
setGraphLabels: (labels: string[]) => void
|
85 |
+
setIsFetching: (isFetching: boolean) => void
|
86 |
}
|
87 |
|
88 |
const useGraphStoreBase = create<GraphState>()((set) => ({
|
|
|
92 |
focusedEdge: null,
|
93 |
|
94 |
moveToSelectedNode: false,
|
95 |
+
isFetching: false,
|
96 |
|
97 |
rawGraph: null,
|
98 |
sigmaGraph: null,
|
99 |
graphLabels: ['*'],
|
100 |
|
101 |
+
setIsFetching: (isFetching: boolean) => set({ isFetching }),
|
102 |
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
|
103 |
set({ selectedNode: nodeId, moveToSelectedNode }),
|
104 |
setFocusedNode: (nodeId: string | null) => set({ focusedNode: nodeId }),
|