ArnoChen commited on
Commit
5856d68
·
1 Parent(s): 376f172

implement backend health check and alert system

Browse files
lightrag/api/graph_viewer_webui/src/App.tsx CHANGED
@@ -1,12 +1,18 @@
1
  import ThemeProvider from '@/components/ThemeProvider'
 
2
  import { GraphViewer } from '@/GraphViewer'
 
 
3
 
4
  function App() {
 
 
5
  return (
6
- <ThemeProvider defaultTheme="system" storageKey="lightrag-viewer-webui-theme">
7
- <div className="h-screen w-screen">
8
  <GraphViewer />
9
  </div>
 
10
  </ThemeProvider>
11
  )
12
  }
 
1
  import ThemeProvider from '@/components/ThemeProvider'
2
+ import BackendMessageAlert from '@/components/BackendMessageAlert'
3
  import { GraphViewer } from '@/GraphViewer'
4
+ import { cn } from '@/lib/utils'
5
+ import { useBackendState } from '@/stores/state'
6
 
7
  function App() {
8
+ const health = useBackendState.use.health()
9
+
10
  return (
11
+ <ThemeProvider>
12
+ <div className={cn('h-screen w-screen', !health && 'pointer-events-none')}>
13
  <GraphViewer />
14
  </div>
15
+ {!health && <BackendMessageAlert />}
16
  </ThemeProvider>
17
  )
18
  }
