mirror of
				https://github.com/HKUDS/LightRAG.git
				synced 2025-11-04 03:39:35 +00:00 
			
		
		
		
	Merge pull request #1126 from danielaskdd/improve-graph-reload
Improve graph reload
This commit is contained in:
		
						commit
						2b06cee7fe
					
				
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								lightrag/api/webui/assets/index-BMVv3U43.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								lightrag/api/webui/assets/index-BMVv3U43.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -8,8 +8,8 @@
 | 
			
		||||
    <link rel="icon" type="image/svg+xml" href="logo.png" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Lightrag</title>
 | 
			
		||||
    <script type="module" crossorigin src="/webui/assets/index-C56SCRGK.js"></script>
 | 
			
		||||
    <link rel="stylesheet" crossorigin href="/webui/assets/index-BE_O4IWQ.css">
 | 
			
		||||
    <script type="module" crossorigin src="/webui/assets/index-BxHTAgSP.js"></script>
 | 
			
		||||
    <link rel="stylesheet" crossorigin href="/webui/assets/index-BMVv3U43.css">
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="root"></div>
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ const GraphLabels = () => {
 | 
			
		||||
  // Track if a fetch is in progress to prevent multiple simultaneous fetches
 | 
			
		||||
  const fetchInProgressRef = useRef(false)
 | 
			
		||||
 | 
			
		||||
  // Fetch labels once on component mount, using global flag to prevent duplicates
 | 
			
		||||
  // Fetch labels and trigger initial data load
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // Check if we've already attempted to fetch labels in this session
 | 
			
		||||
    const labelsFetchAttempted = useGraphStore.getState().labelsFetchAttempted
 | 
			
		||||
@ -43,6 +43,14 @@ const GraphLabels = () => {
 | 
			
		||||
    }
 | 
			
		||||
  }, []) // Empty dependency array ensures this only runs once on mount
 | 
			
		||||
 | 
			
		||||
  // Trigger data load when labels are loaded
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (labelsLoadedRef.current) {
 | 
			
		||||
      // Reset the fetch attempted flag to force a new data fetch
 | 
			
		||||
      useGraphStore.getState().setGraphDataFetchAttempted(false)
 | 
			
		||||
    }
 | 
			
		||||
  }, [label])
 | 
			
		||||
 | 
			
		||||
  const getSearchEngine = useCallback(() => {
 | 
			
		||||
    // Create search engine
 | 
			
		||||
    const searchEngine = new MiniSearch({
 | 
			
		||||
@ -85,13 +93,25 @@ const GraphLabels = () => {
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  const handleRefresh = useCallback(() => {
 | 
			
		||||
    const currentLabel = useSettingsStore.getState().queryLabel
 | 
			
		||||
    // Reset labels fetch status to allow fetching labels again
 | 
			
		||||
    useGraphStore.getState().setLabelsFetchAttempted(false)
 | 
			
		||||
 | 
			
		||||
    // Reset graph data fetch status directly, not depending on allDatabaseLabels changes
 | 
			
		||||
    useGraphStore.getState().setGraphDataFetchAttempted(false)
 | 
			
		||||
 | 
			
		||||
    useGraphStore.getState().reset()
 | 
			
		||||
 | 
			
		||||
    useSettingsStore.getState().setQueryLabel(currentLabel)
 | 
			
		||||
    // Fetch all labels again
 | 
			
		||||
    useGraphStore.getState().fetchAllDatabaseLabels()
 | 
			
		||||
      .then(() => {
 | 
			
		||||
        // Trigger a graph data reload by changing the query label back and forth
 | 
			
		||||
        const currentLabel = useSettingsStore.getState().queryLabel
 | 
			
		||||
        useSettingsStore.getState().setQueryLabel('')
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
          useSettingsStore.getState().setQueryLabel(currentLabel)
 | 
			
		||||
        }, 0)
 | 
			
		||||
      })
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
        console.error('Failed to refresh labels:', error)
 | 
			
		||||
      })
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -128,22 +148,13 @@ const GraphLabels = () => {
 | 
			
		||||
            newLabel = '*'
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // Reset the fetch attempted flag to force a new data fetch
 | 
			
		||||
          useGraphStore.getState().setGraphDataFetchAttempted(false)
 | 
			
		||||
 | 
			
		||||
          // Clear current graph data to ensure complete reload when label changes
 | 
			
		||||
          if (newLabel !== currentLabel) {
 | 
			
		||||
            const graphStore = useGraphStore.getState();
 | 
			
		||||
            // Reset the all graph objects and status
 | 
			
		||||
            graphStore.reset();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // Handle reselecting the same label
 | 
			
		||||
          if (newLabel === currentLabel && newLabel !== '*') {
 | 
			
		||||
            // reselect the same itme means qery all
 | 
			
		||||
            useSettingsStore.getState().setQueryLabel('*')
 | 
			
		||||
          } else {
 | 
			
		||||
            useSettingsStore.getState().setQueryLabel(newLabel)
 | 
			
		||||
            newLabel = '*'
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // Update the label, which will trigger the useEffect to handle data loading
 | 
			
		||||
          useSettingsStore.getState().setQueryLabel(newLabel)
 | 
			
		||||
        }}
 | 
			
		||||
        clearable={false}  // Prevent clearing value on reselect
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { useState, useCallback, useEffect } from 'react'
 | 
			
		||||
import { useState, useCallback} from 'react'
 | 
			
		||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
 | 
			
		||||
import Checkbox from '@/components/ui/Checkbox'
 | 
			
		||||
import Button from '@/components/ui/Button'
 | 
			
		||||
@ -7,7 +7,6 @@ import Input from '@/components/ui/Input'
 | 
			
		||||
 | 
			
		||||
import { controlButtonVariant } from '@/lib/constants'
 | 
			
		||||
import { useSettingsStore } from '@/stores/settings'
 | 
			
		||||
import { useBackendState } from '@/stores/state'
 | 
			
		||||
 | 
			
		||||
import { SettingsIcon } from 'lucide-react'
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
@ -113,7 +112,6 @@ const LabeledNumberInput = ({
 | 
			
		||||
 */
 | 
			
		||||
export default function Settings() {
 | 
			
		||||
  const [opened, setOpened] = useState<boolean>(false)
 | 
			
		||||
  const [tempApiKey, setTempApiKey] = useState<string>('')
 | 
			
		||||
 | 
			
		||||
  const showPropertyPanel = useSettingsStore.use.showPropertyPanel()
 | 
			
		||||
  const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
 | 
			
		||||
@ -127,11 +125,6 @@ export default function Settings() {
 | 
			
		||||
  const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations()
 | 
			
		||||
 | 
			
		||||
  const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
 | 
			
		||||
  const apiKey = useSettingsStore.use.apiKey()
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setTempApiKey(apiKey || '')
 | 
			
		||||
  }, [apiKey, opened])
 | 
			
		||||
 | 
			
		||||
  const setEnableNodeDrag = useCallback(
 | 
			
		||||
    () => useSettingsStore.setState((pre) => ({ enableNodeDrag: !pre.enableNodeDrag })),
 | 
			
		||||
@ -180,11 +173,22 @@ export default function Settings() {
 | 
			
		||||
  const setGraphQueryMaxDepth = useCallback((depth: number) => {
 | 
			
		||||
    if (depth < 1) return
 | 
			
		||||
    useSettingsStore.setState({ graphQueryMaxDepth: depth })
 | 
			
		||||
    const currentLabel = useSettingsStore.getState().queryLabel
 | 
			
		||||
    useSettingsStore.getState().setQueryLabel('')
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      useSettingsStore.getState().setQueryLabel(currentLabel)
 | 
			
		||||
    }, 300)
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const setGraphMinDegree = useCallback((degree: number) => {
 | 
			
		||||
    if (degree < 0) return
 | 
			
		||||
    useSettingsStore.setState({ graphMinDegree: degree })
 | 
			
		||||
    const currentLabel = useSettingsStore.getState().queryLabel
 | 
			
		||||
    useSettingsStore.getState().setQueryLabel('')
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      useSettingsStore.getState().setQueryLabel(currentLabel)
 | 
			
		||||
    }, 300)
 | 
			
		||||
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const setGraphLayoutMaxIterations = useCallback((iterations: number) => {
 | 
			
		||||
@ -192,26 +196,21 @@ export default function Settings() {
 | 
			
		||||
    useSettingsStore.setState({ graphLayoutMaxIterations: iterations })
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const setApiKey = useCallback(async () => {
 | 
			
		||||
    useSettingsStore.setState({ apiKey: tempApiKey || null })
 | 
			
		||||
    await useBackendState.getState().check()
 | 
			
		||||
    setOpened(false)
 | 
			
		||||
  }, [tempApiKey])
 | 
			
		||||
 | 
			
		||||
  const handleTempApiKeyChange = useCallback(
 | 
			
		||||
    (e: React.ChangeEvent<HTMLInputElement>) => {
 | 
			
		||||
      setTempApiKey(e.target.value)
 | 
			
		||||
    },
 | 
			
		||||
    [setTempApiKey]
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const saveSettings = () => setOpened(false);
 | 
			
		||||
  const toggleSettings = () => setOpened(!opened);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Popover open={opened} onOpenChange={setOpened}>
 | 
			
		||||
      <Popover open={opened}>
 | 
			
		||||
        <PopoverTrigger asChild>
 | 
			
		||||
          <Button variant={controlButtonVariant} tooltip={t('graphPanel.sideBar.settings.settings')} size="icon">
 | 
			
		||||
          <Button
 | 
			
		||||
            variant={controlButtonVariant}
 | 
			
		||||
            tooltip={t('graphPanel.sideBar.settings.settings')}
 | 
			
		||||
            size="icon"
 | 
			
		||||
            onClick={toggleSettings}
 | 
			
		||||
          >
 | 
			
		||||
            <SettingsIcon />
 | 
			
		||||
          </Button>
 | 
			
		||||
        </PopoverTrigger>
 | 
			
		||||
@ -293,30 +292,15 @@ export default function Settings() {
 | 
			
		||||
              onEditFinished={setGraphLayoutMaxIterations}
 | 
			
		||||
            />
 | 
			
		||||
            <Separator />
 | 
			
		||||
            <Button
 | 
			
		||||
              onClick={saveSettings}
 | 
			
		||||
              variant="outline"
 | 
			
		||||
              size="sm"
 | 
			
		||||
              className="ml-auto px-4"
 | 
			
		||||
            >
 | 
			
		||||
              {t('graphPanel.sideBar.settings.save')}
 | 
			
		||||
            </Button>
 | 
			
		||||
 | 
			
		||||
            <div className="flex flex-col gap-2">
 | 
			
		||||
              <label className="text-sm font-medium">{t('graphPanel.sideBar.settings.apiKey')}</label>
 | 
			
		||||
              <form className="flex h-6 gap-2" onSubmit={(e) => e.preventDefault()}>
 | 
			
		||||
                <div className="w-0 flex-1">
 | 
			
		||||
                  <Input
 | 
			
		||||
                    type="password"
 | 
			
		||||
                    value={tempApiKey}
 | 
			
		||||
                    onChange={handleTempApiKeyChange}
 | 
			
		||||
                    placeholder={t('graphPanel.sideBar.settings.enterYourAPIkey')}
 | 
			
		||||
                    className="max-h-full w-full min-w-0"
 | 
			
		||||
                    autoComplete="off"
 | 
			
		||||
                  />
 | 
			
		||||
                </div>
 | 
			
		||||
                <Button
 | 
			
		||||
                  onClick={setApiKey}
 | 
			
		||||
                  variant="outline"
 | 
			
		||||
                  size="sm"
 | 
			
		||||
                  className="max-h-full shrink-0"
 | 
			
		||||
                >
 | 
			
		||||
                  {t('graphPanel.sideBar.settings.save')}
 | 
			
		||||
                </Button>
 | 
			
		||||
              </form>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </PopoverContent>
 | 
			
		||||
      </Popover>
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ const SettingsDisplay = () => {
 | 
			
		||||
  const graphMinDegree = useSettingsStore.use.graphMinDegree()
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="absolute bottom-2 left-[calc(2rem+2.5rem)] flex items-center gap-2 text-xs text-gray-400">
 | 
			
		||||
    <div className="absolute bottom-4 left-[calc(1rem+2.5rem)] flex items-center gap-2 text-xs text-gray-400">
 | 
			
		||||
      <div>{t('graphPanel.sideBar.settings.depth')}: {graphQueryMaxDepth}</div>
 | 
			
		||||
      <div>{t('graphPanel.sideBar.settings.degree')}: {graphMinDegree}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -189,19 +189,10 @@ const useLightrangeGraph = () => {
 | 
			
		||||
  const nodeToExpand = useGraphStore.use.nodeToExpand()
 | 
			
		||||
  const nodeToPrune = useGraphStore.use.nodeToPrune()
 | 
			
		||||
 | 
			
		||||
  // Track previous parameters to detect actual changes
 | 
			
		||||
  const prevParamsRef = useRef({ queryLabel, maxQueryDepth, minDegree })
 | 
			
		||||
 | 
			
		||||
  // Use ref to track if data has been loaded and initial load
 | 
			
		||||
  const dataLoadedRef = useRef(false)
 | 
			
		||||
  const initialLoadRef = useRef(false)
 | 
			
		||||
 | 
			
		||||
  // Check if parameters have changed
 | 
			
		||||
  const paramsChanged =
 | 
			
		||||
    prevParamsRef.current.queryLabel !== queryLabel ||
 | 
			
		||||
    prevParamsRef.current.maxQueryDepth !== maxQueryDepth ||
 | 
			
		||||
    prevParamsRef.current.minDegree !== minDegree
 | 
			
		||||
 | 
			
		||||
  const getNode = useCallback(
 | 
			
		||||
    (nodeId: string) => {
 | 
			
		||||
      return rawGraph?.getNode(nodeId) || null
 | 
			
		||||
@ -219,30 +210,27 @@ const useLightrangeGraph = () => {
 | 
			
		||||
  // Track if a fetch is in progress to prevent multiple simultaneous fetches
 | 
			
		||||
  const fetchInProgressRef = useRef(false)
 | 
			
		||||
 | 
			
		||||
  // Data fetching logic - simplified but preserving TAB visibility check
 | 
			
		||||
  // Reset graph when query label is cleared
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // Skip if fetch is already in progress
 | 
			
		||||
    if (fetchInProgressRef.current) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If there's no query label, reset the graph
 | 
			
		||||
    if (!queryLabel) {
 | 
			
		||||
      if (rawGraph !== null || sigmaGraph !== null) {
 | 
			
		||||
        const state = useGraphStore.getState()
 | 
			
		||||
        state.reset()
 | 
			
		||||
        state.setGraphDataFetchAttempted(false)
 | 
			
		||||
        state.setLabelsFetchAttempted(false)
 | 
			
		||||
      }
 | 
			
		||||
    if (!queryLabel && (rawGraph !== null || sigmaGraph !== null)) {
 | 
			
		||||
      const state = useGraphStore.getState()
 | 
			
		||||
      state.reset()
 | 
			
		||||
      state.setGraphDataFetchAttempted(false)
 | 
			
		||||
      state.setLabelsFetchAttempted(false)
 | 
			
		||||
      dataLoadedRef.current = false
 | 
			
		||||
      initialLoadRef.current = false
 | 
			
		||||
    }
 | 
			
		||||
  }, [queryLabel, rawGraph, sigmaGraph])
 | 
			
		||||
 | 
			
		||||
  // Data fetching logic
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // Skip if fetch is already in progress or no query label
 | 
			
		||||
    if (fetchInProgressRef.current || !queryLabel) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check if parameters have changed
 | 
			
		||||
    if (!isFetching && !fetchInProgressRef.current &&
 | 
			
		||||
        (paramsChanged || !useGraphStore.getState().graphDataFetchAttempted)) {
 | 
			
		||||
 | 
			
		||||
    // Only fetch data when graphDataFetchAttempted is false (avoids re-fetching on vite dev mode)
 | 
			
		||||
    if (!isFetching && !useGraphStore.getState().graphDataFetchAttempted) {
 | 
			
		||||
      // Set flags
 | 
			
		||||
      fetchInProgressRef.current = true
 | 
			
		||||
      useGraphStore.getState().setGraphDataFetchAttempted(true)
 | 
			
		||||
@ -258,9 +246,6 @@ const useLightrangeGraph = () => {
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Update parameter reference
 | 
			
		||||
      prevParamsRef.current = { queryLabel, maxQueryDepth, minDegree }
 | 
			
		||||
 | 
			
		||||
      console.log('Fetching graph data...')
 | 
			
		||||
 | 
			
		||||
      // Use a local copy of the parameters
 | 
			
		||||
@ -283,8 +268,6 @@ const useLightrangeGraph = () => {
 | 
			
		||||
        state.setSigmaGraph(newSigmaGraph)
 | 
			
		||||
        state.setRawGraph(data)
 | 
			
		||||
 | 
			
		||||
        // No longer need to extract labels from graph data
 | 
			
		||||
 | 
			
		||||
        // Update flags
 | 
			
		||||
        dataLoadedRef.current = true
 | 
			
		||||
        initialLoadRef.current = true
 | 
			
		||||
@ -305,7 +288,7 @@ const useLightrangeGraph = () => {
 | 
			
		||||
        state.setGraphDataFetchAttempted(false)
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }, [queryLabel, maxQueryDepth, minDegree, isFetching, paramsChanged, rawGraph, sigmaGraph])
 | 
			
		||||
  }, [queryLabel, maxQueryDepth, minDegree, isFetching])
 | 
			
		||||
 | 
			
		||||
  // Handle node expansion
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
 | 
			
		||||
@ -99,7 +99,7 @@
 | 
			
		||||
        "hideUnselectedEdges": "隐藏未选中的边",
 | 
			
		||||
        "edgeEvents": "边事件",
 | 
			
		||||
        "maxQueryDepth": "最大查询深度",
 | 
			
		||||
        "minDegree": "最小度数",
 | 
			
		||||
        "minDegree": "最小邻边数",
 | 
			
		||||
        "maxLayoutIterations": "最大布局迭代次数",
 | 
			
		||||
        "depth": "深度",
 | 
			
		||||
        "degree": "邻边",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user