Skip to content

Commit 7a17ac0

Browse files
committed
Merge branch 'update-azure-openai'
2 parents 9b0d09f + 629f21b commit 7a17ac0

File tree

6 files changed

+266
-78
lines changed

6 files changed

+266
-78
lines changed

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
"node-cron": "^3.0.3",
9393
"notistack": "^3.0.1",
9494
"ollama": "^0.5.15",
95-
"openai": "^4.0.0-beta.12",
95+
"openai": "^4.102.0",
9696
"pdf-parse-fork": "^1.2.0",
9797
"pg": "^8.11.3",
9898
"react": "^18.2.0",

src/client/locales/fi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"courseTerms": "Lukukaudet",
4646
"courseNameInfo": "Kurssi",
4747
"usageCount": "Käyttömäärä",
48-
"promptCount": "Kehtotteita",
48+
"promptCount": "Kehotteita",
4949
"programCodes": "Ohjelmat",
5050
"studentCount": "Opis",
5151
"courseCodes": "Kurssikoodit"

src/server/routes/openai.ts

Lines changed: 109 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
import express from 'express'
22
import multer from 'multer'
33

4-
import { CourseChatRequest, AzureOptions, RequestWithUser } from '../types'
4+
import {
5+
CourseChatRequest,
6+
AzureOptions,
7+
RequestWithUser,
8+
AzureOptionsV2,
9+
} from '../types'
510
import { isError } from '../util/parser'
6-
import { calculateUsage, incrementUsage, checkUsage, checkCourseUsage, incrementCourseUsage } from '../services/chatInstances/usage'
7-
import { getCompletionEvents, streamCompletion } from '../util/azure'
8-
import { getMessageContext, getModelContextLimit, getCourseModel, getAllowedModels } from '../util/util'
11+
import {
12+
calculateUsage,
13+
incrementUsage,
14+
checkUsage,
15+
checkCourseUsage,
16+
incrementCourseUsage,
17+
} from '../services/chatInstances/usage'
18+
// import { getCompletionEvents, streamCompletion } from '../util/azure'
19+
import { getCompletionEventsV2, streamCompletionV2 } from '../util/azureV2'
20+
import {
21+
getMessageContext,
22+
getModelContextLimit,
23+
getCourseModel,
24+
getAllowedModels,
25+
} from '../util/util'
926
import getEncoding from '../util/tiktoken'
1027
import logger from '../util/logger'
1128
import { inProduction, DEFAULT_TOKEN_LIMIT, FREE_MODEL } from '../../config'
@@ -20,7 +37,14 @@ const upload = multer({ storage })
2037
const fileParsing = async (options: any, req: any) => {
2138
let fileContent = ''
2239

23-
const textFileTypes = ['text/plain', 'text/html', 'text/css', 'text/csv', 'text/markdown', 'text/md']
40+
const textFileTypes = [
41+
'text/plain',
42+
'text/html',
43+
'text/css',
44+
'text/csv',
45+
'text/markdown',
46+
'text/md',
47+
]
2448
if (textFileTypes.includes(req.file.mimetype)) {
2549
const fileBuffer = req.file.buffer
2650
fileContent = fileBuffer.toString('utf8')
@@ -56,7 +80,9 @@ openaiRouter.post('/stream', upload.single('file'), async (r, res) => {
5680
return
5781
}
5882

59-
const usageAllowed = courseId ? await checkCourseUsage(user, courseId) : model === FREE_MODEL || (await checkUsage(user, model))
83+
const usageAllowed = courseId
84+
? await checkCourseUsage(user, courseId)
85+
: model === FREE_MODEL || (await checkUsage(user, model))
6086

6187
if (!usageAllowed) {
6288
res.status(403).send('Usage limit reached')
@@ -75,14 +101,22 @@ openaiRouter.post('/stream', upload.single('file'), async (r, res) => {
75101
return
76102
}
77103

78-
options.messages = getMessageContext(optionsMessagesWithFile || options.messages)
104+
options.messages = getMessageContext(
105+
optionsMessagesWithFile || options.messages
106+
)
79107
options.stream = true
80108

81109
const encoding = getEncoding(model)
82110
let tokenCount = calculateUsage(options, encoding)
83-
const tokenUsagePercentage = Math.round((tokenCount / DEFAULT_TOKEN_LIMIT) * 100)
84-
85-
if (model !== FREE_MODEL && tokenCount > 0.1 * DEFAULT_TOKEN_LIMIT && !userConsent) {
111+
const tokenUsagePercentage = Math.round(
112+
(tokenCount / DEFAULT_TOKEN_LIMIT) * 100
113+
)
114+
115+
if (
116+
model !== FREE_MODEL &&
117+
tokenCount > 0.1 * DEFAULT_TOKEN_LIMIT &&
118+
!userConsent
119+
) {
86120
res.status(201).json({
87121
tokenConsumtionWarning: true,
88122
message: `You are about to use ${tokenUsagePercentage}% of your monthly CurreChat usage`,
@@ -98,7 +132,7 @@ openaiRouter.post('/stream', upload.single('file'), async (r, res) => {
98132
return
99133
}
100134

101-
const events = await getCompletionEvents(options as AzureOptions)
135+
const events = await getCompletionEventsV2(options)
102136

103137
if (isError(events)) {
104138
res.status(424)
@@ -107,7 +141,7 @@ openaiRouter.post('/stream', upload.single('file'), async (r, res) => {
107141

108142
res.setHeader('content-type', 'text/event-stream')
109143

110-
const completion = await streamCompletion(events, options as AzureOptions, encoding, res)
144+
const completion = await streamCompletionV2(events, encoding, res)
111145

112146
tokenCount += completion.tokenCount
113147

@@ -135,7 +169,8 @@ openaiRouter.post('/stream', upload.single('file'), async (r, res) => {
135169
where: { courseId },
136170
}))
137171

138-
const consentToSave = courseId && course.saveDiscussions && options.saveConsent
172+
const consentToSave =
173+
courseId && course.saveDiscussions && options.saveConsent
139174

140175
console.log('consentToSave', options.saveConsent, user.username)
141176

@@ -155,79 +190,83 @@ openaiRouter.post('/stream', upload.single('file'), async (r, res) => {
155190
return
156191
})
157192

158-
openaiRouter.post('/stream/:courseId', upload.single('file'), async (r, res) => {
159-
const { courseId } = r.params
160-
const req = r as CourseChatRequest
161-
const { options } = JSON.parse(r.body.data)
162-
const { user } = req
163-
164-
if (!user.id) {
165-
res.status(401).send('Unauthorized')
166-
return
167-
}
193+
openaiRouter.post(
194+
'/stream/:courseId',
195+
upload.single('file'),
196+
async (r, res) => {
197+
const { courseId } = r.params
198+
const req = r as CourseChatRequest
199+
const { options } = JSON.parse(r.body.data)
200+
const { user } = req
201+
202+
if (!user.id) {
203+
res.status(401).send('Unauthorized')
204+
return
205+
}
168206

169-
const usageAllowed = await checkCourseUsage(user, courseId)
170-
if (!usageAllowed) {
171-
res.status(403).send('Usage limit reached')
172-
return
173-
}
207+
const usageAllowed = await checkCourseUsage(user, courseId)
208+
if (!usageAllowed) {
209+
res.status(403).send('Usage limit reached')
210+
return
211+
}
174212

175-
options.messages = getMessageContext(options.messages)
176-
options.stream = true
213+
options.messages = getMessageContext(options.messages)
214+
options.stream = true
177215

178-
const model = await getCourseModel(courseId)
216+
const model = await getCourseModel(courseId)
179217

180-
if (options.model) {
181-
const allowedModels = getAllowedModels(model)
182-
if (!allowedModels.includes(options.model)) {
183-
res.status(403).send('Model not allowed')
184-
return
218+
if (options.model) {
219+
const allowedModels = getAllowedModels(model)
220+
if (!allowedModels.includes(options.model)) {
221+
res.status(403).send('Model not allowed')
222+
return
223+
}
224+
} else {
225+
options.model = model
185226
}
186-
} else {
187-
options.model = model
188-
}
189227

190-
const encoding = getEncoding(options.model)
191-
let tokenCount = calculateUsage(options, encoding)
228+
const encoding = getEncoding(options.model)
229+
let tokenCount = calculateUsage(options, encoding)
192230

193-
const contextLimit = getModelContextLimit(options.model)
231+
const contextLimit = getModelContextLimit(options.model)
194232

195-
if (tokenCount > contextLimit) {
196-
logger.info('Maximum context reached')
197-
res.status(403).send('Model maximum context reached')
198-
return
199-
}
233+
if (tokenCount > contextLimit) {
234+
logger.info('Maximum context reached')
235+
res.status(403).send('Model maximum context reached')
236+
return
237+
}
200238

201-
const events = await getCompletionEvents(options as AzureOptions)
239+
const events = await getCompletionEventsV2(options)
202240

203-
if (isError(events)) {
204-
res.status(424).send(events)
205-
return
206-
}
241+
if (isError(events)) {
242+
res.status(424).send(events)
243+
return
244+
}
207245

208-
res.setHeader('content-type', 'text/event-stream')
246+
res.setHeader('content-type', 'text/event-stream')
209247

210-
const completion = await streamCompletion(events, options as AzureOptions, encoding, res)
248+
const completion = await streamCompletionV2(events, encoding, res)
211249

212-
tokenCount += completion.tokenCount
250+
tokenCount += completion.tokenCount
213251

214-
let userToCharge = user
215-
if (inProduction && req.hijackedBy) {
216-
userToCharge = req.hijackedBy
217-
}
252+
let userToCharge = user
253+
if (inProduction && req.hijackedBy) {
254+
userToCharge = req.hijackedBy
255+
}
218256

219-
await incrementCourseUsage(userToCharge, courseId, tokenCount)
220-
logger.info(`Stream ended. Total tokens: ${tokenCount}`, {
221-
tokenCount,
222-
courseId,
223-
model: options.model,
224-
user: user.username,
225-
})
257+
await incrementCourseUsage(userToCharge, courseId, tokenCount)
258+
logger.info(`Stream ended. Total tokens: ${tokenCount}`, {
259+
tokenCount,
260+
courseId,
261+
model: options.model,
262+
user: user.username,
263+
})
226264

227-
encoding.free()
265+
encoding.free()
228266

229-
res.end()
230-
return
231-
})
267+
res.end()
268+
return
269+
}
270+
)
232271

233272
export default openaiRouter

src/server/types.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Request } from 'express'
22
import OpenAI from 'openai'
33
import { ChatRequestMessage, GetChatCompletionsOptions } from '@azure/openai'
4+
import { ChatCompletionMessageParam } from 'openai/resources/chat'
5+
import { RequestOptions } from 'openai/core'
46

57
export type PartialRecord<K extends keyof any, T> = Partial<Record<K, T>>
68

@@ -34,7 +36,9 @@ export interface CourseChatRequest extends ChatRequest {
3436

3537
export type APIError = typeof OpenAI.APIError
3638

37-
export type OpenAIStream = ReturnType<typeof OpenAI.prototype.chat.completions.create>
39+
export type OpenAIStream = ReturnType<
40+
typeof OpenAI.prototype.chat.completions.create
41+
>
3842

3943
export type Message = OpenAI.Chat.ChatCompletionMessage
4044
export interface CustomMessage {
@@ -43,14 +47,21 @@ export interface CustomMessage {
4347
}
4448
export type Role = 'system' | 'assistant' | 'user'
4549

46-
export type StreamingOptions = OpenAI.Chat.Completions.CompletionCreateParamsStreaming
50+
export type StreamingOptions =
51+
OpenAI.Chat.Completions.CompletionCreateParamsStreaming
4752

4853
export type AzureOptions = {
4954
model: string
5055
messages: ChatRequestMessage[]
5156
options: GetChatCompletionsOptions
5257
}
5358

59+
export type AzureOptionsV2 = {
60+
model: string
61+
messages: ChatCompletionMessageParam[]
62+
options: RequestOptions
63+
}
64+
5465
interface RequestBody {
5566
id?: string
5667
options?: StreamingOptions

0 commit comments

Comments
 (0)