lightrag/api/graph_viewer_webui/src/GraphViewer.tsx CHANGED
@@ -153,7 +153,7 @@ export const GraphViewer = () => {
153
  />
154
  </div>
155
 
156
- <div className="bg-background/20 absolute bottom-2 left-2 flex flex-col rounded-xl border-2 backdrop-blur-lg">
157
  <Settings />
158
  <ZoomControl />
159
  <LayoutsControl />
 
153
  />
154
  </div>
155
 
156
+ <div className="bg-background/60 absolute bottom-2 left-2 flex flex-col rounded-xl border-2 backdrop-blur-lg">
157
  <Settings />
158
  <ZoomControl />
159
  <LayoutsControl />
lightrag/api/graph_viewer_webui/src/api/lightrag.ts ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { backendBaseUrl } from '@/lib/constants'
2
+
3
+ export type LightragNodeType = {
4
+ id: string
5
+ labels: string[]
6
+ properties: Record<string, any>
7
+ }
8
+
9
+ export type LightragEdgeType = {
10
+ id: string
11
+ source: string
12
+ target: string
13
+ type: string
14
+ properties: Record<string, any>
15
+ }
16
+
17
+ export type LightragGraphType = {
18
+ nodes: LightragNodeType[]
19
+ edges: LightragEdgeType[]
20
+ }
21
+
22
+ export type LightragStatus = {
23
+ status: 'healthy'
24
+ working_directory: string
25
+ input_directory: string
26
+ indexed_files: string[]
27
+ indexed_files_count: number
28
+ configuration: {
29
+ llm_binding: string
30
+ llm_binding_host: string
31
+ llm_model: string
32
+ embedding_binding: string
33
+ embedding_binding_host: string
34
+ embedding_model: string
35
+ max_tokens: number
36
+ kv_storage: string
37
+ doc_status_storage: string
38
+ graph_storage: string
39
+ vector_storage: string
40
+ }
41
+ }
42
+
43
+ export type LightragDocumentsScanProgress = {
44
+ is_scanning: boolean
45
+ current_file: string
46
+ indexed_count: number
47
+ total_files: number
48
+ progress: number
49
+ }
50
+
51
+ const checkResponse = (response: Response) => {
52
+ if (!response.ok) {
53
+ throw new Error(`${response.status} ${response.statusText} ${response.url}`)
54
+ }
55
+ }
56
+
57
+ export const queryGraphs = async (label: string): Promise<LightragGraphType> => {
58
+ const response = await fetch(backendBaseUrl + `/graphs?label=${label}`)
59
+ checkResponse(response)
60
+ return await response.json()
61
+ }
62
+
63
+ export const getGraphLabels = async (): Promise<string[]> => {
64
+ const response = await fetch(backendBaseUrl + '/graph/label/list')
65
+ checkResponse(response)
66
+ return await response.json()
67
+ }
68
+
69
+ export const checkHealth = async (): Promise<
70
+ LightragStatus | { status: 'error'; message: string }
71
+ > => {
72
+ try {
73
+ const response = await fetch(backendBaseUrl + '/health')
74
+ if (!response.ok) {
75
+ return {
76
+ status: 'error',
77
+ message: `Health check failed. Service is currently unavailable.\n${response.status} ${response.statusText} ${response.url}`
78
+ }
79
+ }
80
+ return await response.json()
81
+ } catch (e) {
82
+ return {
83
+ status: 'error',
84
+ message: `${e}`
85
+ }
86
+ }
87
+ }
88
+
89
+ export const getDocuments = async (): Promise<string[]> => {
90
+ const response = await fetch(backendBaseUrl + '/documents')
91
+ return await response.json()
92
+ }
93
+
94
+ export const getDocumentsScanProgress = async (): Promise<LightragDocumentsScanProgress> => {
95
+ const response = await fetch(backendBaseUrl + '/documents/scan-progress')
96
+ return await response.json()
97
+ }
lightrag/api/graph_viewer_webui/src/components/BackendMessageAlert.tsx ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/Alert'
2
+ import { useBackendState } from '@/stores/state'
3
+ import { AlertCircle } from 'lucide-react'
4
+
5
+ const BackendMessageAlert = () => {
6
+ const health = useBackendState.use.health()
7
+ const message = useBackendState.use.message()
8
+ const messageTitle = useBackendState.use.messageTitle()
9
+
10
+ return (
11
+ <Alert
12
+ variant={health ? 'default' : 'destructive'}
13
+ className="absolute top-1/2 left-1/2 w-auto -translate-x-1/2 -translate-y-1/2 transform"
14
+ >
15
+ {!health && <AlertCircle className="h-4 w-4" />}
16
+ <AlertTitle>{messageTitle}</AlertTitle>
17
+ <AlertDescription>{message}</AlertDescription>
18
+ </Alert>
19
+ )
20
+ }
21
+
22
+ export default BackendMessageAlert
lightrag/api/graph_viewer_webui/src/components/GraphSearch.tsx CHANGED
@@ -70,7 +70,7 @@ export const GraphSearchInput = ({
70
 
71
  return (
72
  <AsyncSelect
73
- className="bg-background/20 w-52 rounded-xl border-1 opacity-60 backdrop-blur-lg transition-opacity hover:opacity-100"
74
  fetcher={loadOptions}
75
  renderOption={OptionComponent}
76
  getOptionValue={(item) => item.id}
 
70
 
71
  return (
72
  <AsyncSelect
73
+ className="bg-background/60 w-52 rounded-xl border-1 opacity-60 backdrop-blur-lg transition-opacity hover:opacity-100"
74
  fetcher={loadOptions}
75
  renderOption={OptionComponent}
76
  getOptionValue={(item) => item.id}
lightrag/api/graph_viewer_webui/src/components/PropertiesView.tsx CHANGED
@@ -59,7 +59,7 @@ const PropertiesView = () => {
59
  return <></>
60
  }
61
  return (
62
- <div className="bg-background/20 max-w-sm rounded-xl border-2 p-2 backdrop-blur-lg">
63
  {currentType == 'node' ? (
64
  <NodePropertiesView node={currentElement as any} />
65
  ) : (
 
59
  return <></>
60
  }
61
  return (
62
+ <div className="bg-background/80 max-w-sm rounded-xl border-2 p-2 backdrop-blur-lg">
63
  {currentType == 'node' ? (
64
  <NodePropertiesView node={currentElement as any} />
65
  ) : (
lightrag/api/graph_viewer_webui/src/components/Settings.tsx CHANGED
@@ -7,6 +7,8 @@ import { useSettingsStore } from '@/stores/settings'
7
 
8
  import { SettingsIcon } from 'lucide-react'
9
 
 
 
10
  /**
11
  * Component that displays a checkbox with a label.
12
  */
@@ -95,6 +97,13 @@ export default function Settings() {
95
  onCheckedChange={setShowEdgeLabel}
96
  label="Show Edge Label"
97
  />
 
 
 
 
 
 
 
98
  </div>
99
  </PopoverContent>
100
  </Popover>
 
7
 
8
  import { SettingsIcon } from 'lucide-react'
9
 
10
+ import * as Api from '@/api/lightrag'
11
+
12
  /**
13
  * Component that displays a checkbox with a label.
14
  */
 
97
  onCheckedChange={setShowEdgeLabel}
98
  label="Show Edge Label"
99
  />
100
+ <Button
101
+ onClick={async () => {
102
+ console.log(Api.checkHealth())
103
+ }}
104
+ >
105
+ Test Api
106
+ </Button>
107
  </div>
108
  </PopoverContent>
109
  </Popover>
lightrag/api/graph_viewer_webui/src/components/ui/Alert.tsx ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from 'react'
2
+ import { cva, type VariantProps } from 'class-variance-authority'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ const alertVariants = cva(
7
+ 'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-background text-foreground',
12
+ destructive:
13
+ 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive'
14
+ }
15
+ },
16
+ defaultVariants: {
17
+ variant: 'default'
18
+ }
19
+ }
20
+ )
21
+
22
+ const Alert = React.forwardRef<
23
+ HTMLDivElement,
24
+ React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
25
+ >(({ className, variant, ...props }, ref) => (
26
+ <div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
27
+ ))
28
+ Alert.displayName = 'Alert'
29
+
30
+ const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
31
+ ({ className, ...props }, ref) => (
32
+ <h5
33
+ ref={ref}
34
+ className={cn('mb-1 leading-none font-medium tracking-tight', className)}
35
+ {...props}
36
+ />
37
+ )
38
+ )
39
+ AlertTitle.displayName = 'AlertTitle'
40
+
41
+ const AlertDescription = React.forwardRef<
42
+ HTMLParagraphElement,
43
+ React.HTMLAttributes<HTMLParagraphElement>
44
+ >(({ className, ...props }, ref) => (
45
+ <div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />
46
+ ))
47
+ AlertDescription.displayName = 'AlertDescription'
48
+
49
+ export { Alert, AlertTitle, AlertDescription }
lightrag/api/graph_viewer_webui/src/hooks/useLightragGraph.tsx CHANGED
@@ -3,6 +3,8 @@ import { useCallback, useEffect, useState } from 'react'
3
  import { randomColor } from '@/lib/utils'
4
  import * as Constants from '@/lib/constants'
5
  import { useGraphStore, RawGraph } from '@/stores/graph'
 
 
6
 
7
  const validateGraph = (graph: RawGraph) => {
8
  if (!graph) {
@@ -46,8 +48,14 @@ export type NodeType = {
46
  export type EdgeType = { label: string }
47
 
48
  const fetchGraph = async (label: string) => {
49
- const response = await fetch(`/graphs?label=${label}`)
50
- const rawData = await response.json()
 
 
 
 
 
 
51
 
52
  let rawGraph = null
53
 
 
3
  import { randomColor } from '@/lib/utils'
4
  import * as Constants from '@/lib/constants'
5
  import { useGraphStore, RawGraph } from '@/stores/graph'
6
+ import { queryGraphs } from '@/api/lightrag'
7
+ import { useBackendState } from '@/stores/state'
8
 
9
  const validateGraph = (graph: RawGraph) => {
10
  if (!graph) {
 
48
  export type EdgeType = { label: string }
49
 
50
  const fetchGraph = async (label: string) => {
51
+ let rawData: any = null
52
+
53
+ try {
54
+ rawData = await queryGraphs(label)
55
+ } catch (e) {
56
+ useBackendState.getState().setErrorMessage(`${e}`, 'Query Graphs Error!')
57
+ return null
58
+ }
59
 
60
  let rawGraph = null
61
 
lightrag/api/graph_viewer_webui/src/lib/constants.ts CHANGED
@@ -1,5 +1,7 @@
1
  import { ButtonVariantType } from '@/components/ui/Button'
2
 
 
 
3
  export const controlButtonVariant: ButtonVariantType = 'ghost'
4
 
5
  export const labelColorDarkTheme = '#B2EBF2'
 
1
  import { ButtonVariantType } from '@/components/ui/Button'
2
 
3
+ export const backendBaseUrl = ''
4
+
5
  export const controlButtonVariant: ButtonVariantType = 'ghost'
6
 
7
  export const labelColorDarkTheme = '#B2EBF2'
lightrag/api/graph_viewer_webui/src/stores/state.ts ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { create } from 'zustand'
2
+ import { createSelectors } from '@/lib/utils'
3
+ import { checkHealth } from '@/api/lightrag'
4
+
5
+ interface BackendState {
6
+ health: boolean
7
+ message: string | null
8
+ messageTitle: string | null
9
+
10
+ check: () => Promise<boolean>
11
+ clear: () => void
12
+ setErrorMessage: (message: string, messageTitle: string) => void
13
+ }
14
+
15
+ const useBackendStateStoreBase = create<BackendState>()((set) => ({
16
+ health: true,
17
+ message: null,
18
+ messageTitle: null,
19
+
20
+ check: async () => {
21
+ const health = await checkHealth()
22
+ if (health.status === 'healthy') {
23
+ set({ health: true, message: null, messageTitle: null })
24
+ return true
25
+ }
26
+ set({ health: false, message: health.message, messageTitle: 'Backend Health Check Error!' })
27
+ return false
28
+ },
29
+
30
+ clear: () => {
31
+ set({ health: true, message: null, messageTitle: null })
32
+ },
33
+
34
+ setErrorMessage: (message: string, messageTitle: string) => {
35
+ set({ health: false, message, messageTitle })
36
+ }
37
+ }))
38
+
39
+ const useBackendState = createSelectors(useBackendStateStoreBase)
40
+
41
+ export { useBackendState }