Skip to content

Commit cfcf511

Browse files
committed
go to the cloud with rag management
1 parent 90f263c commit cfcf511

File tree

5 files changed

+91
-48
lines changed

5 files changed

+91
-48
lines changed

src/client/components/Rag/RagFile.tsx

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@ import type { RagIndexAttributes } from '../../../server/db/models/ragIndex'
88
import { Chunk } from './Chunk'
99

1010
type RagFile = RagFileAttributes & {
11-
chunks: {
12-
id: string
13-
content: string
14-
title: string
15-
metadata: Record<string, unknown>
16-
}[]
11+
fileContent: string
1712
ragIndex: RagIndexAttributes
1813
}
1914

@@ -41,20 +36,17 @@ export const RagFile: React.FC = () => {
4136
{ragFile.ragIndex.metadata.name} / {ragFile.filename}
4237
</Typography>
4338
<RagFileInfo file={ragFile} />
44-
<Typography variant="h4">Chunks</Typography>
45-
{ragFile.chunks.length === 0 ? (
46-
<Typography variant="body1">No chunks found</Typography>
39+
<Typography variant="h4">Content</Typography>
40+
{ragFile.fileContent.length === 0 ? (
41+
<Typography variant="body1">No content</Typography>
4742
) : (
48-
ragFile.chunks.map((chunk) => (
49-
<Chunk
50-
key={chunk.id}
51-
doc={{
52-
title: chunk.title,
53-
content: chunk.content,
54-
metadata: chunk.metadata,
55-
}}
56-
/>
57-
))
43+
<Chunk
44+
doc={{
45+
title: ragFile.filename,
46+
content: ragFile.fileContent,
47+
metadata: ragFile.metadata,
48+
}}
49+
/>
5850
)}
5951
</>
6052
)

src/client/components/Rag/RagIndex.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@ type RagIndexDetails = Omit<RagIndexAttributes, 'ragFileCount'> & {
1313
ragFiles: RagFileAttributes[]
1414
}
1515

16-
const useRagIndexDetails = (indexId: number | null, refetchInterval: number) => {
16+
const useRagIndexDetails = (indexId: number | null) => {
1717
const { data, ...rest } = useQuery<RagIndexDetails>({
1818
queryKey: ['ragIndex', indexId],
1919
queryFn: async () => {
2020
const response = await apiClient.get(`/rag/indices/${indexId}`)
2121
return response.data
2222
},
2323
enabled: !!indexId,
24-
refetchInterval,
2524
})
2625

2726
return { data, ...rest }
@@ -74,7 +73,7 @@ export const RagIndex: React.FC = () => {
7473
const navigate = useNavigate()
7574
const id = parseInt(strId, 10)
7675
const deleteIndexMutation = useDeleteRagIndexMutation()
77-
const { data: ragDetails, isLoading } = useRagIndexDetails(id, 1000)
76+
const { data: ragDetails, isLoading, refetch } = useRagIndexDetails(id)
7877
const uploadMutation = useUploadMutation(ragDetails)
7978

8079
if (isLoading) {
@@ -99,6 +98,7 @@ export const RagIndex: React.FC = () => {
9998
console.log('Files selected:', files)
10099
if (files && files.length > 0) {
101100
await uploadMutation.mutateAsync(files)
101+
refetch()
102102
}
103103
}}
104104
multiple

src/server/routes/rag.ts

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1+
import fs from 'fs'
12
import { NextFunction, Request, Response, Router } from 'express'
23
import { EMBED_DIM } from '../../config'
3-
import { createChunkIndex, deleteChunkIndex, getRagFileChunks } from '../services/rag/chunkDb'
44
import { RagFile, RagIndex } from '../db/models'
55
import { RequestWithUser } from '../types'
66
import z from 'zod'
77
import { queryRagIndex } from '../services/rag/query'
8-
import { ingestionPipeline } from '../services/rag/ingestion/pipeline'
98
import multer from 'multer'
109
import { mkdir, rm, stat } from 'fs/promises'
11-
import { getOllamaOpenAIClient } from '../util/ollama'
10+
import { getAzureOpenAIClient } from '../util/azure/client'
1211

1312
const router = Router()
1413

@@ -23,16 +22,20 @@ router.post('/indices', async (req, res) => {
2322
const { user } = req as RequestWithUser
2423
const { name, dim } = IndexCreationSchema.parse(req.body)
2524

25+
const client = getAzureOpenAIClient('curredev4omini')
26+
const vectorStore = await client.vectorStores.create({
27+
name,
28+
})
29+
2630
const ragIndex = await RagIndex.create({
2731
userId: user.id,
2832
metadata: {
2933
name,
3034
dim,
35+
azureVectorStoreId: vectorStore.id,
3136
},
3237
})
3338

34-
await createChunkIndex(ragIndex)
35-
3639
// Create upload directory for this index
3740

3841
res.json(ragIndex)
@@ -51,7 +54,14 @@ router.delete('/indices/:id', async (req, res) => {
5154
return
5255
}
5356

54-
await deleteChunkIndex(ragIndex)
57+
const client = getAzureOpenAIClient('curredev4omini')
58+
try {
59+
await client.vectorStores.del(ragIndex.metadata.azureVectorStoreId)
60+
} catch (error) {
61+
console.error(`Failed to delete Azure vector store ${ragIndex.metadata.azureVectorStoreId}:`, error)
62+
res.status(500).json({ error: 'Failed to delete Azure vector store' })
63+
return
64+
}
5565

5666
const uploadPath = `${UPLOAD_DIR}/${id}`
5767
try {
@@ -77,11 +87,14 @@ router.get('/indices', async (_req, res) => {
7787
],
7888
})
7989

90+
const client = getAzureOpenAIClient('curredev4omini')
91+
8092
// Add ragFileCount to each index
8193
const indicesWithCount = await Promise.all(
8294
indices.map(async (index: any) => {
95+
const vectorStore = await client.vectorStores.retrieve(index.metadata.azureVectorStoreId)
8396
const count = await RagFile.count({ where: { ragIndexId: index.id } })
84-
return { ...index.toJSON(), ragFileCount: count }
97+
return { ...index.toJSON(), ragFileCount: count, vectorStore }
8598
}),
8699
)
87100

@@ -107,7 +120,13 @@ router.get('/indices/:id', async (req, res) => {
107120
return
108121
}
109122

110-
res.json(ragIndex)
123+
const client = getAzureOpenAIClient('curredev4omini')
124+
const vectorStore = await client.vectorStores.retrieve(ragIndex.metadata.azureVectorStoreId)
125+
126+
res.json({
127+
...ragIndex.toJSON(),
128+
vectorStore,
129+
})
111130
})
112131

113132
router.get('/indices/:id/files/:fileId', async (req, res) => {
@@ -129,11 +148,20 @@ router.get('/indices/:id/files/:fileId', async (req, res) => {
129148
return
130149
}
131150

132-
const chunks = await getRagFileChunks(ragFile.ragIndex, ragFile)
151+
// Read file content
152+
const filePath = `${UPLOAD_DIR}/${indexId}/${ragFile.filename}`
153+
let fileContent: string
154+
try {
155+
fileContent = await fs.promises.readFile(filePath, 'utf-8')
156+
} catch (error) {
157+
console.error(`Failed to read file ${filePath}:`, error)
158+
res.status(500).json({ error: 'Failed to read file content' })
159+
return
160+
}
133161

134162
res.json({
135163
...ragFile.toJSON(),
136-
chunks,
164+
fileContent,
137165
})
138166
})
139167

@@ -160,9 +188,7 @@ const indexUploadDirMiddleware = async (req: Request, _res: Response, next: Next
160188
const uploadPath = `${UPLOAD_DIR}/${id}`
161189
try {
162190
await stat(uploadPath)
163-
await rm(uploadPath, { recursive: true, force: true })
164-
console.log(`Upload directory ${uploadPath} deleted`)
165-
await mkdir(uploadPath, { recursive: true })
191+
console.log(`RAG upload dir exists: ${uploadPath}`)
166192
} catch (error) {
167193
console.warn(`RAG upload dir not found, creating ${uploadPath} --- ${error}`)
168194
await mkdir(uploadPath, { recursive: true })
@@ -187,23 +213,44 @@ router.post('/indices/:id/upload', [indexUploadDirMiddleware, uploadMiddleware],
187213
return
188214
}
189215

190-
await RagFile.bulkCreate(
191-
req.files.map((file) => ({
192-
filename: file.originalname,
193-
fileType: 'pending',
194-
fileSize: 0,
195-
numChunks: 0,
196-
userId: user.id,
197-
ragIndexId: ragIndex.id,
198-
pipelineStage: 'pending',
199-
})),
216+
const ragFiles: RagFile[] = await Promise.all(
217+
req.files.map((file: Express.Multer.File) =>
218+
RagFile.create({
219+
userId: user.id,
220+
ragIndexId: ragIndex.id,
221+
pipelineStage: 'upload',
222+
filename: file.originalname,
223+
fileType: file.mimetype,
224+
fileSize: file.size,
225+
metadata: {},
226+
}),
227+
),
200228
)
201229

202-
const openAiClient = getOllamaOpenAIClient() // getAzureOpenAIClient(EMBED_MODEL)
230+
const client = getAzureOpenAIClient('curredev4omini')
231+
232+
const uploadDirPath = `${UPLOAD_DIR}/${id}`
203233

204-
ingestionPipeline(openAiClient, `uploads/rag/${id}`, ragIndex, user)
234+
const fileStreams = req.files.map((file: Express.Multer.File) => {
235+
const filePath = `${uploadDirPath}/${file.originalname}`
236+
return fs.createReadStream(filePath)
237+
})
238+
239+
const fileBatchRes = await client.vectorStores.fileBatches.uploadAndPoll(ragIndex.metadata.azureVectorStoreId, {
240+
files: fileStreams,
241+
})
242+
243+
console.log('File batch upload response:', fileBatchRes)
244+
245+
await Promise.all(
246+
ragFiles.map(async (ragFile) =>
247+
ragFile.update({
248+
pipelineStage: 'completed',
249+
}),
250+
),
251+
)
205252

206-
res.json({ message: 'Files uploaded successfully, starting ingestion' })
253+
res.json({ message: 'Files uploaded successfully' })
207254
})
208255

209256
const RagIndexQuerySchema = z.object({

src/shared/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ export const IngestionPipelineStages = {
22
pending: {
33
name: 'Pending',
44
},
5+
upload: {
6+
name: 'Uploading',
7+
},
58
readFiles: {
69
name: 'Read files',
710
},

src/shared/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export type RagIndexMetadata = {
22
name: string
33
dim: number
4+
azureVectorStoreId: string
45
}
56

67
export type FileCitation = {

0 commit comments

Comments
 (0)