1
- import React , { useState } from 'react'
1
+ import React , { useRef , useState } from 'react'
2
2
import { TextField , Button , Box , Typography , Table , TableHead , TableBody , TableRow , TableCell , Paper , IconButton , Dialog , DialogTitle , styled } from '@mui/material'
3
3
import apiClient from '../util/apiClient'
4
4
import { useMutation , useQuery } from '@tanstack/react-query'
5
- import type { RagIndexAttributes } from '../../server/db/models/ragIndex'
6
5
import { CloudUpload , Settings } from '@mui/icons-material'
6
+ import Markdown from './Banner/Markdown'
7
7
8
8
type RagResponse = {
9
- total : number
10
- documents : Array < {
11
- id : string
12
- value : {
13
- title : string
14
- content : string
15
- score : number
16
- }
17
- } >
9
+ id : string
10
+ value : {
11
+ title : string
12
+ content : string
13
+ score : number
14
+ }
15
+ }
16
+
17
+ type RagIndexAttributes = {
18
+ id : number
19
+ metadata : {
20
+ name : string
21
+ dim : number
22
+ }
23
+ numOfChunks : number
18
24
}
19
25
20
26
const useRagIndices = ( ) => {
@@ -60,11 +66,18 @@ const useUploadMutation = (index: RagIndexAttributes | null) => {
60
66
Array . from ( files ) . forEach ( ( file ) => {
61
67
formData . append ( 'files' , file )
62
68
} )
63
- const response = await apiClient . post ( `/rag/indices/${ index . id } /upload` , formData , {
69
+
70
+ // Returns a stream
71
+ const response = await apiClient . put ( `/rag/indices/${ index . id } /upload` , formData , {
64
72
headers : {
65
73
'Content-Type' : 'multipart/form-data' ,
66
74
} ,
75
+ responseType : 'stream' ,
76
+
77
+
67
78
} )
79
+
80
+ console . log ( 'Upload response:' , response . data )
68
81
return response . data
69
82
} ,
70
83
} )
@@ -90,21 +103,49 @@ const Rag: React.FC = () => {
90
103
const [ indexName , setIndexName ] = useState ( '' )
91
104
const [ selectedIndex , setSelectedIndex ] = useState < RagIndexAttributes > ( null )
92
105
const [ inputValue , setInputValue ] = useState ( '' )
93
- const [ response , setResponse ] = useState < RagResponse | null > ( null )
106
+ const [ topK , setTopK ] = useState ( 5 )
107
+ const [ response , setResponse ] = useState < RagResponse [ ] | null > ( null )
94
108
const uploadMutation = useUploadMutation ( selectedIndex )
95
109
const [ modalOpen , setModalOpen ] = useState ( false )
110
+ const progressLogs = useRef < HTMLParagraphElement > ( )
96
111
97
112
const handleSubmit = async ( event : React . FormEvent ) => {
98
113
event . preventDefault ( )
99
114
console . log ( 'Form submitted with value:' , inputValue )
100
115
const res = await apiClient . post ( '/rag/query' , {
101
- prompt : inputValue ,
116
+ query : inputValue ,
117
+ indexId : selectedIndex ?. id ,
118
+ topK,
102
119
} )
103
120
console . log ( 'Response from server:' , res . data )
104
121
setResponse ( res . data )
105
122
setInputValue ( '' )
106
123
}
107
124
125
+ // Processes the upload progress stream which returns JSON objects
126
+ const processUploadProgressStream = ( stream ) => {
127
+ stream . on ( 'data' , ( data : any ) => {
128
+ const parsedData = JSON . parse ( data . toString ( ) )
129
+ console . log ( 'Parsed data:' , parsedData )
130
+ if ( parsedData . stage === 'done' ) {
131
+ progressLogs . current . innerHTML += `Upload completed: ${ JSON . stringify ( parsedData ) } \n`
132
+ } else if ( parsedData . error ) {
133
+ progressLogs . current . innerHTML += `Error: ${ parsedData . error } \n`
134
+ } else {
135
+ progressLogs . current . innerHTML += `Progress: ${ JSON . stringify ( parsedData ) } \n`
136
+ }
137
+ } )
138
+ stream . on ( 'end' , ( ) => {
139
+ progressLogs . current . innerHTML += 'Upload stream ended.\n'
140
+ } )
141
+ stream . on ( 'error' , ( err : any ) => {
142
+ progressLogs . current . innerHTML += `Error: ${ err } \n`
143
+ } )
144
+ stream . on ( 'close' , ( ) => {
145
+ progressLogs . current . innerHTML += 'Upload stream closed.\n'
146
+ } )
147
+ }
148
+
108
149
return (
109
150
< Box sx = { { display : 'flex' , gap : 2 } } >
110
151
< Dialog open = { ! ! selectedIndex && modalOpen } onClose = { ( ) => setModalOpen ( false ) } >
@@ -118,8 +159,8 @@ const Rag: React.FC = () => {
118
159
const files = event . target . files
119
160
console . log ( 'Files selected:' , files )
120
161
if ( files && files . length > 0 ) {
121
- await uploadMutation . mutateAsync ( files )
122
- refetch ( )
162
+ const stream = await uploadMutation . mutateAsync ( files )
163
+ processUploadProgressStream ( stream )
123
164
}
124
165
} }
125
166
multiple
@@ -139,6 +180,9 @@ const Rag: React.FC = () => {
139
180
Delete Index
140
181
</ Button >
141
182
</ Box >
183
+ < Box sx = { { padding : 2 } } >
184
+ < p ref = { progressLogs } style = { { whiteSpace : 'pre-wrap' } } />
185
+ </ Box >
142
186
</ Dialog >
143
187
< Box >
144
188
< Typography variant = "h4" mb = "1rem" >
@@ -174,13 +218,15 @@ const Rag: React.FC = () => {
174
218
< TableCell > ID</ TableCell >
175
219
< TableCell > Name</ TableCell >
176
220
< TableCell > Dim</ TableCell >
221
+ < TableCell > Num chunks</ TableCell >
177
222
</ TableRow >
178
223
</ TableHead >
179
224
< TableBody >
180
225
< TableRow >
181
226
< TableCell > { index . id } </ TableCell >
182
227
< TableCell > { index . metadata . name } </ TableCell >
183
228
< TableCell > { index . metadata . dim } </ TableCell >
229
+ < TableCell > { index . numOfChunks } </ TableCell >
184
230
</ TableRow >
185
231
</ TableBody >
186
232
</ Table >
@@ -205,35 +251,25 @@ const Rag: React.FC = () => {
205
251
display : 'flex' ,
206
252
flexDirection : 'column' ,
207
253
gap : 2 ,
208
- width : '300px' ,
209
254
margin : '0 auto' ,
210
255
} }
211
256
>
212
257
< TextField label = "Enter text" variant = "outlined" value = { inputValue } onChange = { ( e ) => setInputValue ( e . target . value ) } fullWidth />
213
- < Button type = "submit" variant = "contained" color = "primary" >
214
- Submit
258
+ < TextField label = "top k" variant = "outlined" type = "number" value = { topK } onChange = { ( e ) => setTopK ( parseInt ( e . target . value , 10 ) ) } fullWidth />
259
+ < Button type = "submit" variant = "contained" color = "primary" disabled = { ! inputValue || ! selectedIndex } >
260
+ Search
215
261
</ Button >
216
262
{ response && (
217
- < Box
218
- sx = { {
219
- marginTop : 2 ,
220
- padding : 2 ,
221
- border : '1px solid #ccc' ,
222
- borderRadius : '4px' ,
223
- } }
224
- >
263
+ < Box mt = { 2 } >
225
264
< Typography variant = "h6" > Response:</ Typography >
226
- < Typography variant = "body1" > Total: { response . total } </ Typography >
227
- { response . documents . map ( ( doc ) => (
228
- < Box key = { doc . id } sx = { { marginBottom : 1 } } >
229
- < Typography variant = "subtitle1" > { doc . value . title } </ Typography >
230
- < Typography variant = "body2" > { doc . value . content } </ Typography >
265
+ { response . map ( ( doc ) => (
266
+ < Paper key = { doc . id } sx = { { marginBottom : 2 , p : 1 } } elevation = { 2 } >
231
267
< Typography variant = "caption" > Score: { doc . value . score } </ Typography >
232
- </ Box >
268
+ < Markdown > { doc . value . content } </ Markdown >
269
+ </ Paper >
233
270
) ) }
234
271
</ Box >
235
272
) }
236
- ss
237
273
</ Box >
238
274
</ Box >
239
275
)
0 commit comments