Sync frontend with backend MAX_GRAPH_NODES

- Auto-adjust graph nodes when limit changes
- Add refresh trigger for node count changes
- Store backend limit in settings
This commit is contained in:
yangdx 2025-07-08 00:21:42 +08:00
parent ef79088f60
commit 9bf048a4b1
3 changed files with 64 additions and 15 deletions

View file

@ -1,4 +1,4 @@
import { useState, useCallback} from 'react' import { useState, useCallback, useEffect} from 'react'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
import Checkbox from '@/components/ui/Checkbox' import Checkbox from '@/components/ui/Checkbox'
import Button from '@/components/ui/Button' import Button from '@/components/ui/Button'
@ -62,6 +62,10 @@ const LabeledNumberInput = ({
// Create unique ID using the label text converted to lowercase with spaces removed // Create unique ID using the label text converted to lowercase with spaces removed
const id = `input-${label.toLowerCase().replace(/\s+/g, '-')}`; const id = `input-${label.toLowerCase().replace(/\s+/g, '-')}`;
useEffect(() => {
setCurrentValue(value)
}, [value])
const onValueChange = useCallback( const onValueChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => { (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value.trim() const text = e.target.value.trim()
@ -154,6 +158,7 @@ export default function Settings() {
const maxEdgeSize = useSettingsStore.use.maxEdgeSize() const maxEdgeSize = useSettingsStore.use.maxEdgeSize()
const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth() const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth()
const graphMaxNodes = useSettingsStore.use.graphMaxNodes() const graphMaxNodes = useSettingsStore.use.graphMaxNodes()
const backendMaxGraphNodes = useSettingsStore.use.backendMaxGraphNodes()
const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations() const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations()
const enableHealthCheck = useSettingsStore.use.enableHealthCheck() const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
@ -213,14 +218,10 @@ export default function Settings() {
}, []) }, [])
const setGraphMaxNodes = useCallback((nodes: number) => { const setGraphMaxNodes = useCallback((nodes: number) => {
if (nodes < 1 || nodes > 1000) return const maxLimit = backendMaxGraphNodes || 1000
useSettingsStore.setState({ graphMaxNodes: nodes }) if (nodes < 1 || nodes > maxLimit) return
const currentLabel = useSettingsStore.getState().queryLabel useSettingsStore.getState().setGraphMaxNodes(nodes, true)
useSettingsStore.getState().setQueryLabel('') }, [backendMaxGraphNodes])
setTimeout(() => {
useSettingsStore.getState().setQueryLabel(currentLabel)
}, 300)
}, [])
const setGraphLayoutMaxIterations = useCallback((iterations: number) => { const setGraphLayoutMaxIterations = useCallback((iterations: number) => {
if (iterations < 1) return if (iterations < 1) return
@ -360,11 +361,11 @@ export default function Settings() {
onEditFinished={setGraphQueryMaxDepth} onEditFinished={setGraphQueryMaxDepth}
/> />
<LabeledNumberInput <LabeledNumberInput
label={t('graphPanel.sideBar.settings.maxNodes')} label={`${t('graphPanel.sideBar.settings.maxNodes')} (≤ ${backendMaxGraphNodes || 1000})`}
min={1} min={1}
max={1000} max={backendMaxGraphNodes || 1000}
value={graphMaxNodes} value={graphMaxNodes}
defaultValue={1000} defaultValue={backendMaxGraphNodes || 1000}
onEditFinished={setGraphMaxNodes} onEditFinished={setGraphMaxNodes}
/> />
<LabeledNumberInput <LabeledNumberInput

View file

@ -36,7 +36,10 @@ interface SettingsState {
setGraphQueryMaxDepth: (depth: number) => void setGraphQueryMaxDepth: (depth: number) => void
graphMaxNodes: number graphMaxNodes: number
setGraphMaxNodes: (nodes: number) => void setGraphMaxNodes: (nodes: number, triggerRefresh?: boolean) => void
backendMaxGraphNodes: number | null
setBackendMaxGraphNodes: (maxNodes: number | null) => void
graphLayoutMaxIterations: number graphLayoutMaxIterations: number
setGraphLayoutMaxIterations: (iterations: number) => void setGraphLayoutMaxIterations: (iterations: number) => void
@ -90,6 +93,7 @@ const useSettingsStoreBase = create<SettingsState>()(
graphQueryMaxDepth: 3, graphQueryMaxDepth: 3,
graphMaxNodes: 1000, graphMaxNodes: 1000,
backendMaxGraphNodes: null,
graphLayoutMaxIterations: 15, graphLayoutMaxIterations: 15,
queryLabel: defaultQueryLabel, queryLabel: defaultQueryLabel,
@ -143,7 +147,27 @@ const useSettingsStoreBase = create<SettingsState>()(
setGraphQueryMaxDepth: (depth: number) => set({ graphQueryMaxDepth: depth }), setGraphQueryMaxDepth: (depth: number) => set({ graphQueryMaxDepth: depth }),
setGraphMaxNodes: (nodes: number) => set({ graphMaxNodes: nodes }), setGraphMaxNodes: (nodes: number, triggerRefresh: boolean = false) => {
const state = useSettingsStore.getState();
if (state.graphMaxNodes === nodes) {
return;
}
if (triggerRefresh) {
const currentLabel = state.queryLabel;
// Atomically update both the node count and the query label to trigger a refresh.
set({ graphMaxNodes: nodes, queryLabel: '' });
// Restore the label after a short delay.
setTimeout(() => {
set({ queryLabel: currentLabel });
}, 300);
} else {
set({ graphMaxNodes: nodes });
}
},
setBackendMaxGraphNodes: (maxNodes: number | null) => set({ backendMaxGraphNodes: maxNodes }),
setMinEdgeSize: (size: number) => set({ minEdgeSize: size }), setMinEdgeSize: (size: number) => set({ minEdgeSize: size }),
@ -168,7 +192,7 @@ const useSettingsStoreBase = create<SettingsState>()(
{ {
name: 'settings-storage', name: 'settings-storage',
storage: createJSONStorage(() => localStorage), storage: createJSONStorage(() => localStorage),
version: 13, version: 14,
migrate: (state: any, version: number) => { migrate: (state: any, version: number) => {
if (version < 2) { if (version < 2) {
state.showEdgeLabel = false state.showEdgeLabel = false
@ -232,6 +256,10 @@ const useSettingsStoreBase = create<SettingsState>()(
state.querySettings.user_prompt = '' state.querySettings.user_prompt = ''
} }
} }
if (version < 14) {
// Add backendMaxGraphNodes field for older versions
state.backendMaxGraphNodes = null
}
return state return state
} }
} }

View file

@ -1,6 +1,7 @@
import { create } from 'zustand' import { create } from 'zustand'
import { createSelectors } from '@/lib/utils' import { createSelectors } from '@/lib/utils'
import { checkHealth, LightragStatus } from '@/api/lightrag' import { checkHealth, LightragStatus } from '@/api/lightrag'
import { useSettingsStore } from './settings'
interface BackendState { interface BackendState {
health: boolean health: boolean
@ -58,6 +59,25 @@ const useBackendStateStoreBase = create<BackendState>()((set) => ({
); );
} }
// Extract and store backend max graph nodes limit
if (health.configuration?.max_graph_nodes) {
const maxNodes = parseInt(health.configuration.max_graph_nodes, 10)
if (!isNaN(maxNodes) && maxNodes > 0) {
const currentBackendMaxNodes = useSettingsStore.getState().backendMaxGraphNodes
// Only update if the backend limit has actually changed
if (currentBackendMaxNodes !== maxNodes) {
useSettingsStore.getState().setBackendMaxGraphNodes(maxNodes)
// Auto-adjust current graphMaxNodes if it exceeds the new backend limit
const currentMaxNodes = useSettingsStore.getState().graphMaxNodes
if (currentMaxNodes > maxNodes) {
useSettingsStore.getState().setGraphMaxNodes(maxNodes, true)
}
}
}
}
set({ set({
health: true, health: true,
message: null, message: null,