Skip to content

Commit f206b8b

Browse files
committed
file citation mayheming
1 parent 8e45fe2 commit f206b8b

File tree

10 files changed

+191
-71
lines changed

10 files changed

+191
-71
lines changed

chunkingTest.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

package-lock.json

Lines changed: 34 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client/components/ChatV2/CitationsBox.tsx

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,85 @@
11
import { Box, Paper, Typography } from '@mui/material'
2-
import { FileCitation } from '../../../shared/types'
2+
import { CourseAssistant, FileCitation } from '../../../shared/types'
33
import { Message } from '../../types'
4+
import { useQuery } from '@tanstack/react-query'
5+
import { useCourseAssistant } from '../../hooks/useCourseAssistant'
6+
import { useParams } from 'react-router-dom'
7+
8+
const useFileCitationText = (citation: FileCitation, vectorStoreId: string) => {
9+
const { data, ...rest } = useQuery({
10+
queryKey: ['file-citation-content', vectorStoreId, citation.file_id],
11+
queryFn: async () => {
12+
const response = await fetch(`/api/files/${vectorStoreId}/${citation.file_id}`)
13+
if (!response.ok) {
14+
throw new Error('Failed to fetch file text')
15+
}
16+
return response.text()
17+
},
18+
retry: false,
19+
})
20+
21+
return {
22+
text: data,
23+
...rest,
24+
}
25+
}
26+
27+
const Citation = ({ citation, courseAssistant }: { citation: FileCitation, courseAssistant: CourseAssistant }) => {
28+
const { text, isLoading, error } = useFileCitationText(citation, courseAssistant.vector_store_id)
429

5-
const Citation = ({ citation }: { citation: FileCitation }) => {
630
return (
731
<Box>
832
<Typography variant="body2" color="textSecondary">
933
{citation.filename} (Index: {citation.index})
1034
</Typography>
35+
{isLoading ? (
36+
<Typography variant="body2" color="textSecondary">
37+
Loading citation text...
38+
</Typography>
39+
) : error ? (
40+
<Typography variant="body2" color="error">
41+
Error loading citation text: {error.message}
42+
</Typography>
43+
) : (
44+
<Typography variant="body2">{text}</Typography>
45+
)}
1146
</Box>
1247
)
1348
}
1449

15-
const MessageCitations = ({ citations }: { citations: FileCitation[] }) => {
50+
const MessageCitations = ({ citations, courseAssistant }: { citations: FileCitation[]; courseAssistant: CourseAssistant }) => {
1651
return (
1752
<Box sx={{ mt: 1, fontSize: '0.875rem', color: 'gray' }}>
1853
Citations:
1954
{citations.map((citation, index) => (
20-
<Citation key={index} citation={citation} />
55+
<Citation key={index} citation={citation} courseAssistant={courseAssistant} />
2156
))}
2257
</Box>
2358
)
2459
}
2560

2661
export const CitationsBox = ({ messages, citations }: { messages: Message[]; citations: FileCitation[] }) => {
62+
const { courseId } = useParams()
63+
const { courseAssistant, isLoading } = useCourseAssistant(courseId)
64+
2765
const messageCitations = [...messages.map((message) => (Array.isArray(message.citations) ? message.citations : [])), citations]
2866

2967
console.log('CitationsBox messageCitations', messageCitations)
3068

3169
return (
3270
<Paper>
3371
<Box sx={{ width: 300, padding: 2 }}>
34-
{messageCitations.map((c, index) => (
35-
<MessageCitations key={index} citations={c} />
36-
))}
72+
{courseAssistant ? (
73+
messageCitations.map((c, index) => <MessageCitations key={index} citations={c} courseAssistant={courseAssistant} />)
74+
) : isLoading ? (
75+
<Typography variant="body2" color="textSecondary">
76+
Loading course assistant...
77+
</Typography>
78+
) : (
79+
<Typography variant="body2" color="error">
80+
No course assistant found for this course.
81+
</Typography>
82+
)}
3783
</Box>
3884
</Paper>
3985
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useQuery } from '@tanstack/react-query'
2+
import { CourseAssistant } from '../../shared/types'
3+
4+
export const useCourseAssistant = (courseId: string | null) => {
5+
const queryKey = ['courseAssistant', courseId]
6+
7+
const { data: courseAssistant, ...rest } = useQuery<CourseAssistant>({
8+
queryKey,
9+
queryFn: async () => {
10+
const response = await fetch(`/api/assistants/for-course/${courseId}`)
11+
if (!response.ok) {
12+
throw new Error('Failed to fetch course assistant')
13+
}
14+
return response.json()
15+
},
16+
enabled: !!courseId,
17+
retry: false,
18+
})
19+
20+
return {
21+
courseAssistant,
22+
...rest,
23+
}
24+
}

src/server/routes/assistants.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Router } from 'express'
2+
import { courseAssistants } from '../util/azure/courseAssistants'
3+
4+
const router = Router()
5+
6+
router.get('/for-course/:courseId', async (req, res) => {
7+
const { courseId } = req.params
8+
9+
const courseAssistant = courseAssistants.find((assistant) => assistant.course_id === courseId)
10+
11+
if (!courseAssistant) {
12+
res.status(404).send('Course assistant not found')
13+
return
14+
}
15+
16+
res.send(courseAssistant)
17+
})
18+
19+
export default router

src/server/routes/files.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Router } from 'express'
2+
import { getAzureOpenAIClient } from '../util/azure/client'
3+
import { z } from 'zod'
4+
5+
const router = Router()
6+
7+
const GetFileParamsSchema = z.object({
8+
vectorStoreId: z.string(),
9+
fileId: z.string(),
10+
})
11+
12+
router.get('/:vectorStoreId/:fileId', async (req, res) => {
13+
const { vectorStoreId, fileId } = GetFileParamsSchema.parse(req.params)
14+
const client = getAzureOpenAIClient('curredev4omini')
15+
16+
const asd = await client.vectorStores.files.retrieve(vectorStoreId, fileId)
17+
console.log('asd', asd)
18+
if (!asd) {
19+
res.status(404).send('File not found')
20+
return
21+
}
22+
23+
const fileRes = await client.vectorStores.files.content(vectorStoreId, fileId)
24+
console.log('fileRes', fileRes)
25+
26+
const content = fileRes.data?.[0]?.text
27+
28+
if (!content) {
29+
res.status(404).send('File not found or empty')
30+
return
31+
}
32+
33+
res.send(content)
34+
})
35+
36+
export default router

src/server/routes/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import emailRouter from './email'
1717
import adminRouter from './admin'
1818
import facultyRouter from './faculty'
1919
import infoTextRouter from './infotext'
20+
import filesRouter from './files'
21+
import assistantRouter from './assistants'
2022

2123
const router = express()
2224

@@ -36,6 +38,8 @@ router.get('/ping', (_, res) => {
3638

3739
router.use('/ai', openaiRouter)
3840
router.use('/rag', ragRouter)
41+
router.use('/files', filesRouter)
42+
router.use('/assistants', assistantRouter)
3943
router.use('/users', userRouter)
4044
router.use('/chatinstances', chatInstancesRouter)
4145
router.use('/courses', courseRouter)

src/server/util/azure/ResponsesAPI.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import { AzureOpenAI } from 'openai'
1313
import { Stream } from 'openai/streaming'
1414
import { FileSearchTool, FunctionTool, ResponseInput, ResponseInputItem, ResponseStreamEvent, ResponseTextAnnotationDeltaEvent } from 'openai/resources/responses/responses'
1515

16-
import { courseAssistants, type CourseAssistant } from './courseAssistants'
16+
import { courseAssistants } from './courseAssistants'
1717
import { createFileSearchTool } from './util'
1818

19-
import type { FileCitation, ResponseStreamEventData } from '../../../shared/types'
19+
import type { CourseAssistant, FileCitation, ResponseStreamEventData } from '../../../shared/types'
2020

2121
const endpoint = `https://${AZURE_RESOURCE}.openai.azure.com/`
2222

@@ -28,38 +28,37 @@ export const getAzureOpenAIClient = (deployment: string) =>
2828
endpoint,
2929
})
3030

31-
const client = getAzureOpenAIClient(process.env.GPT_4O)
31+
const client = getAzureOpenAIClient(process.env.GPT_4O_MINI)
3232

3333
export class ResponsesClient {
3434
model: string
3535
instructions: string
3636
tools: FileSearchTool[]
37+
courseAssistant: CourseAssistant
3738

3839
constructor({ model, courseId }: { model: string; courseId?: string }) {
3940
const deploymentId = validModels.find((m) => m.name === model)?.deployment
4041

4142
if (!deploymentId) throw new Error(`Invalid model: ${model}, not one of ${validModels.map((m) => m.name).join(', ')}`)
4243

43-
let courseAssistant: CourseAssistant
44-
4544
if (courseId) {
46-
courseAssistant = courseAssistants.find((assistant) => assistant.course_id === courseId)
45+
this.courseAssistant = courseAssistants.find((assistant) => assistant.course_id === courseId)
4746

48-
if (!courseAssistant) throw new Error(`No course assistant found for course ID: ${courseId}`)
47+
if (!this.courseAssistant) throw new Error(`No course assistant found for course ID: ${courseId}`)
4948
} else {
50-
courseAssistant = courseAssistants.find((assistant) => assistant.name === 'default')
49+
this.courseAssistant = courseAssistants.find((assistant) => assistant.name === 'default')
5150
}
5251

5352
const fileSearchTool = courseId
5453
? [
5554
createFileSearchTool({
56-
vectorStoreId: courseAssistant.vector_store_id,
55+
vectorStoreId: this.courseAssistant.vector_store_id,
5756
}),
5857
]
5958
: [] // needs to retrun empty array for null
6059

6160
this.model = deploymentId
62-
this.instructions = courseAssistant.assistant_instruction
61+
this.instructions = this.courseAssistant.assistant_instruction
6362
this.tools = fileSearchTool
6463
}
6564

src/server/util/azure/courseAssistants.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
export interface CourseAssistant {
2-
course_id: string | null
3-
name: string
4-
assistant_instruction: string
5-
vector_store_id: string | null
6-
}
1+
import { CourseAssistant } from "../../../shared/types";
72

83
export const courseAssistants = [
94
{

0 commit comments

Comments
 (0)