Files
page-assist/src/hooks/useMessageOption.tsx

1414 lines
39 KiB
TypeScript
Raw Normal View History

import React from "react"
2024-03-23 14:44:05 +05:30
import { cleanUrl } from "~/libs/clean-url"
import {
2024-04-06 00:30:23 +05:30
defaultEmbeddingModelForRag,
geWebSearchFollowUpPrompt,
2025-02-14 18:17:12 +08:00
geWebSearchKeywordsPrompt,
getOllamaURL,
2024-04-06 00:30:23 +05:30
promptForRag,
systemPromptForNonRagOption
2024-03-23 14:44:05 +05:30
} from "~/services/ollama"
import type { ChatHistory, Message, MeteringEntry } from "~/store/option"
import { SystemMessage } from "@langchain/core/messages"
2024-03-23 14:44:05 +05:30
import { useStoreMessageOption } from "~/store/option"
import {
deleteChatForEdit,
generateID,
getPromptById,
removeMessageUsingHistoryId,
updateMessageByIndex
} from "@/db"
import { useNavigate } from "react-router-dom"
2024-02-18 13:23:47 +05:30
import { notification } from "antd"
2024-03-23 14:44:05 +05:30
import { getSystemPromptForWeb } from "~/web/web"
2024-03-24 12:43:43 +05:30
import { generateHistory } from "@/utils/generate-history"
import { useTranslation } from "react-i18next"
import {
saveMessageOnError as saveError,
saveMessageOnSuccess as saveSuccess
} from "./chat-helper"
import { usePageAssist } from "@/context"
2024-04-06 00:30:23 +05:30
import { PageAssistVectorStore } from "@/libs/PageAssistVectorStore"
import { formatDocs } from "@/chain/chat-with-x"
import { useWebUI } from "@/store/webui"
2024-04-16 23:04:25 +05:30
import { useStorage } from "@plasmohq/storage/hook"
import { useStoreChatModelSettings } from "@/store/model"
import { getAllDefaultModelSettings } from "@/services/model-settings"
2024-06-30 20:45:06 +05:30
import { pageAssistModel } from "@/models"
import { getNoOfRetrievedDocs } from "@/services/app"
import { humanMessageFormatter } from "@/utils/human-message"
import { pageAssistEmbeddingModel } from "@/models/embedding"
2025-01-24 22:29:18 +05:30
import {
isReasoningEnded,
isReasoningStarted,
mergeReasoningContent,
2025-01-24 22:29:18 +05:30
removeReasoning
} from "@/libs/reasoning"
export const useMessageOption = () => {
const {
controller: abortController,
setController: setAbortController,
messages,
2024-04-06 00:30:23 +05:30
setMessages
} = usePageAssist()
const {
history,
setHistory,
meteringEntries,
setMeteringEntries,
setStreaming,
streaming,
setIsFirstMessage,
historyId,
setHistoryId,
isLoading,
setIsLoading,
isProcessing,
setIsProcessing,
chatMode,
2024-02-15 00:26:13 +05:30
setChatMode,
webSearch,
setWebSearch,
2025-02-14 18:17:12 +08:00
iodSearch,
setIodSearch,
isSearchingInternet,
setIsSearchingInternet,
selectedQuickPrompt,
setSelectedQuickPrompt,
selectedSystemPrompt,
setSelectedSystemPrompt,
selectedKnowledge,
setSelectedKnowledge,
temporaryChat,
setTemporaryChat,
useOCR,
setUseOCR
} = useStoreMessageOption()
const currentChatModelSettings = useStoreChatModelSettings()
2024-04-16 23:04:25 +05:30
const [selectedModel, setSelectedModel] = useStorage("selectedModel")
2025-01-24 22:29:18 +05:30
const [defaultInternetSearchOn] = useStorage("defaultInternetSearchOn", false)
const [speechToTextLanguage, setSpeechToTextLanguage] = useStorage(
"speechToTextLanguage",
"en-US"
)
2024-04-16 23:04:25 +05:30
const { ttsEnabled } = useWebUI()
2024-03-24 12:43:43 +05:30
const { t } = useTranslation("option")
const navigate = useNavigate()
const textareaRef = React.useRef<HTMLTextAreaElement>(null)
const clearChat = () => {
navigate("/")
setMessages([])
setHistory([])
setHistoryId(null)
setIsFirstMessage(true)
setIsLoading(false)
setIsProcessing(false)
setStreaming(false)
currentChatModelSettings.reset()
textareaRef?.current?.focus()
2025-01-24 22:29:18 +05:30
if (defaultInternetSearchOn) {
setWebSearch(true)
}
}
// 从最后的结果中解析出 思维链 (Chain-of-Thought) 和 结果
2025-02-22 14:09:57 +08:00
const responseResolver = (msg: string) => {
const cotStart = msg.indexOf("<think>")
const cotEnd = msg.indexOf("</think>")
let cot = ""
2025-02-22 14:09:57 +08:00
let content = ""
if (cotStart > -1 && cotEnd > -1) {
cot = msg.substring(cotStart + 7, cotEnd)
content = msg.substring(cotEnd + 8)
2025-02-22 14:09:57 +08:00
} else {
content = msg
}
// 去掉换行符
cot = cot.replace(/\n/g, "")
2025-02-22 14:09:57 +08:00
content = content.replace(/\n/g, "")
return {
cot: cot,
2025-02-22 14:09:57 +08:00
content
}
}
const searchChatMode = async (
2025-02-14 18:17:12 +08:00
webSearch: boolean,
iodSearch: boolean,
message: string,
image: string,
isRegenerate: boolean,
messages: Message[],
history: ChatHistory,
signal: AbortSignal
) => {
const url = await getOllamaURL()
const userDefaultModelSettings = await getAllDefaultModelSettings()
if (image.length > 0) {
image = `data:image/jpeg;base64,${image.split(",")[1]}`
}
2024-06-30 20:45:06 +05:30
const ollama = await pageAssistModel({
2024-03-24 12:43:43 +05:30
model: selectedModel!,
baseUrl: cleanUrl(url),
keepAlive:
currentChatModelSettings?.keepAlive ??
userDefaultModelSettings?.keepAlive,
temperature:
currentChatModelSettings?.temperature ??
userDefaultModelSettings?.temperature,
topK: currentChatModelSettings?.topK ?? userDefaultModelSettings?.topK,
topP: currentChatModelSettings?.topP ?? userDefaultModelSettings?.topP,
numCtx:
currentChatModelSettings?.numCtx ?? userDefaultModelSettings?.numCtx,
2024-08-20 16:11:50 +05:30
seed: currentChatModelSettings?.seed,
numGpu:
currentChatModelSettings?.numGpu ?? userDefaultModelSettings?.numGpu,
numPredict:
currentChatModelSettings?.numPredict ??
userDefaultModelSettings?.numPredict,
useMMap:
currentChatModelSettings?.useMMap ?? userDefaultModelSettings?.useMMap,
minP: currentChatModelSettings?.minP ?? userDefaultModelSettings?.minP,
repeatLastN:
currentChatModelSettings?.repeatLastN ??
userDefaultModelSettings?.repeatLastN,
repeatPenalty:
currentChatModelSettings?.repeatPenalty ??
userDefaultModelSettings?.repeatPenalty,
tfsZ: currentChatModelSettings?.tfsZ ?? userDefaultModelSettings?.tfsZ,
numKeep:
currentChatModelSettings?.numKeep ?? userDefaultModelSettings?.numKeep,
numThread:
currentChatModelSettings?.numThread ??
userDefaultModelSettings?.numThread,
useMlock:
currentChatModelSettings?.useMlock ?? userDefaultModelSettings?.useMlock
})
let newMessage: Message[] = []
let generateMessageId = generateID()
const meter: MeteringEntry = {
2025-02-22 14:09:57 +08:00
id: generateMessageId,
queryContent: message
} as MeteringEntry
if (!isRegenerate) {
newMessage = [
...messages,
{
isBot: false,
name: "You",
message,
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
images: [image]
},
{
isBot: true,
name: selectedModel,
message: "▋",
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
id: generateMessageId
}
]
} else {
newMessage = [
...messages,
{
isBot: true,
name: selectedModel,
message: "▋",
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
id: generateMessageId
}
]
}
setMessages(newMessage)
let fullText = ""
let contentToSave = ""
2025-01-24 22:29:18 +05:30
let timetaken = 0
try {
setIsSearchingInternet(true)
let query = message
2025-02-14 18:17:12 +08:00
let keywords: string[] = []
if (newMessage.length > 2) {
let questionPrompt = await geWebSearchFollowUpPrompt()
const lastTenMessages = newMessage.slice(-10)
lastTenMessages.pop()
const chat_history = lastTenMessages
.map((message) => {
return `${message.isBot ? "Assistant: " : "Human: "}${message.message}`
})
.join("\n")
const promptForQuestion = questionPrompt
.replaceAll("{chat_history}", chat_history)
.replaceAll("{question}", message)
2024-06-30 20:45:06 +05:30
const questionOllama = await pageAssistModel({
2024-03-24 12:43:43 +05:30
model: selectedModel!,
baseUrl: cleanUrl(url),
keepAlive:
currentChatModelSettings?.keepAlive ??
userDefaultModelSettings?.keepAlive,
temperature:
currentChatModelSettings?.temperature ??
userDefaultModelSettings?.temperature,
topK:
currentChatModelSettings?.topK ?? userDefaultModelSettings?.topK,
topP:
currentChatModelSettings?.topP ?? userDefaultModelSettings?.topP,
numCtx:
currentChatModelSettings?.numCtx ??
userDefaultModelSettings?.numCtx,
2024-08-20 16:11:50 +05:30
seed: currentChatModelSettings?.seed,
numGpu:
currentChatModelSettings?.numGpu ??
userDefaultModelSettings?.numGpu,
numPredict:
currentChatModelSettings?.numPredict ??
userDefaultModelSettings?.numPredict,
useMMap:
currentChatModelSettings?.useMMap ??
userDefaultModelSettings?.useMMap,
minP:
currentChatModelSettings?.minP ?? userDefaultModelSettings?.minP,
repeatLastN:
currentChatModelSettings?.repeatLastN ??
userDefaultModelSettings?.repeatLastN,
repeatPenalty:
currentChatModelSettings?.repeatPenalty ??
userDefaultModelSettings?.repeatPenalty,
tfsZ:
currentChatModelSettings?.tfsZ ?? userDefaultModelSettings?.tfsZ,
numKeep:
currentChatModelSettings?.numKeep ??
userDefaultModelSettings?.numKeep,
numThread:
currentChatModelSettings?.numThread ??
userDefaultModelSettings?.numThread,
useMlock:
currentChatModelSettings?.useMlock ??
userDefaultModelSettings?.useMlock
})
const response = await questionOllama.invoke(promptForQuestion)
query = response.content.toString()
2025-01-24 22:29:18 +05:30
query = removeReasoning(query)
}
2025-02-14 18:17:12 +08:00
// Currently only IoD search use keywords
if (iodSearch) {
// Extract keywords
const questionPrompt = await geWebSearchKeywordsPrompt()
const promptForQuestion = questionPrompt.replaceAll("{query}", query)
const response = await ollama.invoke(promptForQuestion)
let res = response.content.toString()
res = removeReasoning(res)
2025-02-22 14:09:57 +08:00
keywords = res
.replace(/^Keywords:/i, "")
.split(", ")
.map((k) => k.trim())
2025-02-14 18:17:12 +08:00
}
const { prompt, webSources, iodSources, iodSearchResults: iodData, iodTokenCount } =
2025-02-23 13:02:32 +08:00
await getSystemPromptForWeb(query, keywords, webSearch, iodSearch)
2025-02-22 14:09:57 +08:00
console.log("prompt:\n" + prompt)
setIsSearchingInternet(false)
meter.prompt = prompt
meter.iodKeywords = keywords
meter.iodData = iodData
meter.iodTokenCount = iodTokenCount
// message = message.trim().replaceAll("\n", " ")
let humanMessage = await humanMessageFormatter({
content: [
{
text: message,
type: "text"
}
],
model: selectedModel,
useOCR: useOCR
})
if (image.length > 0) {
humanMessage = await humanMessageFormatter({
content: [
{
text: message,
type: "text"
},
{
image_url: image,
type: "image_url"
}
],
model: selectedModel,
useOCR: useOCR
})
}
const applicationChatHistory = generateHistory(history, selectedModel)
if (prompt) {
applicationChatHistory.unshift(
new SystemMessage({
content: prompt
})
)
}
let generationInfo: any | undefined = undefined
const chunks = await ollama.stream(
[...applicationChatHistory, humanMessage],
{
signal: signal,
callbacks: [
{
handleLLMEnd(output: any): any {
try {
generationInfo = output?.generations?.[0][0]?.generationInfo
} catch (e) {
2025-01-25 15:19:28 +05:30
console.error("handleLLMEnd error", e)
}
}
}
]
}
)
let count = 0
const chatStartTime = new Date()
2025-01-24 22:29:18 +05:30
let reasoningStartTime: Date | undefined = undefined
let reasoningEndTime: Date | undefined = undefined
let apiReasoning = false
for await (const chunk of chunks) {
if (chunk?.additional_kwargs?.reasoning_content) {
const reasoningContent = mergeReasoningContent(
fullText,
chunk?.additional_kwargs?.reasoning_content || ""
)
contentToSave = reasoningContent
fullText = reasoningContent
apiReasoning = true
} else {
if (apiReasoning) {
fullText += "</think>"
contentToSave += "</think>"
apiReasoning = false
}
}
contentToSave += chunk?.content
fullText += chunk?.content
if (count === 0) {
setIsProcessing(true)
}
2025-01-24 22:29:18 +05:30
if (isReasoningStarted(fullText) && !reasoningStartTime) {
reasoningStartTime = new Date()
}
if (
reasoningStartTime &&
!reasoningEndTime &&
isReasoningEnded(fullText)
) {
reasoningEndTime = new Date()
const reasoningTime =
reasoningEndTime.getTime() - reasoningStartTime.getTime()
timetaken = reasoningTime
}
setMessages((prev) => {
return prev.map((message) => {
if (message.id === generateMessageId) {
return {
...message,
2025-01-24 22:29:18 +05:30
message: fullText + "▋",
reasoning_time_taken: timetaken
}
}
return message
})
})
count++
}
// update the message with the full text
setMessages((prev) => {
return prev.map((message) => {
if (message.id === generateMessageId) {
return {
...message,
message: fullText,
2025-02-14 18:17:12 +08:00
webSources,
iodSources,
2025-01-24 22:29:18 +05:30
generationInfo,
reasoning_time_taken: timetaken
}
}
return message
})
})
setHistory([
...history,
{
role: "user",
content: message,
image
},
{
role: "assistant",
content: fullText
}
])
await saveMessageOnSuccess({
historyId,
setHistoryId,
isRegenerate,
selectedModel: selectedModel,
message,
image,
fullText,
2025-02-14 18:17:12 +08:00
webSources,
iodSources,
2025-01-24 22:29:18 +05:30
generationInfo,
reasoning_time_taken: timetaken
})
setIsProcessing(false)
setStreaming(false)
2025-02-22 14:09:57 +08:00
// Save metering entry
const { cot, content } = responseResolver(fullText)
setMeteringEntries([...meteringEntries, {
...meter,
modelInputTokenCount: generationInfo?.prompt_eval_count ?? 0,
modelOutputTokenCount: generationInfo?.eval_count ?? 0,
model: generationInfo?.model ?? "",
relatedDataCount: iodData?.length ?? 0,
timeTaken: timetaken,
date: chatStartTime,
cot,
responseContent: content,
modelResponseContent: fullText,
}])
} catch (e) {
const errorSave = await saveMessageOnError({
e,
botMessage: fullText,
history,
historyId,
image,
selectedModel,
setHistory,
setHistoryId,
userMessage: message,
isRegenerating: isRegenerate
})
if (!errorSave) {
notification.error({
2024-03-24 12:43:43 +05:30
message: t("error"),
description: e?.message || t("somethingWentWrong")
})
}
setIsProcessing(false)
setStreaming(false)
} finally {
setAbortController(null)
}
}
const saveMessageOnSuccess = async (e: any) => {
if (!temporaryChat) {
return await saveSuccess(e)
} else {
setHistoryId("temp")
}
return true
}
const saveMessageOnError = async (e: any) => {
if (!temporaryChat) {
return await saveError(e)
} else {
setHistory([
...history,
{
role: "user",
content: e.userMessage,
image: e.image
},
{
role: "assistant",
content: e.botMessage
}
])
setHistoryId("temp")
}
return true
}
const normalChatMode = async (
message: string,
image: string,
isRegenerate: boolean,
messages: Message[],
history: ChatHistory,
signal: AbortSignal
) => {
const url = await getOllamaURL()
const userDefaultModelSettings = await getAllDefaultModelSettings()
let promptId: string | undefined = selectedSystemPrompt
let promptContent: string | undefined = undefined
if (image.length > 0) {
image = `data:image/jpeg;base64,${image.split(",")[1]}`
}
2024-06-30 20:45:06 +05:30
const ollama = await pageAssistModel({
2024-03-24 12:43:43 +05:30
model: selectedModel!,
baseUrl: cleanUrl(url),
keepAlive:
currentChatModelSettings?.keepAlive ??
userDefaultModelSettings?.keepAlive,
temperature:
currentChatModelSettings?.temperature ??
userDefaultModelSettings?.temperature,
topK: currentChatModelSettings?.topK ?? userDefaultModelSettings?.topK,
topP: currentChatModelSettings?.topP ?? userDefaultModelSettings?.topP,
numCtx:
currentChatModelSettings?.numCtx ?? userDefaultModelSettings?.numCtx,
2024-08-20 16:11:50 +05:30
seed: currentChatModelSettings?.seed,
numGpu:
currentChatModelSettings?.numGpu ?? userDefaultModelSettings?.numGpu,
numPredict:
currentChatModelSettings?.numPredict ??
userDefaultModelSettings?.numPredict,
useMMap:
currentChatModelSettings?.useMMap ?? userDefaultModelSettings?.useMMap,
minP: currentChatModelSettings?.minP ?? userDefaultModelSettings?.minP,
repeatLastN:
currentChatModelSettings?.repeatLastN ??
userDefaultModelSettings?.repeatLastN,
repeatPenalty:
currentChatModelSettings?.repeatPenalty ??
userDefaultModelSettings?.repeatPenalty,
tfsZ: currentChatModelSettings?.tfsZ ?? userDefaultModelSettings?.tfsZ,
numKeep:
currentChatModelSettings?.numKeep ?? userDefaultModelSettings?.numKeep,
numThread:
currentChatModelSettings?.numThread ??
userDefaultModelSettings?.numThread,
useMlock:
currentChatModelSettings?.useMlock ?? userDefaultModelSettings?.useMlock
})
let newMessage: Message[] = []
let generateMessageId = generateID()
if (!isRegenerate) {
newMessage = [
...messages,
{
isBot: false,
name: "You",
message,
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
images: [image]
},
{
isBot: true,
name: selectedModel,
message: "▋",
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
id: generateMessageId
}
]
} else {
newMessage = [
...messages,
{
isBot: true,
name: selectedModel,
message: "▋",
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
id: generateMessageId
}
]
}
setMessages(newMessage)
let fullText = ""
let contentToSave = ""
2025-01-24 22:29:18 +05:30
let timetaken = 0
try {
const prompt = await systemPromptForNonRagOption()
const selectedPrompt = await getPromptById(selectedSystemPrompt)
let humanMessage = await humanMessageFormatter({
content: [
{
text: message,
type: "text"
}
],
model: selectedModel,
useOCR: useOCR
})
if (image.length > 0) {
humanMessage = await humanMessageFormatter({
content: [
{
text: message,
type: "text"
},
{
image_url: image,
type: "image_url"
}
],
model: selectedModel,
useOCR: useOCR
})
}
const applicationChatHistory = generateHistory(history, selectedModel)
if (prompt && !selectedPrompt) {
applicationChatHistory.unshift(
new SystemMessage({
content: prompt
})
)
}
const isTempSystemprompt =
currentChatModelSettings.systemPrompt &&
currentChatModelSettings.systemPrompt?.trim().length > 0
if (!isTempSystemprompt && selectedPrompt) {
applicationChatHistory.unshift(
new SystemMessage({
content: selectedPrompt.content
})
)
promptContent = selectedPrompt.content
}
if (isTempSystemprompt) {
applicationChatHistory.unshift(
new SystemMessage({
content: currentChatModelSettings.systemPrompt
})
)
promptContent = currentChatModelSettings.systemPrompt
}
let generationInfo: any | undefined = undefined
const chunks = await ollama.stream(
[...applicationChatHistory, humanMessage],
{
signal: signal,
callbacks: [
{
handleLLMEnd(output: any): any {
try {
generationInfo = output?.generations?.[0][0]?.generationInfo
} catch (e) {
2025-01-25 15:19:28 +05:30
console.error("handleLLMEnd error", e)
}
}
}
]
}
)
2024-06-30 20:45:06 +05:30
let count = 0
2025-01-24 22:29:18 +05:30
let reasoningStartTime: Date | null = null
let reasoningEndTime: Date | null = null
let apiReasoning: boolean = false
2025-01-24 22:29:18 +05:30
for await (const chunk of chunks) {
if (chunk?.additional_kwargs?.reasoning_content) {
const reasoningContent = mergeReasoningContent(
fullText,
chunk?.additional_kwargs?.reasoning_content || ""
)
contentToSave = reasoningContent
fullText = reasoningContent
apiReasoning = true
} else {
if (apiReasoning) {
fullText += "</think>"
contentToSave += "</think>"
apiReasoning = false
}
}
contentToSave += chunk?.content
fullText += chunk?.content
2025-01-24 22:29:18 +05:30
if (isReasoningStarted(fullText) && !reasoningStartTime) {
reasoningStartTime = new Date()
}
if (
reasoningStartTime &&
!reasoningEndTime &&
isReasoningEnded(fullText)
) {
reasoningEndTime = new Date()
const reasoningTime =
reasoningEndTime.getTime() - reasoningStartTime.getTime()
timetaken = reasoningTime
}
if (count === 0) {
setIsProcessing(true)
}
setMessages((prev) => {
return prev.map((message) => {
if (message.id === generateMessageId) {
return {
...message,
2025-01-24 22:29:18 +05:30
message: fullText + "▋",
reasoning_time_taken: timetaken
}
}
return message
})
})
count++
}
setMessages((prev) => {
return prev.map((message) => {
if (message.id === generateMessageId) {
return {
...message,
message: fullText,
2025-01-24 22:29:18 +05:30
generationInfo,
reasoning_time_taken: timetaken
}
}
return message
})
})
setHistory([
...history,
{
role: "user",
content: message,
image
},
{
role: "assistant",
content: fullText
}
])
await saveMessageOnSuccess({
historyId,
setHistoryId,
isRegenerate,
selectedModel: selectedModel,
message,
image,
fullText,
source: [],
generationInfo,
prompt_content: promptContent,
2025-01-24 22:29:18 +05:30
prompt_id: promptId,
reasoning_time_taken: timetaken
})
setIsProcessing(false)
setStreaming(false)
setIsProcessing(false)
setStreaming(false)
} catch (e) {
const errorSave = await saveMessageOnError({
e,
botMessage: fullText,
history,
historyId,
image,
selectedModel,
setHistory,
setHistoryId,
userMessage: message,
isRegenerating: isRegenerate,
prompt_content: promptContent,
prompt_id: promptId
})
if (!errorSave) {
2024-02-18 13:23:47 +05:30
notification.error({
2024-03-24 12:43:43 +05:30
message: t("error"),
description: e?.message || t("somethingWentWrong")
2024-02-18 13:23:47 +05:30
})
}
setIsProcessing(false)
setStreaming(false)
} finally {
setAbortController(null)
}
}
2024-04-06 00:30:23 +05:30
const ragMode = async (
message: string,
image: string,
isRegenerate: boolean,
messages: Message[],
history: ChatHistory,
signal: AbortSignal
) => {
const url = await getOllamaURL()
const userDefaultModelSettings = await getAllDefaultModelSettings()
2024-04-06 00:30:23 +05:30
2024-06-30 20:45:06 +05:30
const ollama = await pageAssistModel({
2024-04-06 00:30:23 +05:30
model: selectedModel!,
baseUrl: cleanUrl(url),
keepAlive:
currentChatModelSettings?.keepAlive ??
userDefaultModelSettings?.keepAlive,
temperature:
currentChatModelSettings?.temperature ??
userDefaultModelSettings?.temperature,
topK: currentChatModelSettings?.topK ?? userDefaultModelSettings?.topK,
topP: currentChatModelSettings?.topP ?? userDefaultModelSettings?.topP,
numCtx:
currentChatModelSettings?.numCtx ?? userDefaultModelSettings?.numCtx,
2024-08-20 16:11:50 +05:30
seed: currentChatModelSettings?.seed,
numGpu:
currentChatModelSettings?.numGpu ?? userDefaultModelSettings?.numGpu,
numPredict:
currentChatModelSettings?.numPredict ??
userDefaultModelSettings?.numPredict,
useMMap:
currentChatModelSettings?.useMMap ?? userDefaultModelSettings?.useMMap,
minP: currentChatModelSettings?.minP ?? userDefaultModelSettings?.minP,
repeatLastN:
currentChatModelSettings?.repeatLastN ??
userDefaultModelSettings?.repeatLastN,
repeatPenalty:
currentChatModelSettings?.repeatPenalty ??
userDefaultModelSettings?.repeatPenalty,
tfsZ: currentChatModelSettings?.tfsZ ?? userDefaultModelSettings?.tfsZ,
numKeep:
currentChatModelSettings?.numKeep ?? userDefaultModelSettings?.numKeep,
numThread:
currentChatModelSettings?.numThread ??
userDefaultModelSettings?.numThread,
useMlock:
currentChatModelSettings?.useMlock ?? userDefaultModelSettings?.useMlock
2024-04-06 00:30:23 +05:30
})
let newMessage: Message[] = []
let generateMessageId = generateID()
if (!isRegenerate) {
newMessage = [
...messages,
{
isBot: false,
name: "You",
message,
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
2024-04-06 00:30:23 +05:30
images: []
},
{
isBot: true,
name: selectedModel,
message: "▋",
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
2024-04-06 00:30:23 +05:30
id: generateMessageId
}
]
} else {
newMessage = [
...messages,
{
isBot: true,
name: selectedModel,
message: "▋",
2025-02-14 18:17:12 +08:00
webSources: [],
iodSources: [],
2024-04-06 00:30:23 +05:30
id: generateMessageId
}
]
}
setMessages(newMessage)
let fullText = ""
let contentToSave = ""
const embeddingModle = await defaultEmbeddingModelForRag()
const ollamaUrl = await getOllamaURL()
const ollamaEmbedding = await pageAssistEmbeddingModel({
2024-04-06 00:30:23 +05:30
model: embeddingModle || selectedModel,
baseUrl: cleanUrl(ollamaUrl),
keepAlive:
currentChatModelSettings?.keepAlive ??
userDefaultModelSettings?.keepAlive
2024-04-06 00:30:23 +05:30
})
let vectorstore = await PageAssistVectorStore.fromExistingIndex(
ollamaEmbedding,
{
file_id: null,
knownledge_id: selectedKnowledge.id
}
)
2025-01-24 22:29:18 +05:30
let timetaken = 0
2024-04-06 00:30:23 +05:30
try {
let query = message
const { ragPrompt: systemPrompt, ragQuestionPrompt: questionPrompt } =
await promptForRag()
if (newMessage.length > 2) {
const lastTenMessages = newMessage.slice(-10)
lastTenMessages.pop()
const chat_history = lastTenMessages
.map((message) => {
return `${message.isBot ? "Assistant: " : "Human: "}${message.message}`
})
.join("\n")
const promptForQuestion = questionPrompt
.replaceAll("{chat_history}", chat_history)
.replaceAll("{question}", message)
2024-06-30 20:45:06 +05:30
const questionOllama = await pageAssistModel({
2024-04-06 00:30:23 +05:30
model: selectedModel!,
baseUrl: cleanUrl(url),
keepAlive:
currentChatModelSettings?.keepAlive ??
userDefaultModelSettings?.keepAlive,
temperature:
currentChatModelSettings?.temperature ??
userDefaultModelSettings?.temperature,
topK:
currentChatModelSettings?.topK ?? userDefaultModelSettings?.topK,
topP:
currentChatModelSettings?.topP ?? userDefaultModelSettings?.topP,
numCtx:
currentChatModelSettings?.numCtx ??
userDefaultModelSettings?.numCtx,
2024-08-20 16:11:50 +05:30
seed: currentChatModelSettings?.seed,
numGpu:
currentChatModelSettings?.numGpu ??
userDefaultModelSettings?.numGpu,
numPredict:
currentChatModelSettings?.numPredict ??
userDefaultModelSettings?.numPredict,
useMMap:
currentChatModelSettings?.useMMap ??
userDefaultModelSettings?.useMMap,
minP:
currentChatModelSettings?.minP ?? userDefaultModelSettings?.minP,
repeatLastN:
currentChatModelSettings?.repeatLastN ??
userDefaultModelSettings?.repeatLastN,
repeatPenalty:
currentChatModelSettings?.repeatPenalty ??
userDefaultModelSettings?.repeatPenalty,
tfsZ:
currentChatModelSettings?.tfsZ ?? userDefaultModelSettings?.tfsZ,
numKeep:
currentChatModelSettings?.numKeep ??
userDefaultModelSettings?.numKeep,
numThread:
currentChatModelSettings?.numThread ??
userDefaultModelSettings?.numThread,
useMlock:
currentChatModelSettings?.useMlock ??
userDefaultModelSettings?.useMlock
2024-04-06 00:30:23 +05:30
})
const response = await questionOllama.invoke(promptForQuestion)
query = response.content.toString()
2025-01-24 22:29:18 +05:30
query = removeReasoning(query)
2024-04-06 00:30:23 +05:30
}
const docSize = await getNoOfRetrievedDocs()
2024-04-06 00:30:23 +05:30
const docs = await vectorstore.similaritySearch(query, docSize)
2024-04-06 00:30:23 +05:30
const context = formatDocs(docs)
const source = docs.map((doc) => {
return {
2024-04-06 20:18:46 +05:30
...doc,
2024-04-06 00:30:23 +05:30
name: doc?.metadata?.source || "untitled",
type: doc?.metadata?.type || "unknown",
2024-04-06 20:18:46 +05:30
mode: "rag",
2024-04-06 00:30:23 +05:30
url: ""
}
})
// message = message.trim().replaceAll("\n", " ")
2024-04-06 00:30:23 +05:30
let humanMessage = await humanMessageFormatter({
2024-04-06 00:30:23 +05:30
content: [
{
text: systemPrompt
.replace("{context}", context)
.replace("{question}", message),
type: "text"
}
],
model: selectedModel,
useOCR: useOCR
2024-04-06 00:30:23 +05:30
})
const applicationChatHistory = generateHistory(history, selectedModel)
2024-04-06 00:30:23 +05:30
let generationInfo: any | undefined = undefined
2024-04-06 00:30:23 +05:30
const chunks = await ollama.stream(
[...applicationChatHistory, humanMessage],
{
signal: signal,
callbacks: [
{
handleLLMEnd(output: any): any {
try {
generationInfo = output?.generations?.[0][0]?.generationInfo
} catch (e) {
2025-01-25 15:19:28 +05:30
console.error("handleLLMEnd error", e)
}
}
}
]
2024-04-06 00:30:23 +05:30
}
)
let count = 0
2025-01-24 22:29:18 +05:30
let reasoningStartTime: Date | undefined = undefined
let reasoningEndTime: Date | undefined = undefined
let apiReasoning = false
2025-01-24 22:29:18 +05:30
2024-04-06 00:30:23 +05:30
for await (const chunk of chunks) {
if (chunk?.additional_kwargs?.reasoning_content) {
const reasoningContent = mergeReasoningContent(
fullText,
chunk?.additional_kwargs?.reasoning_content || ""
)
contentToSave = reasoningContent
fullText = reasoningContent
apiReasoning = true
} else {
if (apiReasoning) {
fullText += "</think>"
contentToSave += "</think>"
apiReasoning = false
}
}
contentToSave += chunk?.content
fullText += chunk?.content
2024-04-06 00:30:23 +05:30
if (count === 0) {
setIsProcessing(true)
}
2025-01-24 22:29:18 +05:30
if (isReasoningStarted(fullText) && !reasoningStartTime) {
reasoningStartTime = new Date()
}
if (
reasoningStartTime &&
!reasoningEndTime &&
isReasoningEnded(fullText)
) {
reasoningEndTime = new Date()
const reasoningTime =
reasoningEndTime.getTime() - reasoningStartTime.getTime()
timetaken = reasoningTime
}
2024-04-06 00:30:23 +05:30
setMessages((prev) => {
return prev.map((message) => {
if (message.id === generateMessageId) {
return {
...message,
2025-01-24 22:29:18 +05:30
message: fullText + "▋",
reasoning_time_taken: timetaken
2024-04-06 00:30:23 +05:30
}
}
return message
})
})
count++
}
// update the message with the full text
setMessages((prev) => {
return prev.map((message) => {
if (message.id === generateMessageId) {
return {
...message,
message: fullText,
2025-02-14 18:17:12 +08:00
webSources: source,
2025-01-24 22:29:18 +05:30
generationInfo,
reasoning_time_taken: timetaken
2024-04-06 00:30:23 +05:30
}
}
return message
})
})
setHistory([
...history,
{
role: "user",
content: message,
image
},
{
role: "assistant",
content: fullText
}
])
await saveMessageOnSuccess({
historyId,
setHistoryId,
isRegenerate,
selectedModel: selectedModel,
message,
image,
fullText,
source,
2025-01-24 22:29:18 +05:30
generationInfo,
reasoning_time_taken: timetaken
2024-04-06 00:30:23 +05:30
})
setIsProcessing(false)
setStreaming(false)
} catch (e) {
const errorSave = await saveMessageOnError({
e,
botMessage: fullText,
history,
historyId,
image,
selectedModel,
setHistory,
setHistoryId,
userMessage: message,
isRegenerating: isRegenerate
})
if (!errorSave) {
notification.error({
message: t("error"),
description: e?.message || t("somethingWentWrong")
})
}
setIsProcessing(false)
setStreaming(false)
} finally {
setAbortController(null)
}
}
const onSubmit = async ({
message,
image,
isRegenerate = false,
messages: chatHistory,
memory,
controller
}: {
message: string
image: string
isRegenerate?: boolean
messages?: Message[]
memory?: ChatHistory
controller?: AbortController
}) => {
setStreaming(true)
let signal: AbortSignal
if (!controller) {
const newController = new AbortController()
signal = newController.signal
setAbortController(newController)
} else {
setAbortController(controller)
signal = controller.signal
}
2024-04-06 00:30:23 +05:30
if (selectedKnowledge) {
await ragMode(
message,
image,
isRegenerate,
chatHistory || messages,
memory || history,
signal
)
} else {
2025-02-14 18:17:12 +08:00
if (webSearch || iodSearch) {
2024-04-06 00:30:23 +05:30
await searchChatMode(
2025-02-14 18:17:12 +08:00
webSearch,
iodSearch,
2024-04-06 00:30:23 +05:30
message,
image,
isRegenerate,
chatHistory || messages,
memory || history,
signal
)
} else {
await normalChatMode(
message,
image,
isRegenerate,
chatHistory || messages,
memory || history,
signal
)
}
}
}
const regenerateLastMessage = async () => {
const isOk = validateBeforeSubmit()
if (!isOk) {
return
}
if (history.length > 0) {
const lastMessage = history[history.length - 2]
let newHistory = history.slice(0, -2)
let mewMessages = messages
mewMessages.pop()
setHistory(newHistory)
setMessages(mewMessages)
await removeMessageUsingHistoryId(historyId)
if (lastMessage.role === "user") {
const newController = new AbortController()
await onSubmit({
message: lastMessage.content,
image: lastMessage.image || "",
isRegenerate: true,
memory: newHistory,
controller: newController
})
}
}
}
const stopStreamingRequest = () => {
if (abortController) {
abortController.abort()
setAbortController(null)
}
}
const validateBeforeSubmit = () => {
if (!selectedModel || selectedModel?.trim()?.length === 0) {
notification.error({
2024-03-24 12:43:43 +05:30
message: t("error"),
description: t("validationSelectModel")
})
return false
}
return true
}
const editMessage = async (
index: number,
message: string,
isHuman: boolean,
isSend: boolean
) => {
let newMessages = messages
let newHistory = history
// if human message and send then only trigger the submit
if (isHuman && isSend) {
const isOk = validateBeforeSubmit()
if (!isOk) {
return
}
const currentHumanMessage = newMessages[index]
newMessages[index].message = message
const previousMessages = newMessages.slice(0, index + 1)
setMessages(previousMessages)
const previousHistory = newHistory.slice(0, index)
setHistory(previousHistory)
await updateMessageByIndex(historyId, index, message)
await deleteChatForEdit(historyId, index)
const abortController = new AbortController()
await onSubmit({
message: message,
image: currentHumanMessage.images[0] || "",
isRegenerate: true,
messages: previousMessages,
memory: previousHistory,
controller: abortController
})
return
}
newMessages[index].message = message
setMessages(newMessages)
newHistory[index].content = message
setHistory(newHistory)
await updateMessageByIndex(historyId, index, message)
}
return {
editMessage,
messages,
setMessages,
onSubmit,
setStreaming,
streaming,
setHistory,
historyId,
setHistoryId,
setIsFirstMessage,
isLoading,
setIsLoading,
isProcessing,
stopStreamingRequest,
clearChat,
selectedModel,
setSelectedModel,
chatMode,
2024-02-15 00:26:13 +05:30
setChatMode,
speechToTextLanguage,
setSpeechToTextLanguage,
regenerateLastMessage,
webSearch,
setWebSearch,
2025-02-14 18:17:12 +08:00
iodSearch,
setIodSearch,
isSearchingInternet,
setIsSearchingInternet,
selectedQuickPrompt,
setSelectedQuickPrompt,
selectedSystemPrompt,
setSelectedSystemPrompt,
textareaRef,
selectedKnowledge,
setSelectedKnowledge,
ttsEnabled,
temporaryChat,
setTemporaryChat,
useOCR,
setUseOCR,
2025-01-24 22:29:18 +05:30
defaultInternetSearchOn
}
}