Merge branch 'main' into i18n-france-arabic
This commit is contained in:
1
lightrag/api/webui/assets/index-BSOt8Nur.css
generated
1
lightrag/api/webui/assets/index-BSOt8Nur.css
generated
File diff suppressed because one or more lines are too long
1
lightrag/api/webui/assets/index-BViPRMGA.css
generated
Normal file
1
lightrag/api/webui/assets/index-BViPRMGA.css
generated
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
lightrag/api/webui/index.html
generated
4
lightrag/api/webui/index.html
generated
@@ -8,8 +8,8 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="logo.png" />
|
<link rel="icon" type="image/svg+xml" href="logo.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Lightrag</title>
|
<title>Lightrag</title>
|
||||||
<script type="module" crossorigin src="/webui/assets/index-4I5HV9Fr.js"></script>
|
<script type="module" crossorigin src="/webui/assets/index-C-CHRwmZ.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/webui/assets/index-BSOt8Nur.css">
|
<link rel="stylesheet" crossorigin href="/webui/assets/index-BViPRMGA.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
@@ -186,7 +186,9 @@ class LightRAG:
|
|||||||
embedding_batch_num: int = field(default=int(os.getenv("EMBEDDING_BATCH_NUM", 32)))
|
embedding_batch_num: int = field(default=int(os.getenv("EMBEDDING_BATCH_NUM", 32)))
|
||||||
"""Batch size for embedding computations."""
|
"""Batch size for embedding computations."""
|
||||||
|
|
||||||
embedding_func_max_async: int = field(default=int(os.getenv("EMBEDDING_FUNC_MAX_ASYNC", 16)))
|
embedding_func_max_async: int = field(
|
||||||
|
default=int(os.getenv("EMBEDDING_FUNC_MAX_ASYNC", 16))
|
||||||
|
)
|
||||||
"""Maximum number of concurrent embedding function calls."""
|
"""Maximum number of concurrent embedding function calls."""
|
||||||
|
|
||||||
embedding_cache_config: dict[str, Any] = field(
|
embedding_cache_config: dict[str, Any] = field(
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { AsyncSelect } from '@/components/ui/AsyncSelect'
|
import { AsyncSelect } from '@/components/ui/AsyncSelect'
|
||||||
import { useSettingsStore } from '@/stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
import { useGraphStore } from '@/stores/graph'
|
import { useGraphStore } from '@/stores/graph'
|
||||||
@@ -12,44 +12,8 @@ const GraphLabels = () => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const label = useSettingsStore.use.queryLabel()
|
const label = useSettingsStore.use.queryLabel()
|
||||||
const allDatabaseLabels = useGraphStore.use.allDatabaseLabels()
|
const allDatabaseLabels = useGraphStore.use.allDatabaseLabels()
|
||||||
const rawGraph = useGraphStore.use.rawGraph()
|
|
||||||
const labelsLoadedRef = useRef(false)
|
|
||||||
|
|
||||||
// Track if a fetch is in progress to prevent multiple simultaneous fetches
|
// Remove initial label fetch effect as it's now handled by fetchGraph based on lastSuccessfulQueryLabel
|
||||||
const fetchInProgressRef = useRef(false)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Only fetch if we haven't attempted in this session and no fetch is in progress
|
|
||||||
if (!labelsFetchAttempted && !fetchInProgressRef.current) {
|
|
||||||
fetchInProgressRef.current = true
|
|
||||||
// Set global flag to indicate we've attempted to fetch in this session
|
|
||||||
useGraphStore.getState().setLabelsFetchAttempted(true)
|
|
||||||
|
|
||||||
useGraphStore.getState().fetchAllDatabaseLabels()
|
|
||||||
.then(() => {
|
|
||||||
labelsLoadedRef.current = true
|
|
||||||
fetchInProgressRef.current = false
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('Failed to fetch labels:', error)
|
|
||||||
fetchInProgressRef.current = false
|
|
||||||
// Reset global flag to allow retry
|
|
||||||
useGraphStore.getState().setLabelsFetchAttempted(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, []) // 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(() => {
|
const getSearchEngine = useCallback(() => {
|
||||||
// Create search engine
|
// Create search engine
|
||||||
@@ -93,30 +57,31 @@ const GraphLabels = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const handleRefresh = useCallback(() => {
|
const handleRefresh = useCallback(() => {
|
||||||
// Reset labels fetch status to allow fetching labels again
|
// Reset fetch status flags
|
||||||
useGraphStore.getState().setLabelsFetchAttempted(false)
|
useGraphStore.getState().setLabelsFetchAttempted(false)
|
||||||
|
|
||||||
// Reset graph data fetch status directly, not depending on allDatabaseLabels changes
|
|
||||||
useGraphStore.getState().setGraphDataFetchAttempted(false)
|
useGraphStore.getState().setGraphDataFetchAttempted(false)
|
||||||
|
|
||||||
// Fetch all labels again
|
// Clear last successful query label to ensure labels are fetched
|
||||||
useGraphStore.getState().fetchAllDatabaseLabels()
|
useGraphStore.getState().setLastSuccessfulQueryLabel('')
|
||||||
.then(() => {
|
|
||||||
// Trigger a graph data reload by changing the query label back and forth
|
// Get current label
|
||||||
const currentLabel = useSettingsStore.getState().queryLabel
|
const currentLabel = useSettingsStore.getState().queryLabel
|
||||||
|
|
||||||
|
// If current label is empty, use default label '*'
|
||||||
|
if (!currentLabel) {
|
||||||
|
useSettingsStore.getState().setQueryLabel('*')
|
||||||
|
} else {
|
||||||
|
// Trigger data reload
|
||||||
useSettingsStore.getState().setQueryLabel('')
|
useSettingsStore.getState().setQueryLabel('')
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
useSettingsStore.getState().setQueryLabel(currentLabel)
|
useSettingsStore.getState().setQueryLabel(currentLabel)
|
||||||
}, 0)
|
}, 0)
|
||||||
})
|
}
|
||||||
.catch((error) => {
|
}, []);
|
||||||
console.error('Failed to refresh labels:', error)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{rawGraph && (
|
{/* Always show refresh button */}
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
variant={controlButtonVariant}
|
variant={controlButtonVariant}
|
||||||
@@ -126,7 +91,6 @@ const GraphLabels = () => {
|
|||||||
>
|
>
|
||||||
<RefreshCw className="h-4 w-4" />
|
<RefreshCw className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<AsyncSelect<string>
|
<AsyncSelect<string>
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
triggerClassName="max-h-8"
|
triggerClassName="max-h-8"
|
||||||
@@ -141,20 +105,23 @@ const GraphLabels = () => {
|
|||||||
placeholder={t('graphPanel.graphLabels.placeholder')}
|
placeholder={t('graphPanel.graphLabels.placeholder')}
|
||||||
value={label !== null ? label : '*'}
|
value={label !== null ? label : '*'}
|
||||||
onChange={(newLabel) => {
|
onChange={(newLabel) => {
|
||||||
const currentLabel = useSettingsStore.getState().queryLabel
|
const currentLabel = useSettingsStore.getState().queryLabel;
|
||||||
|
|
||||||
// select the last item means query all
|
// select the last item means query all
|
||||||
if (newLabel === '...') {
|
if (newLabel === '...') {
|
||||||
newLabel = '*'
|
newLabel = '*';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle reselecting the same label
|
// Handle reselecting the same label
|
||||||
if (newLabel === currentLabel && newLabel !== '*') {
|
if (newLabel === currentLabel && newLabel !== '*') {
|
||||||
newLabel = '*'
|
newLabel = '*';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the label, which will trigger the useEffect to handle data loading
|
// Reset graphDataFetchAttempted flag to ensure data fetch is triggered
|
||||||
useSettingsStore.getState().setQueryLabel(newLabel)
|
useGraphStore.getState().setGraphDataFetchAttempted(false);
|
||||||
|
|
||||||
|
// Update the label to trigger data loading
|
||||||
|
useSettingsStore.getState().setQueryLabel(newLabel);
|
||||||
}}
|
}}
|
||||||
clearable={false} // Prevent clearing value on reselect
|
clearable={false} // Prevent clearing value on reselect
|
||||||
/>
|
/>
|
||||||
|
@@ -12,34 +12,52 @@ import { useSettingsStore } from '@/stores/settings'
|
|||||||
import seedrandom from 'seedrandom'
|
import seedrandom from 'seedrandom'
|
||||||
|
|
||||||
const validateGraph = (graph: RawGraph) => {
|
const validateGraph = (graph: RawGraph) => {
|
||||||
|
// Check if graph exists
|
||||||
if (!graph) {
|
if (!graph) {
|
||||||
return false
|
console.log('Graph validation failed: graph is null');
|
||||||
}
|
return false;
|
||||||
if (!Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if nodes and edges are arrays
|
||||||
|
if (!Array.isArray(graph.nodes) || !Array.isArray(graph.edges)) {
|
||||||
|
console.log('Graph validation failed: nodes or edges is not an array');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if nodes array is empty
|
||||||
|
if (graph.nodes.length === 0) {
|
||||||
|
console.log('Graph validation failed: nodes array is empty');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate each node
|
||||||
for (const node of graph.nodes) {
|
for (const node of graph.nodes) {
|
||||||
if (!node.id || !node.labels || !node.properties) {
|
if (!node.id || !node.labels || !node.properties) {
|
||||||
return false
|
console.log('Graph validation failed: invalid node structure');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate each edge
|
||||||
for (const edge of graph.edges) {
|
for (const edge of graph.edges) {
|
||||||
if (!edge.id || !edge.source || !edge.target) {
|
if (!edge.id || !edge.source || !edge.target) {
|
||||||
return false
|
console.log('Graph validation failed: invalid edge structure');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate edge connections
|
||||||
for (const edge of graph.edges) {
|
for (const edge of graph.edges) {
|
||||||
const source = graph.getNode(edge.source)
|
const source = graph.getNode(edge.source);
|
||||||
const target = graph.getNode(edge.target)
|
const target = graph.getNode(edge.target);
|
||||||
if (source == undefined || target == undefined) {
|
if (source == undefined || target == undefined) {
|
||||||
return false
|
console.log('Graph validation failed: edge references non-existent node');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
console.log('Graph validation passed');
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodeType = {
|
export type NodeType = {
|
||||||
@@ -53,16 +71,32 @@ export type NodeType = {
|
|||||||
export type EdgeType = { label: string }
|
export type EdgeType = { label: string }
|
||||||
|
|
||||||
const fetchGraph = async (label: string, maxDepth: number, minDegree: number) => {
|
const fetchGraph = async (label: string, maxDepth: number, minDegree: number) => {
|
||||||
let rawData: any = null
|
let rawData: any = null;
|
||||||
|
|
||||||
|
// Check if we need to fetch all database labels first
|
||||||
|
const lastSuccessfulQueryLabel = useGraphStore.getState().lastSuccessfulQueryLabel;
|
||||||
|
if (!lastSuccessfulQueryLabel) {
|
||||||
|
console.log('Last successful query label is empty, fetching all database labels first...');
|
||||||
try {
|
try {
|
||||||
rawData = await queryGraphs(label, maxDepth, minDegree)
|
await useGraphStore.getState().fetchAllDatabaseLabels();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!')
|
console.error('Failed to fetch all database labels:', e);
|
||||||
return null
|
// Continue with graph fetch even if labels fetch fails
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rawGraph = null
|
// If label is empty, use default label '*'
|
||||||
|
const queryLabel = label || '*';
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Fetching graph data with label: ${queryLabel}, maxDepth: ${maxDepth}, minDegree: ${minDegree}`);
|
||||||
|
rawData = await queryGraphs(queryLabel, maxDepth, minDegree);
|
||||||
|
} catch (e) {
|
||||||
|
useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rawGraph = null;
|
||||||
|
|
||||||
if (rawData) {
|
if (rawData) {
|
||||||
const nodeIdMap: Record<string, number> = {}
|
const nodeIdMap: Record<string, number> = {}
|
||||||
@@ -192,6 +226,8 @@ const useLightrangeGraph = () => {
|
|||||||
// Use ref to track if data has been loaded and initial load
|
// Use ref to track if data has been loaded and initial load
|
||||||
const dataLoadedRef = useRef(false)
|
const dataLoadedRef = useRef(false)
|
||||||
const initialLoadRef = useRef(false)
|
const initialLoadRef = useRef(false)
|
||||||
|
// Use ref to track if empty data has been handled
|
||||||
|
const emptyDataHandledRef = useRef(false)
|
||||||
|
|
||||||
const getNode = useCallback(
|
const getNode = useCallback(
|
||||||
(nodeId: string) => {
|
(nodeId: string) => {
|
||||||
@@ -224,11 +260,16 @@ const useLightrangeGraph = () => {
|
|||||||
|
|
||||||
// Data fetching logic
|
// Data fetching logic
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip if fetch is already in progress or no query label
|
// Skip if fetch is already in progress
|
||||||
if (fetchInProgressRef.current || !queryLabel) {
|
if (fetchInProgressRef.current) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empty queryLabel should be only handle once(avoid infinite loop)
|
||||||
|
if (!queryLabel && emptyDataHandledRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Only fetch data when graphDataFetchAttempted is false (avoids re-fetching on vite dev mode)
|
// Only fetch data when graphDataFetchAttempted is false (avoids re-fetching on vite dev mode)
|
||||||
if (!isFetching && !useGraphStore.getState().graphDataFetchAttempted) {
|
if (!isFetching && !useGraphStore.getState().graphDataFetchAttempted) {
|
||||||
// Set flags
|
// Set flags
|
||||||
@@ -246,49 +287,106 @@ const useLightrangeGraph = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Fetching graph data...')
|
console.log('Preparing graph data...')
|
||||||
|
|
||||||
// Use a local copy of the parameters
|
// Use a local copy of the parameters
|
||||||
const currentQueryLabel = queryLabel
|
const currentQueryLabel = queryLabel
|
||||||
const currentMaxQueryDepth = maxQueryDepth
|
const currentMaxQueryDepth = maxQueryDepth
|
||||||
const currentMinDegree = minDegree
|
const currentMinDegree = minDegree
|
||||||
|
|
||||||
// Fetch graph data
|
// Declare a variable to store data promise
|
||||||
fetchGraph(currentQueryLabel, currentMaxQueryDepth, currentMinDegree).then((data) => {
|
let dataPromise;
|
||||||
|
|
||||||
|
// 1. If query label is not empty, use fetchGraph
|
||||||
|
if (currentQueryLabel) {
|
||||||
|
dataPromise = fetchGraph(currentQueryLabel, currentMaxQueryDepth, currentMinDegree);
|
||||||
|
} else {
|
||||||
|
// 2. If query label is empty, set data to null
|
||||||
|
console.log('Query label is empty, show empty graph')
|
||||||
|
dataPromise = Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Process data
|
||||||
|
dataPromise.then((data) => {
|
||||||
const state = useGraphStore.getState()
|
const state = useGraphStore.getState()
|
||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
state.reset()
|
state.reset()
|
||||||
|
|
||||||
// Create and set new graph directly
|
// Check if data is empty or invalid
|
||||||
const newSigmaGraph = createSigmaGraph(data)
|
if (!data || !data.nodes || data.nodes.length === 0) {
|
||||||
data?.buildDynamicMap()
|
// Create a graph with a single "Graph Is Empty" node
|
||||||
|
const emptyGraph = new DirectedGraph();
|
||||||
|
|
||||||
|
// Add a single node with "Graph Is Empty" label
|
||||||
|
emptyGraph.addNode('empty-graph-node', {
|
||||||
|
label: t('graphPanel.emptyGraph'),
|
||||||
|
color: '#cccccc', // gray color
|
||||||
|
x: 0.5,
|
||||||
|
y: 0.5,
|
||||||
|
size: 15,
|
||||||
|
borderColor: Constants.nodeBorderColor,
|
||||||
|
borderSize: 0.2
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set graph to store
|
||||||
|
state.setSigmaGraph(emptyGraph);
|
||||||
|
state.setRawGraph(null);
|
||||||
|
|
||||||
|
// Still mark graph as empty for other logic
|
||||||
|
state.setGraphIsEmpty(true);
|
||||||
|
|
||||||
|
// Only clear current label if it's not already empty
|
||||||
|
if (currentQueryLabel) {
|
||||||
|
useSettingsStore.getState().setQueryLabel('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear last successful query label to ensure labels are fetched next time
|
||||||
|
state.setLastSuccessfulQueryLabel('');
|
||||||
|
|
||||||
|
console.log('Graph data is empty, created graph with empty graph node');
|
||||||
|
} else {
|
||||||
|
// Create and set new graph
|
||||||
|
const newSigmaGraph = createSigmaGraph(data);
|
||||||
|
data.buildDynamicMap();
|
||||||
|
|
||||||
// Set new graph data
|
// Set new graph data
|
||||||
state.setSigmaGraph(newSigmaGraph)
|
state.setSigmaGraph(newSigmaGraph);
|
||||||
state.setRawGraph(data)
|
state.setRawGraph(data);
|
||||||
|
state.setGraphIsEmpty(false);
|
||||||
|
|
||||||
|
// Update last successful query label
|
||||||
|
state.setLastSuccessfulQueryLabel(currentQueryLabel);
|
||||||
|
|
||||||
|
// Reset camera view
|
||||||
|
state.setMoveToSelectedNode(true);
|
||||||
|
|
||||||
|
console.log('Graph data loaded successfully');
|
||||||
|
}
|
||||||
|
|
||||||
// Update flags
|
// Update flags
|
||||||
dataLoadedRef.current = true
|
dataLoadedRef.current = true
|
||||||
initialLoadRef.current = true
|
initialLoadRef.current = true
|
||||||
fetchInProgressRef.current = false
|
fetchInProgressRef.current = false
|
||||||
|
|
||||||
// Reset camera view
|
|
||||||
state.setMoveToSelectedNode(true)
|
|
||||||
|
|
||||||
state.setIsFetching(false)
|
state.setIsFetching(false)
|
||||||
|
|
||||||
|
// Mark empty data as handled if data is empty and query label is empty
|
||||||
|
if ((!data || !data.nodes || data.nodes.length === 0) && !currentQueryLabel) {
|
||||||
|
emptyDataHandledRef.current = true;
|
||||||
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('Error fetching graph data:', error)
|
console.error('Error fetching graph data:', error)
|
||||||
|
|
||||||
// Reset state on error
|
// Reset state on error
|
||||||
const state = useGraphStore.getState()
|
const state = useGraphStore.getState()
|
||||||
state.setIsFetching(false)
|
state.setIsFetching(false)
|
||||||
dataLoadedRef.current = false
|
dataLoadedRef.current = false;
|
||||||
fetchInProgressRef.current = false
|
fetchInProgressRef.current = false
|
||||||
state.setGraphDataFetchAttempted(false)
|
state.setGraphDataFetchAttempted(false)
|
||||||
|
state.setLastSuccessfulQueryLabel('') // Clear last successful query label on error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [queryLabel, maxQueryDepth, minDegree, isFetching])
|
}, [queryLabel, maxQueryDepth, minDegree, isFetching, t])
|
||||||
|
|
||||||
// Handle node expansion
|
// Handle node expansion
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@@ -201,7 +201,8 @@
|
|||||||
"placeholder": "Search labels...",
|
"placeholder": "Search labels...",
|
||||||
"andOthers": "And {count} others",
|
"andOthers": "And {count} others",
|
||||||
"refreshTooltip": "Reload graph data"
|
"refreshTooltip": "Reload graph data"
|
||||||
}
|
},
|
||||||
|
"emptyGraph": "Graph Is Empty"
|
||||||
},
|
},
|
||||||
"retrievePanel": {
|
"retrievePanel": {
|
||||||
"chatMessage": {
|
"chatMessage": {
|
||||||
|
@@ -198,7 +198,8 @@
|
|||||||
"placeholder": "搜索标签...",
|
"placeholder": "搜索标签...",
|
||||||
"andOthers": "还有 {count} 个",
|
"andOthers": "还有 {count} 个",
|
||||||
"refreshTooltip": "重新加载图形数据"
|
"refreshTooltip": "重新加载图形数据"
|
||||||
}
|
},
|
||||||
|
"emptyGraph": "图谱数据为空"
|
||||||
},
|
},
|
||||||
"retrievePanel": {
|
"retrievePanel": {
|
||||||
"chatMessage": {
|
"chatMessage": {
|
||||||
|
@@ -74,6 +74,8 @@ interface GraphState {
|
|||||||
|
|
||||||
moveToSelectedNode: boolean
|
moveToSelectedNode: boolean
|
||||||
isFetching: boolean
|
isFetching: boolean
|
||||||
|
graphIsEmpty: boolean
|
||||||
|
lastSuccessfulQueryLabel: string
|
||||||
|
|
||||||
// Global flags to track data fetching attempts
|
// Global flags to track data fetching attempts
|
||||||
graphDataFetchAttempted: boolean
|
graphDataFetchAttempted: boolean
|
||||||
@@ -88,6 +90,8 @@ interface GraphState {
|
|||||||
reset: () => void
|
reset: () => void
|
||||||
|
|
||||||
setMoveToSelectedNode: (moveToSelectedNode: boolean) => void
|
setMoveToSelectedNode: (moveToSelectedNode: boolean) => void
|
||||||
|
setGraphIsEmpty: (isEmpty: boolean) => void
|
||||||
|
setLastSuccessfulQueryLabel: (label: string) => void
|
||||||
|
|
||||||
setRawGraph: (rawGraph: RawGraph | null) => void
|
setRawGraph: (rawGraph: RawGraph | null) => void
|
||||||
setSigmaGraph: (sigmaGraph: DirectedGraph | null) => void
|
setSigmaGraph: (sigmaGraph: DirectedGraph | null) => void
|
||||||
@@ -120,6 +124,8 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
|
|||||||
|
|
||||||
moveToSelectedNode: false,
|
moveToSelectedNode: false,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
|
graphIsEmpty: false,
|
||||||
|
lastSuccessfulQueryLabel: '', // Initialize as empty to ensure fetchAllDatabaseLabels runs on first query
|
||||||
|
|
||||||
// Initialize global flags
|
// Initialize global flags
|
||||||
graphDataFetchAttempted: false,
|
graphDataFetchAttempted: false,
|
||||||
@@ -132,6 +138,9 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
|
|||||||
|
|
||||||
searchEngine: null,
|
searchEngine: null,
|
||||||
|
|
||||||
|
setGraphIsEmpty: (isEmpty: boolean) => set({ graphIsEmpty: isEmpty }),
|
||||||
|
setLastSuccessfulQueryLabel: (label: string) => set({ lastSuccessfulQueryLabel: label }),
|
||||||
|
|
||||||
|
|
||||||
setIsFetching: (isFetching: boolean) => set({ isFetching }),
|
setIsFetching: (isFetching: boolean) => set({ isFetching }),
|
||||||
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
|
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
|
||||||
@@ -155,7 +164,9 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
|
|||||||
rawGraph: null,
|
rawGraph: null,
|
||||||
sigmaGraph: null, // to avoid other components from acccessing graph objects
|
sigmaGraph: null, // to avoid other components from acccessing graph objects
|
||||||
searchEngine: null,
|
searchEngine: null,
|
||||||
moveToSelectedNode: false
|
moveToSelectedNode: false,
|
||||||
|
graphIsEmpty: false
|
||||||
|
// Do not reset lastSuccessfulQueryLabel here as it's used to track query history
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user