|
1 | 1 | import { useState } from 'react'
|
2 | 2 | import { RagIndexAttributes } from '../../../shared/types'
|
3 |
| -import { RagChunk } from '../../../shared/rag' |
| 3 | +import { RagChunk, SearchInputParams, SearchParams } from '../../../shared/rag' |
4 | 4 | import apiClient from '../../util/apiClient'
|
| 5 | +import { Box, Checkbox, Collapse, Fade, FormControl, FormControlLabel, Grow, LinearProgress, TextField, Typography, Zoom } from '@mui/material' |
| 6 | +import { OutlineButtonBlue } from '../ChatV2/general/Buttons' |
| 7 | +import { amber, green, blue } from '@mui/material/colors' |
| 8 | + |
| 9 | +const TimeLineColors = [blue[200], amber[300], green[300]] |
5 | 10 |
|
6 | 11 | export const Search = ({ ragIndex }: { ragIndex: RagIndexAttributes }) => {
|
7 | 12 | const [query, setQuery] = useState('')
|
8 |
| - const [results, setResults] = useState<RagChunk[]>([]) |
| 13 | + const [vector, setVector] = useState(true) |
| 14 | + const [ft, setFt] = useState(true) |
| 15 | + const [rerank, setRerank] = useState(true) |
| 16 | + const [isLoading, setIsLoading] = useState(false) |
| 17 | + const [results, setResults] = useState<{ results: RagChunk[]; timings: Record<string, number> }>() |
9 | 18 |
|
10 | 19 | const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
11 | 20 | event.preventDefault()
|
12 |
| - const response = await apiClient.post<RagChunk[]>(`/rag/indices/${ragIndex.id}/search`, { |
| 21 | + const searchParams: SearchInputParams = { |
13 | 22 | query,
|
14 |
| - }) |
| 23 | + vector, |
| 24 | + ft, |
| 25 | + rerank, |
| 26 | + } |
| 27 | + setIsLoading(true) |
| 28 | + setResults(undefined) |
| 29 | + const response = await apiClient.post<{ results: RagChunk[]; timings: Record<string, number> }>(`/rag/indices/${ragIndex.id}/search`, searchParams) |
15 | 30 |
|
16 |
| - setResults(response.data ?? []) |
| 31 | + setResults(response.data) |
| 32 | + setIsLoading(false) |
17 | 33 | }
|
18 | 34 |
|
19 | 35 | const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
20 | 36 | setQuery(event.target.value)
|
21 | 37 | }
|
22 | 38 |
|
| 39 | + const totalTime = Object.values(results?.timings ?? {}).reduce((acc, curr) => acc + curr, 0) |
| 40 | + |
23 | 41 | return (
|
24 |
| - <form onSubmit={handleSubmit}> |
25 |
| - <input type="text" value={query} onChange={handleInputChange} /> |
26 |
| - <button type="submit">Search</button> |
27 |
| - <ul> |
28 |
| - {results.map((chunk) => ( |
29 |
| - <li key={chunk.id}>{chunk.content}</li> |
30 |
| - ))} |
31 |
| - </ul> |
32 |
| - </form> |
| 42 | + <Box my="2rem"> |
| 43 | + <form onSubmit={handleSubmit}> |
| 44 | + <FormControl> |
| 45 | + <TextField type="text" value={query} onChange={handleInputChange} label="Search Query" /> |
| 46 | + <FormControlLabel control={<Checkbox checked={vector} onChange={(e) => setVector(e.target.checked)} />} label="Use semantic search" /> |
| 47 | + <FormControlLabel control={<Checkbox checked={ft} onChange={(e) => setFt(e.target.checked)} />} label="Use keyword search" /> |
| 48 | + <FormControlLabel control={<Checkbox checked={rerank} onChange={(e) => setRerank(e.target.checked)} />} label="Use reranking" /> |
| 49 | + </FormControl> |
| 50 | + <OutlineButtonBlue type="submit">Search</OutlineButtonBlue> |
| 51 | + </form> |
| 52 | + {isLoading && <LinearProgress />} |
| 53 | + <Fade in={!!results?.timings}> |
| 54 | + {results?.timings ? ( |
| 55 | + <Box my="2rem"> |
| 56 | + <Typography variant="h6">Timings</Typography> |
| 57 | + <Box display="flex" width="70vw"> |
| 58 | + {Object.entries(results?.timings ?? {}).map(([key, value], idx) => ( |
| 59 | + <Zoom |
| 60 | + in={true} |
| 61 | + key={key} |
| 62 | + style={{ width: `${(value / totalTime) * 100}%`, minWidth: '1rem', whiteSpace: 'nowrap' }} |
| 63 | + timeout={{ enter: 1000 + idx * 1000 }} |
| 64 | + > |
| 65 | + <div> |
| 66 | + {key}: {value} ms |
| 67 | + <Box width="100%" height="1rem" bgcolor={TimeLineColors[idx % TimeLineColors.length]} border="1px solid black" borderRadius="1rem" /> |
| 68 | + </div> |
| 69 | + </Zoom> |
| 70 | + ))} |
| 71 | + </Box> |
| 72 | + </Box> |
| 73 | + ) : ( |
| 74 | + <div /> |
| 75 | + )} |
| 76 | + </Fade> |
| 77 | + <Fade in={!!results?.results}> |
| 78 | + {results?.results ? ( |
| 79 | + <Box my="1rem"> |
| 80 | + <Typography variant="h6" mb="1rem"> |
| 81 | + Results |
| 82 | + </Typography> |
| 83 | + <Box> |
| 84 | + {results?.results?.map((chunk) => ( |
| 85 | + <Box key={chunk.id} my="1rem"> |
| 86 | + <Typography variant="subtitle2" color="text.secondary"> |
| 87 | + Source: {chunk.metadata?.ragFileName ?? 'unknown'} |
| 88 | + </Typography> |
| 89 | + <Typography whiteSpace="pre-line" variant="body2"> |
| 90 | + {chunk.content} |
| 91 | + </Typography> |
| 92 | + </Box> |
| 93 | + ))} |
| 94 | + </Box> |
| 95 | + </Box> |
| 96 | + ) : ( |
| 97 | + <div /> |
| 98 | + )} |
| 99 | + </Fade> |
| 100 | + </Box> |
33 | 101 | )
|
34 | 102 | }
|
0 commit comments