Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 4165 - Refactor message builder to avoid sending empty messages #4199

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 0 additions & 30 deletions web/hooks/useSendChatMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
ConversationalExtension,
EngineManager,
ToolManager,
ChatCompletionMessage,
} from '@janhq/core'
import { extractInferenceParams, extractModelLoadParams } from '@janhq/core'
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
Expand All @@ -21,7 +20,6 @@
fileUploadAtom,
} from '@/containers/Providers/Jotai'

import { Stack } from '@/utils/Stack'
import { compressImage, getBase64 } from '@/utils/base64'
import { MessageRequestBuilder } from '@/utils/messageRequestBuilder'

Expand Down Expand Up @@ -86,77 +84,50 @@
selectedModelRef.current = selectedModel
}, [selectedModel])

const normalizeMessages = (
messages: ChatCompletionMessage[]
): ChatCompletionMessage[] => {
const stack = new Stack<ChatCompletionMessage>()
for (const message of messages) {
if (stack.isEmpty()) {
stack.push(message)
continue
}
const topMessage = stack.peek()

if (message.role === topMessage.role) {
// add an empty message
stack.push({
role:
topMessage.role === ChatCompletionRole.User
? ChatCompletionRole.Assistant
: ChatCompletionRole.User,
content: '.', // some model requires not empty message
})
}
stack.push(message)
}

return stack.reverseOutput()
}

const resendChatMessage = async (currentMessage: ThreadMessage) => {
// Delete last response before regenerating
const newConvoData = currentMessages
let toSendMessage = currentMessage

Check warning on line 90 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

89-90 lines are not covered with tests

do {
deleteMessage(currentMessage.id)
const msg = newConvoData.pop()
if (!msg) break
toSendMessage = msg
deleteMessage(toSendMessage.id ?? '')

Check warning on line 97 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

92-97 lines are not covered with tests
} while (toSendMessage.role !== ChatCompletionRole.User)

if (activeThreadRef.current) {
await extensionManager

Check warning on line 101 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

100-101 lines are not covered with tests
.get<ConversationalExtension>(ExtensionTypeEnum.Conversational)
?.writeMessages(activeThreadRef.current.id, newConvoData)
}

sendChatMessage(toSendMessage.content[0]?.text.value)

Check warning on line 106 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

106 line is not covered with tests
}

const sendChatMessage = async (
message: string,
messages?: ThreadMessage[]
) => {
if (!message || message.trim().length === 0) return

Check warning on line 113 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

113 line is not covered with tests

if (!activeThreadRef.current) {
console.error('No active thread')
return

Check warning on line 117 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

115-117 lines are not covered with tests
}

if (engineParamsUpdate) setReloadModel(true)
setTokenSpeed(undefined)

Check warning on line 121 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

120-121 lines are not covered with tests

const runtimeParams = extractInferenceParams(activeModelParams)
const settingParams = extractModelLoadParams(activeModelParams)

Check warning on line 124 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

123-124 lines are not covered with tests

const prompt = message.trim()

Check warning on line 126 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

126 line is not covered with tests

updateThreadWaiting(activeThreadRef.current.id, true)
setCurrentPrompt('')
setEditPrompt('')

Check warning on line 130 in web/hooks/useSendChatMessage.ts

View workflow job for this annotation

GitHub Actions / coverage-check

128-130 lines are not covered with tests

let base64Blob = fileUpload[0]
? await getBase64(fileUpload[0].file)
Expand Down Expand Up @@ -247,7 +218,6 @@
(assistant) => assistant.tools ?? []
) ?? []
)
request.messages = normalizeMessages(request.messages ?? [])

// Request for inference
EngineManager.instance()
Expand Down
33 changes: 31 additions & 2 deletions web/utils/messageRequestBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { ulid } from 'ulidx'

import { FileType } from '@/containers/Providers/Jotai'

import { Stack } from '@/utils/Stack'

export class MessageRequestBuilder {
msgId: string
type: MessageRequestType
Expand All @@ -36,7 +38,7 @@ export class MessageRequestBuilder {
.filter((e) => e.status !== MessageStatus.Error)
.map<ChatCompletionMessage>((msg) => ({
role: msg.role,
content: msg.content[0]?.text.value ?? '',
content: msg.content[0]?.text.value ?? '.',
}))
}

Expand Down Expand Up @@ -130,12 +132,39 @@ export class MessageRequestBuilder {
return this
}

normalizeMessages = (
messages: ChatCompletionMessage[]
): ChatCompletionMessage[] => {
const stack = new Stack<ChatCompletionMessage>()
for (const message of messages) {
if (stack.isEmpty()) {
stack.push(message)
continue
}
const topMessage = stack.peek()

if (message.role === topMessage.role) {
// add an empty message
stack.push({
role:
topMessage.role === ChatCompletionRole.User
? ChatCompletionRole.Assistant
: ChatCompletionRole.User,
content: '.', // some model requires not empty message
})
}
stack.push(message)
}

return stack.reverseOutput()
}

build(): MessageRequest {
return {
id: this.msgId,
type: this.type,
threadId: this.thread.id,
messages: this.messages,
messages: this.normalizeMessages(this.messages),
model: this.model,
thread: this.thread,
}
Expand Down
Loading