Skip to content

Commit 98fed36

Browse files
committed
add prevResponesId end-to-end flow
1 parent 30b80d1 commit 98fed36

File tree

6 files changed

+93
-27
lines changed

6 files changed

+93
-27
lines changed

src/client/components/ChatV2/ChatBox.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ export const ChatBox = ({ disabled, onSubmit }: { disabled: boolean; onSubmit: (
1919
refetchStatus()
2020
}
2121
}
22-
useEffect(() => {
23-
console.log('userStatus', userStatus)
24-
})
22+
23+
// Work in progress from Matias. Commented by Ben
24+
// useEffect(() => {
25+
// console.log('userStatus', userStatus)
26+
// })
2527

2628
if (statusLoading) {
2729
return <p>loading</p>

src/client/components/ChatV2/ChatV2.tsx

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import useLocalStorageState from '../../hooks/useLocalStorageState'
66
import { DEFAULT_MODEL } from '../../../config'
77
import useInfoTexts from '../../hooks/useInfoTexts'
88
import { Message } from '../../types'
9+
import { ResponseStreamValue } from '../../../shared/types'
910
import useRetryTimeout from '../../hooks/useRetryTimeout'
1011
import { useTranslation } from 'react-i18next'
1112
import { handleCompletionStreamError } from './error'
@@ -33,6 +34,9 @@ export const ChatV2 = () => {
3334
const [system, setSystem] = useLocalStorageState<{ content: string }>('general-chat-system', { content: '' })
3435
const [message, setMessage] = useLocalStorageState<{ content: string }>('general-chat-current', { content: '' })
3536
const [messages, setMessages] = useLocalStorageState<Message[]>('general-chat-messages', [])
37+
const [prevResponse, setPrevResponse] = useLocalStorageState<{
38+
id: string
39+
}>('general-prev-response', { id: '' })
3640

3741
const appContainerRef = useContext(AppContext)
3842
const chatContainerRef = useRef<HTMLDivElement>(null)
@@ -55,21 +59,43 @@ export const ChatV2 = () => {
5559
const disclaimerInfo = infoTexts?.find((infoText) => infoText.name === 'disclaimer')?.text[language] ?? null
5660
const systemMessageInfo = infoTexts?.find((infoText) => infoText.name === 'systemMessage')?.text[language] ?? null
5761

62+
const decoder = new TextDecoder()
63+
5864
const processStream = async (stream: ReadableStream) => {
5965
try {
6066
const reader = stream.getReader()
6167

6268
let content = ''
63-
const decoder = new TextDecoder()
6469

6570
while (true) {
6671
const { value, done } = await reader.read()
6772
if (done) break
6873

69-
const text = decoder.decode(value)
70-
setCompletion((prev) => prev + text)
71-
content += text
72-
console.log(text)
74+
const data = decoder.decode(value)
75+
76+
for (const chunk of data.split('\n')) {
77+
if (!chunk || chunk.trim().length === 0) continue
78+
79+
const parsedChunk: ResponseStreamValue = JSON.parse(chunk)
80+
81+
switch (parsedChunk.status) {
82+
case 'writing':
83+
setCompletion((prev) => prev + parsedChunk.text)
84+
content += parsedChunk.text
85+
break
86+
87+
case 'complete':
88+
setPrevResponse({ id: parsedChunk.prevResponseId })
89+
break
90+
91+
case 'error':
92+
console.error('Somehing went wrong when streaming responses')
93+
break
94+
95+
default:
96+
break
97+
}
98+
}
7399
}
74100

75101
setMessages((prev: Message[]) => prev.concat({ role: 'assistant', content }))
@@ -89,6 +115,7 @@ export const ChatV2 = () => {
89115
const newMessages = messages.concat({ role: 'user', content: message })
90116
setMessages(newMessages)
91117
setMessage({ content: '' })
118+
setPrevResponse({ id: '' })
92119
setCompletion('')
93120
setStreamController(new AbortController())
94121
setRetryTimeout(() => {
@@ -108,6 +135,7 @@ export const ChatV2 = () => {
108135
courseId,
109136
abortController: streamController,
110137
saveConsent,
138+
prevResponseId: prevResponse.id,
111139
})
112140

113141
if (tokenUsageAnalysis && tokenUsageAnalysis.message) {

src/client/components/ChatV2/util.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface GetCompletoinStreamProps {
99
userConsent: boolean
1010
modelTemperature: number
1111
courseId?: string
12+
prevResponseId?: string
1213
abortController?: AbortController
1314
saveConsent: boolean
1415
}
@@ -20,6 +21,7 @@ export const getCompletionStream = async ({
2021
userConsent,
2122
modelTemperature,
2223
courseId,
24+
prevResponseId,
2325
abortController,
2426
saveConsent,
2527
}: GetCompletoinStreamProps) => {
@@ -37,6 +39,7 @@ export const getCompletionStream = async ({
3739
userConsent,
3840
modelTemperature,
3941
saveConsent,
42+
prevResponseId,
4043
},
4144
}
4245

@@ -54,8 +57,9 @@ interface GetCourseCompletionStreamProps {
5457
model: string
5558
courseId: string
5659
abortController?: AbortController
60+
prevResponseId?: string
5761
}
58-
export const getCourseCompletionStream = async ({ id, system, messages, model, courseId, abortController }: GetCourseCompletionStreamProps) => {
62+
export const getCourseCompletionStream = async ({ id, system, messages, model, courseId, abortController, prevResponseId }: GetCourseCompletionStreamProps) => {
5963
const data = {
6064
id,
6165
courseId,
@@ -68,6 +72,7 @@ export const getCourseCompletionStream = async ({ id, system, messages, model, c
6872
...messages,
6973
],
7074
model,
75+
prevResponseId,
7176
},
7277
}
7378
const formData = new FormData()

src/server/routes/openai.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ openaiRouter.post('/stream/:version?', upload.single('file'), async (r, res) =>
106106

107107
let events
108108
if (version === 'v2') {
109-
events = await responsesClient.createResponse({ input: options.messages })
109+
const latestMessage = options.messages[options.messages.length - 1] // Adhoc to input only the latest message
110+
events = await responsesClient.createResponse({ input: [latestMessage], prevResponseId: options.prevResponseId })
110111
} else {
111112
events = await getCompletionEvents(options)
112113
}
@@ -224,7 +225,8 @@ openaiRouter.post('/stream/:courseId/:version?', upload.single('file'), async (r
224225

225226
let events
226227
if (version === 'v2') {
227-
events = await responsesClient.createResponse({ input: options.messages })
228+
const latestMessage = options.messages[options.messages.length - 1] // Adhoc to input only the latest message
229+
events = await responsesClient.createResponse({ input: [latestMessage], prevResponseId: options.prevResponseId })
228230
} else {
229231
events = await getCompletionEvents(options)
230232
}

src/server/util/azure/ResponsesAPI.ts

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { FileSearchTool, FunctionTool, ResponseInput, ResponseInputItem, Respons
1616
import { courseAssistants, type CourseAssistant } from './courseAssistants'
1717
import { createFileSearchTool } from './util'
1818

19+
import type { ResponseStreamValue } from '../../../shared/types'
20+
1921
const endpoint = `https://${AZURE_RESOURCE}.openai.azure.com/`
2022

2123
export const getAzureOpenAIClient = (deployment: string) =>
@@ -48,9 +50,6 @@ export class ResponsesClient {
4850
courseAssistant = courseAssistants.find((assistant) => assistant.name === 'default')
4951
}
5052

51-
this.model = deploymentId
52-
this.instructions = courseAssistant.assistant_instruction
53-
5453
const fileSearchTool = courseId
5554
? [
5655
createFileSearchTool({
@@ -59,14 +58,16 @@ export class ResponsesClient {
5958
]
6059
: [] // needs to retrun empty array for null
6160

61+
this.model = deploymentId
62+
this.instructions = courseAssistant.assistant_instruction
6263
this.tools = fileSearchTool
6364
}
6465

65-
async createResponse({ input }: { input: ResponseInput }): Promise<Stream<ResponseStreamEvent> | APIError> {
66+
async createResponse({ input, prevResponseId }: { input: ResponseInput; prevResponseId?: string }): Promise<Stream<ResponseStreamEvent> | APIError> {
6667
try {
6768
return await client.responses.create({
6869
model: this.model,
69-
// previous_response_id=response.id // THIS MIGHT BE IT!!!!!!1
70+
previous_response_id: prevResponseId,
7071
instructions: this.instructions,
7172
input,
7273
stream: true,
@@ -89,7 +90,14 @@ export class ResponsesClient {
8990

9091
switch (event.type) {
9192
case 'response.output_text.delta':
92-
await this.writeDelta(event.delta, res)
93+
await this.write(
94+
{
95+
status: 'writing',
96+
text: event.delta,
97+
prevResponseId: null,
98+
},
99+
res,
100+
)
93101

94102
contents.push(event.delta)
95103
tokenCount += encoding.encode(event.delta).length ?? 0
@@ -107,8 +115,15 @@ export class ResponsesClient {
107115
console.log('ANNOTATIONS ADDED', JSON.stringify(event, null, 2))
108116
break
109117

110-
case 'response.function_call_arguments.done':
111-
// Listen to file_search instead
118+
case 'response.completed':
119+
await this.write(
120+
{
121+
status: 'complete',
122+
text: null,
123+
prevResponseId: event.response.id,
124+
},
125+
res,
126+
)
112127
break
113128
}
114129
}
@@ -119,16 +134,24 @@ export class ResponsesClient {
119134
}
120135
}
121136

122-
private async writeDelta(text: string, res: Response) {
123-
// if (!inProduction) logger.info(text)
137+
private async write({ status, text, prevResponseId }: ResponseStreamValue, res: Response) {
138+
// if (!inProduction) logger.info(message)
124139

125140
await new Promise((resolve) => {
126-
if (
127-
!res.write(text, (err) => {
128-
if (err) logger.error(`${text} ${err}`)
129-
})
130-
) {
131-
logger.info(`${text} res.write returned false, waiting for drain`)
141+
const data: ResponseStreamValue = {
142+
status,
143+
text,
144+
prevResponseId: prevResponseId,
145+
}
146+
147+
const success = res.write(JSON.stringify(data) + '\n', (err) => {
148+
if (err) {
149+
logger.error(err)
150+
}
151+
})
152+
153+
if (!success) {
154+
logger.info('res.write returned false, waiting for drain')
132155
res.once('drain', resolve)
133156
} else {
134157
process.nextTick(resolve)

src/shared/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,9 @@ export type RagIndexMetadata = {
22
name: string
33
dim: number
44
}
5+
6+
export type ResponseStreamValue = {
7+
status: 'writing' | 'complete' | 'error'
8+
text: string | null
9+
prevResponseId: string | null
10+
}

0 commit comments

Comments
 (0)