Skip to content

Commit d252dc0

Browse files
committed
Rag management UX
1 parent 66b570f commit d252dc0

File tree

7 files changed

+241
-133
lines changed

7 files changed

+241
-133
lines changed

src/client/Router.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Chats from './components/Chats'
1313
import Statistics from './components/Statistics'
1414
import Rag from './components/Rag/Rag'
1515
import { ChatV2 } from './components/ChatV2/ChatV2'
16+
import { RagIndex } from './components/Rag/RagIndex'
1617

1718
const router = createBrowserRouter(
1819
createRoutesFromElements(
@@ -29,6 +30,7 @@ const router = createBrowserRouter(
2930
<Route path="/chats" element={<Chats />} />
3031
<Route path="/statistics" element={<Statistics />} />
3132
<Route path="/rag" element={<Rag />} />
33+
<Route path="/rag/:id" element={<RagIndex />} />
3234
</Route>,
3335
),
3436
{

src/client/components/Rag/Rag.tsx

Lines changed: 18 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import React, { useState } from 'react'
2-
import { TextField, Button, Box, Typography, Table, TableHead, TableBody, TableRow, TableCell, Paper, IconButton, Dialog, DialogTitle, styled } from '@mui/material'
3-
import apiClient, { postAbortableStream } from '../../util/apiClient'
2+
import { TextField, Button, Box, Typography, Table, TableHead, TableBody, TableRow, TableCell, Paper, IconButton, Dialog, DialogTitle, styled, LinearProgress } from '@mui/material'
3+
import apiClient from '../../util/apiClient'
44
import { useMutation, useQuery } from '@tanstack/react-query'
55
import { CloudUpload, Settings } from '@mui/icons-material'
66
import Markdown from '../Banner/Markdown'
77
import { useSnackbar } from 'notistack'
8-
import { ProgressReporter } from './ProgressReporter'
8+
import type { RagFileAttributes } from '../../../server/db/models/ragFile'
9+
import { orderBy } from 'lodash'
10+
import { IngestionPipelineStageKeys, IngestionPipelineStages } from '../../../shared/constants'
11+
import { useNavigate } from 'react-router-dom'
912

1013
type RagResponse = {
1114
id: string
@@ -19,12 +22,13 @@ type RagResponse = {
1922

2023
type RagIndexAttributes = {
2124
id: number
25+
createdAt: string
26+
updatedAt: string
2227
metadata: {
2328
name: string
2429
dim: number
2530
}
26-
numOfChunks: number
27-
filenames: string[]
31+
ragFileCount: number
2832
}
2933

3034
const useRagIndices = () => {
@@ -38,7 +42,6 @@ const useRagIndices = () => {
3842

3943
return { data, ...rest }
4044
}
41-
4245
const useCreateRagIndexMutation = () => {
4346
const mutation = useMutation({
4447
mutationFn: async (indexName: string) => {
@@ -49,65 +52,16 @@ const useCreateRagIndexMutation = () => {
4952
return mutation
5053
}
5154

52-
const useDeleteRagIndexMutation = () => {
53-
const mutation = useMutation({
54-
mutationFn: async (indexId: number) => {
55-
const response = await apiClient.delete(`/rag/indices/${indexId}`)
56-
return response.data
57-
},
58-
})
59-
return mutation
60-
}
61-
62-
const useUploadMutation = (index: RagIndexAttributes | null) => {
63-
const mutation = useMutation({
64-
mutationFn: async (files: FileList) => {
65-
if (!index) {
66-
throw new Error('Index is required')
67-
}
68-
const formData = new FormData()
69-
// Append each file individually
70-
Array.from(files).forEach((file) => {
71-
formData.append('files', file)
72-
})
73-
74-
const { stream } = await postAbortableStream(`/rag/indices/${index.id}/upload`, formData)
75-
if (!stream) {
76-
throw new Error('No stream returned from server')
77-
}
78-
79-
return stream
80-
},
81-
})
82-
return mutation
83-
}
84-
85-
const VisuallyHiddenInput = styled('input')({
86-
clip: 'rect(0 0 0 0)',
87-
clipPath: 'inset(50%)',
88-
height: 1,
89-
overflow: 'hidden',
90-
position: 'absolute',
91-
bottom: 0,
92-
left: 0,
93-
whiteSpace: 'nowrap',
94-
width: 1,
95-
})
96-
9755
const Rag: React.FC = () => {
9856
const { enqueueSnackbar } = useSnackbar()
57+
const navigate = useNavigate()
9958
const { data: indices, refetch } = useRagIndices()
10059
const createIndexMutation = useCreateRagIndexMutation()
101-
const deleteIndexMutation = useDeleteRagIndexMutation()
10260
const [indexName, setIndexName] = useState('')
10361
const [selectedIndex, setSelectedIndex] = useState<RagIndexAttributes>(null)
10462
const [inputValue, setInputValue] = useState('')
10563
const [topK, setTopK] = useState(5)
10664
const [response, setResponse] = useState<RagResponse[] | null>(null)
107-
const uploadMutation = useUploadMutation(selectedIndex)
108-
const [modalOpen, setModalOpen] = useState(false)
109-
const [stream, setStream] = useState<ReadableStream | null>(null)
110-
const [filenames, setFilenames] = useState<string[]>([])
11165

11266
const handleSubmit = async (event: React.FormEvent) => {
11367
event.preventDefault()
@@ -130,51 +84,8 @@ const Rag: React.FC = () => {
13084
setInputValue('')
13185
}
13286

133-
const handleUploadError = () => {
134-
setStream(null)
135-
}
136-
13787
return (
13888
<Box sx={{ display: 'flex', gap: 2 }}>
139-
<Dialog open={!!selectedIndex && modalOpen} onClose={() => { setModalOpen(false); refetch(); }} fullWidth maxWidth="md">
140-
<DialogTitle>Edit {selectedIndex?.metadata?.name}</DialogTitle>
141-
<Box sx={{ padding: 2 }}>
142-
<Box sx={{ display: 'flex', gap: 2 }}>
143-
<Button component="label" role={undefined} variant="contained" tabIndex={-1} startIcon={<CloudUpload />} disabled={uploadMutation.isPending}>
144-
{uploadMutation.isPending ? 'Uploading...' : 'Upload Files'}
145-
<VisuallyHiddenInput
146-
type="file"
147-
onChange={async (event) => {
148-
const files = event.target.files
149-
console.log('Files selected:', files)
150-
if (files && files.length > 0) {
151-
const stream = await uploadMutation.mutateAsync(files)
152-
setFilenames(Array.from(files).map((file) => file.name))
153-
setStream(stream)
154-
}
155-
}}
156-
multiple
157-
/>
158-
</Button>
159-
<Button
160-
variant="text"
161-
color="error"
162-
onClick={async () => {
163-
if (selectedIndex && window.confirm(`Are you sure you want to delete index ${selectedIndex.metadata.name}?`)) {
164-
await deleteIndexMutation.mutateAsync(selectedIndex.id)
165-
setSelectedIndex(null)
166-
refetch()
167-
}
168-
}}
169-
>
170-
Delete Index
171-
</Button>
172-
</Box>
173-
<Box mt={2}>
174-
<ProgressReporter filenames={filenames} stream={stream} onError={handleUploadError} />
175-
</Box>
176-
</Box>
177-
</Dialog>
17889
<Box>
17990
<Typography variant="h4" mb="1rem">
18091
RAG Indices
@@ -185,9 +96,9 @@ const Rag: React.FC = () => {
18596
variant="contained"
18697
color="primary"
18798
onClick={async () => {
188-
await createIndexMutation.mutateAsync(indexName)
99+
const newIndex = await createIndexMutation.mutateAsync(indexName)
189100
setIndexName('')
190-
refetch()
101+
navigate(`/rag/${newIndex.id}`)
191102
}}
192103
>
193104
Create Index
@@ -208,40 +119,23 @@ const Rag: React.FC = () => {
208119
<TableRow>
209120
<TableCell>ID</TableCell>
210121
<TableCell>Name</TableCell>
211-
<TableCell>Files</TableCell>
212-
<TableCell>Dim</TableCell>
213-
<TableCell>Num chunks</TableCell>
122+
<TableCell>Vector Dimensions</TableCell>
123+
<TableCell>Number of files</TableCell>
214124
</TableRow>
215125
</TableHead>
216126
<TableBody>
217127
<TableRow>
218128
<TableCell>{index.id}</TableCell>
219-
<TableCell>{index.metadata.name}</TableCell>
220-
<TableCell>
221-
{index.filenames.length ? (
222-
<>
223-
{index.filenames.map((filename) => (
224-
<p key={filename}>{filename}</p>
225-
))}
226-
</>
227-
) : (
228-
'No files'
229-
)}
230-
</TableCell>
231-
<TableCell>{index.metadata.dim}</TableCell>
232-
<TableCell>{index.numOfChunks}</TableCell>
129+
<TableCell>{index.metadata?.name}</TableCell>
130+
<TableCell>{index.metadata?.dim}</TableCell>
131+
<TableCell>{index.ragFileCount}</TableCell>
233132
</TableRow>
234133
</TableBody>
235134
</Table>
236135
<Button disabled={selectedIndex?.id === index.id} onClick={() => setSelectedIndex(index)}>
237136
{selectedIndex?.id === index.id ? 'Selected' : 'Select'}
238137
</Button>
239-
<IconButton
240-
onClick={() => {
241-
setSelectedIndex(index)
242-
setModalOpen(true)
243-
}}
244-
>
138+
<IconButton href={`rag/${index.id}`}>
245139
<Settings />
246140
</IconButton>
247141
</Paper>

0 commit comments

Comments
 (0)