refactor: Update version to 1.1.12, fix selectedModel spacing, and update meta viewport tags
This commit is contained in:
		
							parent
							
								
									4a5713d6e4
								
							
						
					
					
						commit
						68bd3e651b
					
				| @ -25,46 +25,6 @@ | |||||||
|   box-shadow: none !important; |   box-shadow: none !important; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .gradient-border { |  | ||||||
|   --borderWidth: 3px; |  | ||||||
|   position: relative; |  | ||||||
|   border-radius: var(--borderWidth); |  | ||||||
| } |  | ||||||
| .gradient-border:after { |  | ||||||
|   content: ""; |  | ||||||
|   position: absolute; |  | ||||||
|   top: calc(-1 * var(--borderWidth)); |  | ||||||
|   left: calc(-1 * var(--borderWidth)); |  | ||||||
|   height: calc(100% + var(--borderWidth) * 2); |  | ||||||
|   width: calc(100% + var(--borderWidth) * 2); |  | ||||||
|   background: linear-gradient( |  | ||||||
|     60deg, |  | ||||||
|     #f79533, |  | ||||||
|     #f37055, |  | ||||||
|     #ef4e7b, |  | ||||||
|     #a166ab, |  | ||||||
|     #5073b8, |  | ||||||
|     #1098ad, |  | ||||||
|     #07b39b, |  | ||||||
|     #6fba82 |  | ||||||
|   ); |  | ||||||
|   border-radius: calc(2 * var(--borderWidth)); |  | ||||||
|   z-index: -1; |  | ||||||
|   animation: animatedgradient 3s ease alternate infinite; |  | ||||||
|   background-size: 300% 300%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @keyframes animatedgradient { |  | ||||||
|   0% { |  | ||||||
|     background-position: 0% 50%; |  | ||||||
|   } |  | ||||||
|   50% { |  | ||||||
|     background-position: 100% 50%; |  | ||||||
|   } |  | ||||||
|   100% { |  | ||||||
|     background-position: 0% 50%; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| /* Hide scrollbar for Chrome, Safari and Opera */ | /* Hide scrollbar for Chrome, Safari and Opera */ | ||||||
| .no-scrollbar::-webkit-scrollbar { | .no-scrollbar::-webkit-scrollbar { | ||||||
|   display: none; |   display: none; | ||||||
| @ -75,3 +35,37 @@ | |||||||
|   -ms-overflow-style: none; /* IE and Edge */ |   -ms-overflow-style: none; /* IE and Edge */ | ||||||
|   scrollbar-width: none; /* Firefox */ |   scrollbar-width: none; /* Firefox */ | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | @keyframes gradient-border { | ||||||
|  |   0% { | ||||||
|  |     border-image-source: linear-gradient( | ||||||
|  |       45deg, | ||||||
|  |       #f79533, | ||||||
|  |       #f37055, | ||||||
|  |       #ef4e7b, | ||||||
|  |       #a166ab | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |   50% { | ||||||
|  |     border-image-source: linear-gradient(45deg, #ef4e7b, #a166ab); | ||||||
|  |   } | ||||||
|  |   74% { | ||||||
|  |     border-image-source: linear-gradient(60deg, #5073b8, #1098ad); | ||||||
|  |   } | ||||||
|  |   100% { | ||||||
|  |     border-image-source: linear-gradient( | ||||||
|  |       45deg, | ||||||
|  |       #f79533, | ||||||
|  |       #f37055, | ||||||
|  |       #ef4e7b, | ||||||
|  |       #a166ab | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .animated-gradient-border { | ||||||
|  |   border: 4px solid; | ||||||
|  |   border-image-slice: 1; | ||||||
|  |   animation: gradient-border 3s infinite; | ||||||
|  |   border-radius: 10px; | ||||||
|  | } | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import { useTranslation } from "react-i18next" | |||||||
| export const WebSearch = () => { | export const WebSearch = () => { | ||||||
|   const {t} = useTranslation('common') |   const {t} = useTranslation('common') | ||||||
|   return ( |   return ( | ||||||
|     <div className="gradient-border mt-4 flex w-56 items-center gap-4 rounded-lg bg-neutral-100 p-1ccc text-slate-900 dark:bg-neutral-800 dark:text-slate-50"> |     <div className="animated-gradient-border mt-4 flex w-56 items-center gap-4 !rounded-lg bg-neutral-100 p-1 text-slate-900 dark:bg-neutral-800 dark:text-slate-50"> | ||||||
|       <div className="rounded p-1"> |       <div className="rounded p-1"> | ||||||
|         <Globe className="w-6 h-6" /> |         <Globe className="w-6 h-6" /> | ||||||
|       </div> |       </div> | ||||||
|  | |||||||
| @ -3,11 +3,19 @@ import { PlaygroundMessage } from "~/components/Common/Playground/Message" | |||||||
| import { useMessage } from "~/hooks/useMessage" | import { useMessage } from "~/hooks/useMessage" | ||||||
| import { EmptySidePanel } from "../Chat/empty" | import { EmptySidePanel } from "../Chat/empty" | ||||||
| import { useWebUI } from "@/store/webui" | import { useWebUI } from "@/store/webui" | ||||||
|  | import { MessageSourcePopup } from "@/components/Common/Playground/MessageSourcePopup" | ||||||
| 
 | 
 | ||||||
| export const SidePanelBody = () => { | export const SidePanelBody = () => { | ||||||
|   const { messages, streaming, regenerateLastMessage, editMessage } = |   const { | ||||||
|     useMessage() |     messages, | ||||||
|  |     streaming, | ||||||
|  |     regenerateLastMessage, | ||||||
|  |     editMessage, | ||||||
|  |     isSearchingInternet | ||||||
|  |   } = useMessage() | ||||||
|   const divRef = React.useRef<HTMLDivElement>(null) |   const divRef = React.useRef<HTMLDivElement>(null) | ||||||
|  |   const [isSourceOpen, setIsSourceOpen] = React.useState(false) | ||||||
|  |   const [source, setSource] = React.useState<any>(null) | ||||||
|   const { ttsEnabled } = useWebUI() |   const { ttsEnabled } = useWebUI() | ||||||
|   React.useEffect(() => { |   React.useEffect(() => { | ||||||
|     if (divRef.current) { |     if (divRef.current) { | ||||||
| @ -27,19 +35,26 @@ export const SidePanelBody = () => { | |||||||
|           currentMessageIndex={index} |           currentMessageIndex={index} | ||||||
|           totalMessages={messages.length} |           totalMessages={messages.length} | ||||||
|           onRengerate={regenerateLastMessage} |           onRengerate={regenerateLastMessage} | ||||||
|  |           isProcessing={streaming} | ||||||
|  |           isSearchingInternet={isSearchingInternet} | ||||||
|  |           sources={message.sources} | ||||||
|           onEditFormSubmit={(value) => { |           onEditFormSubmit={(value) => { | ||||||
|             editMessage(index, value, !message.isBot) |             editMessage(index, value, !message.isBot) | ||||||
|           }} |           }} | ||||||
|           isProcessing={streaming} |           onSourceClick={(data) => { | ||||||
|  |             setSource(data) | ||||||
|  |             setIsSourceOpen(true) | ||||||
|  |           }} | ||||||
|           isTTSEnabled={ttsEnabled} |           isTTSEnabled={ttsEnabled} | ||||||
|         /> |         /> | ||||||
|       ))} |       ))} | ||||||
|       {import.meta.env.BROWSER === "chrome" ? ( |       <div className="w-full h-48 flex-shrink-0"></div> | ||||||
|         <div className="w-full h-32 md:h-48 flex-shrink-0"></div> |  | ||||||
|       ) : ( |  | ||||||
|         <div className="w-full h-48 flex-shrink-0"></div> |  | ||||||
|       )} |  | ||||||
|       <div ref={divRef} /> |       <div ref={divRef} /> | ||||||
|  |       <MessageSourcePopup | ||||||
|  |         open={isSourceOpen} | ||||||
|  |         setOpen={setIsSourceOpen} | ||||||
|  |         source={source} | ||||||
|  |       /> | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,10 +4,17 @@ import React from "react" | |||||||
| import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" | import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" | ||||||
| import { useMessage } from "~/hooks/useMessage" | import { useMessage } from "~/hooks/useMessage" | ||||||
| import { toBase64 } from "~/libs/to-base64" | import { toBase64 } from "~/libs/to-base64" | ||||||
| import { Checkbox, Dropdown, Image, Tooltip } from "antd" | import { Checkbox, Dropdown, Image, Switch, Tooltip } from "antd" | ||||||
| import { useWebUI } from "~/store/webui" | import { useWebUI } from "~/store/webui" | ||||||
| import { defaultEmbeddingModelForRag } from "~/services/ollama" | import { defaultEmbeddingModelForRag } from "~/services/ollama" | ||||||
| import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react" | import { | ||||||
|  |   ImageIcon, | ||||||
|  |   MicIcon, | ||||||
|  |   StopCircleIcon, | ||||||
|  |   X, | ||||||
|  |   Wifi, | ||||||
|  |   WifiOff | ||||||
|  | } from "lucide-react" | ||||||
| import { useTranslation } from "react-i18next" | import { useTranslation } from "react-i18next" | ||||||
| import { ModelSelect } from "@/components/Common/ModelSelect" | import { ModelSelect } from "@/components/Common/ModelSelect" | ||||||
| import { useSpeechRecognition } from "@/hooks/useSpeechRecognition" | import { useSpeechRecognition } from "@/hooks/useSpeechRecognition" | ||||||
| @ -88,6 +95,13 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | |||||||
|             return |             return | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |         if (webSearch) { | ||||||
|  |           const defaultEM = await defaultEmbeddingModelForRag() | ||||||
|  |           if (!defaultEM) { | ||||||
|  |             form.setFieldError("message", t("formError.noEmbeddingModel")) | ||||||
|  |             return | ||||||
|  |           } | ||||||
|  |         } | ||||||
|         form.reset() |         form.reset() | ||||||
|         textAreaFocus() |         textAreaFocus() | ||||||
|         await sendMessage({ |         await sendMessage({ | ||||||
| @ -111,7 +125,9 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | |||||||
|     speechToTextLanguage, |     speechToTextLanguage, | ||||||
|     stopStreamingRequest, |     stopStreamingRequest, | ||||||
|     streaming, |     streaming, | ||||||
|     setChatMode |     setChatMode, | ||||||
|  |     webSearch, | ||||||
|  |     setWebSearch | ||||||
|   } = useMessage() |   } = useMessage() | ||||||
| 
 | 
 | ||||||
|   React.useEffect(() => { |   React.useEffect(() => { | ||||||
| @ -175,6 +191,13 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | |||||||
|                   return |                   return | ||||||
|                 } |                 } | ||||||
|               } |               } | ||||||
|  |               if (webSearch) { | ||||||
|  |                 const defaultEM = await defaultEmbeddingModelForRag() | ||||||
|  |                 if (!defaultEM) { | ||||||
|  |                   form.setFieldError("message", t("formError.noEmbeddingModel")) | ||||||
|  |                   return | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|               await stopListening() |               await stopListening() | ||||||
|               form.reset() |               form.reset() | ||||||
|               textAreaFocus() |               textAreaFocus() | ||||||
| @ -211,6 +234,20 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | |||||||
|               /> |               /> | ||||||
|               <div className="flex mt-4 justify-end gap-3"> |               <div className="flex mt-4 justify-end gap-3"> | ||||||
|                 <ModelSelect /> |                 <ModelSelect /> | ||||||
|  |                 <Tooltip title={t("tooltip.searchInternet")}> | ||||||
|  |                   <button | ||||||
|  |                     type="button" | ||||||
|  |                     onClick={() => setWebSearch(!webSearch)} | ||||||
|  |                     className={`inline-flex items-center gap-2   ${ | ||||||
|  |                       chatMode === "rag" ? "hidden" : "block" | ||||||
|  |                     }`}>
 | ||||||
|  |                     {webSearch ? ( | ||||||
|  |                       <Wifi className="h-5 w-5 text-gray-900 dark:text-gray-300" /> | ||||||
|  |                     ) : ( | ||||||
|  |                       <WifiOff className="h-5 w-5 text-gray-600 dark:text-gray-400" /> | ||||||
|  |                     )} | ||||||
|  |                   </button> | ||||||
|  |                 </Tooltip> | ||||||
|                 {browserSupportsSpeechRecognition && ( |                 {browserSupportsSpeechRecognition && ( | ||||||
|                   <Tooltip title={t("tooltip.speechToText")}> |                   <Tooltip title={t("tooltip.speechToText")}> | ||||||
|                     <button |                     <button | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| <html> | <html> | ||||||
|   <head> |   <head> | ||||||
|     <title>Page Assist - A Web UI for Local AI Models</title> |     <title>Page Assist - A Web UI for Local AI Models</title> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|     <meta name="manifest.type" content="browser_action" /> |     <meta name="manifest.type" content="browser_action" /> | ||||||
|     <link href="~/assets/tailwind.css" rel="stylesheet" /> |     <link href="~/assets/tailwind.css" rel="stylesheet" /> | ||||||
|     <meta charset="utf-8" /> |     <meta charset="utf-8" /> | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| <html> | <html> | ||||||
|   <head> |   <head> | ||||||
|     <title>Page Assist - A Web UI for Local AI Models</title> |     <title>Page Assist - A Web UI for Local AI Models</title> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|     <meta name="manifest.type" content="browser_action" /> |     <meta name="manifest.type" content="browser_action" /> | ||||||
|     <meta name="manifest.open_at_install" content="false" /> |     <meta name="manifest.open_at_install" content="false" /> | ||||||
|     <meta name="manifest.browser_style" content="false" /> |     <meta name="manifest.browser_style" content="false" /> | ||||||
|  | |||||||
| @ -2,11 +2,12 @@ import React from "react" | |||||||
| import { cleanUrl } from "~/libs/clean-url" | import { cleanUrl } from "~/libs/clean-url" | ||||||
| import { | import { | ||||||
|   defaultEmbeddingModelForRag, |   defaultEmbeddingModelForRag, | ||||||
|  |   geWebSearchFollowUpPrompt, | ||||||
|   getOllamaURL, |   getOllamaURL, | ||||||
|   promptForRag, |   promptForRag, | ||||||
|   systemPromptForNonRag |   systemPromptForNonRag | ||||||
| } from "~/services/ollama" | } from "~/services/ollama" | ||||||
| import { type Message } from "~/store/option" | import { useStoreMessageOption, type Message } from "~/store/option" | ||||||
| import { useStoreMessage } from "~/store" | import { useStoreMessage } from "~/store" | ||||||
| import { HumanMessage, SystemMessage } from "@langchain/core/messages" | import { HumanMessage, SystemMessage } from "@langchain/core/messages" | ||||||
| import { getDataFromCurrentTab } from "~/libs/get-html" | import { getDataFromCurrentTab } from "~/libs/get-html" | ||||||
| @ -29,6 +30,7 @@ import { useStorage } from "@plasmohq/storage/hook" | |||||||
| import { useStoreChatModelSettings } from "@/store/model" | import { useStoreChatModelSettings } from "@/store/model" | ||||||
| import { ChatOllama } from "@/models/ChatOllama" | import { ChatOllama } from "@/models/ChatOllama" | ||||||
| import { getAllDefaultModelSettings } from "@/services/model-settings" | import { getAllDefaultModelSettings } from "@/services/model-settings" | ||||||
|  | import { getSystemPromptForWeb } from "@/web/web" | ||||||
| 
 | 
 | ||||||
| export const useMessage = () => { | export const useMessage = () => { | ||||||
|   const { |   const { | ||||||
| @ -42,6 +44,9 @@ export const useMessage = () => { | |||||||
|   const { t } = useTranslation("option") |   const { t } = useTranslation("option") | ||||||
|   const [selectedModel, setSelectedModel] = useStorage("selectedModel") |   const [selectedModel, setSelectedModel] = useStorage("selectedModel") | ||||||
|   const currentChatModelSettings = useStoreChatModelSettings() |   const currentChatModelSettings = useStoreChatModelSettings() | ||||||
|  |   const { setIsSearchingInternet, webSearch, setWebSearch, isSearchingInternet } = | ||||||
|  |     useStoreMessageOption() | ||||||
|  | 
 | ||||||
|   const { |   const { | ||||||
|     history, |     history, | ||||||
|     setHistory, |     setHistory, | ||||||
| @ -571,6 +576,249 @@ export const useMessage = () => { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   const searchChatMode = async ( | ||||||
|  |     message: string, | ||||||
|  |     image: string, | ||||||
|  |     isRegenerate: boolean, | ||||||
|  |     messages: Message[], | ||||||
|  |     history: ChatHistory, | ||||||
|  |     signal: AbortSignal | ||||||
|  |   ) => { | ||||||
|  |     const url = await getOllamaURL() | ||||||
|  |     setStreaming(true) | ||||||
|  |     const userDefaultModelSettings = await getAllDefaultModelSettings() | ||||||
|  |     if (image.length > 0) { | ||||||
|  |       image = `data:image/jpeg;base64,${image.split(",")[1]}` | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const ollama = new ChatOllama({ | ||||||
|  |       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, | ||||||
|  |       seed: currentChatModelSettings?.seed | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     let newMessage: Message[] = [] | ||||||
|  |     let generateMessageId = generateID() | ||||||
|  | 
 | ||||||
|  |     if (!isRegenerate) { | ||||||
|  |       newMessage = [ | ||||||
|  |         ...messages, | ||||||
|  |         { | ||||||
|  |           isBot: false, | ||||||
|  |           name: "You", | ||||||
|  |           message, | ||||||
|  |           sources: [], | ||||||
|  |           images: [image] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           isBot: true, | ||||||
|  |           name: selectedModel, | ||||||
|  |           message: "▋", | ||||||
|  |           sources: [], | ||||||
|  |           id: generateMessageId | ||||||
|  |         } | ||||||
|  |       ] | ||||||
|  |     } else { | ||||||
|  |       newMessage = [ | ||||||
|  |         ...messages, | ||||||
|  |         { | ||||||
|  |           isBot: true, | ||||||
|  |           name: selectedModel, | ||||||
|  |           message: "▋", | ||||||
|  |           sources: [], | ||||||
|  |           id: generateMessageId | ||||||
|  |         } | ||||||
|  |       ] | ||||||
|  |     } | ||||||
|  |     setMessages(newMessage) | ||||||
|  |     let fullText = "" | ||||||
|  |     let contentToSave = "" | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       setIsSearchingInternet(true) | ||||||
|  | 
 | ||||||
|  |       let query = message | ||||||
|  | 
 | ||||||
|  |       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) | ||||||
|  |         const questionOllama = new ChatOllama({ | ||||||
|  |           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, | ||||||
|  |           seed: currentChatModelSettings?.seed | ||||||
|  |         }) | ||||||
|  |         const response = await questionOllama.invoke(promptForQuestion) | ||||||
|  |         query = response.content.toString() | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const { prompt, source } = await getSystemPromptForWeb(query) | ||||||
|  |       setIsSearchingInternet(false) | ||||||
|  | 
 | ||||||
|  |       //  message = message.trim().replaceAll("\n", " ")
 | ||||||
|  | 
 | ||||||
|  |       let humanMessage = new HumanMessage({ | ||||||
|  |         content: [ | ||||||
|  |           { | ||||||
|  |             text: message, | ||||||
|  |             type: "text" | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       }) | ||||||
|  |       if (image.length > 0) { | ||||||
|  |         humanMessage = new HumanMessage({ | ||||||
|  |           content: [ | ||||||
|  |             { | ||||||
|  |               text: message, | ||||||
|  |               type: "text" | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               image_url: image, | ||||||
|  |               type: "image_url" | ||||||
|  |             } | ||||||
|  |           ] | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const applicationChatHistory = generateHistory(history) | ||||||
|  | 
 | ||||||
|  |       if (prompt) { | ||||||
|  |         applicationChatHistory.unshift( | ||||||
|  |           new SystemMessage({ | ||||||
|  |             content: [ | ||||||
|  |               { | ||||||
|  |                 text: prompt, | ||||||
|  |                 type: "text" | ||||||
|  |               } | ||||||
|  |             ] | ||||||
|  |           }) | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const chunks = await ollama.stream( | ||||||
|  |         [...applicationChatHistory, humanMessage], | ||||||
|  |         { | ||||||
|  |           signal: signal | ||||||
|  |         } | ||||||
|  |       ) | ||||||
|  |       let count = 0 | ||||||
|  |       for await (const chunk of chunks) { | ||||||
|  |         contentToSave += chunk.content | ||||||
|  |         fullText += chunk.content | ||||||
|  |         if (count === 0) { | ||||||
|  |           setIsProcessing(true) | ||||||
|  |         } | ||||||
|  |         setMessages((prev) => { | ||||||
|  |           return prev.map((message) => { | ||||||
|  |             if (message.id === generateMessageId) { | ||||||
|  |               return { | ||||||
|  |                 ...message, | ||||||
|  |                 message: fullText + "▋" | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             return message | ||||||
|  |           }) | ||||||
|  |         }) | ||||||
|  |         count++ | ||||||
|  |       } | ||||||
|  |       // update the message with the full text
 | ||||||
|  |       setMessages((prev) => { | ||||||
|  |         return prev.map((message) => { | ||||||
|  |           if (message.id === generateMessageId) { | ||||||
|  |             return { | ||||||
|  |               ...message, | ||||||
|  |               message: fullText, | ||||||
|  |               sources: source | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           return message | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       setHistory([ | ||||||
|  |         ...history, | ||||||
|  |         { | ||||||
|  |           role: "user", | ||||||
|  |           content: message, | ||||||
|  |           image | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           role: "assistant", | ||||||
|  |           content: fullText | ||||||
|  |         } | ||||||
|  |       ]) | ||||||
|  | 
 | ||||||
|  |       await saveMessageOnSuccess({ | ||||||
|  |         historyId, | ||||||
|  |         setHistoryId, | ||||||
|  |         isRegenerate, | ||||||
|  |         selectedModel: selectedModel, | ||||||
|  |         message, | ||||||
|  |         image, | ||||||
|  |         fullText, | ||||||
|  |         source | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       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 ({ |   const onSubmit = async ({ | ||||||
|     message, |     message, | ||||||
|     image, |     image, | ||||||
| @ -597,14 +845,25 @@ export const useMessage = () => { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (chatMode === "normal") { |     if (chatMode === "normal") { | ||||||
|       await normalChatMode( |       if (webSearch) { | ||||||
|         message, |         await searchChatMode( | ||||||
|         image, |           message, | ||||||
|         isRegenerate, |           image, | ||||||
|         chatHistory || messages, |           isRegenerate || false, | ||||||
|         memory || history, |           messages, | ||||||
|         signal |           memory || history, | ||||||
|       ) |           signal | ||||||
|  |         ) | ||||||
|  |       } else { | ||||||
|  |         await normalChatMode( | ||||||
|  |           message, | ||||||
|  |           image, | ||||||
|  |           isRegenerate, | ||||||
|  |           chatHistory || messages, | ||||||
|  |           memory || history, | ||||||
|  |           signal | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|     } else { |     } else { | ||||||
|       const newEmbeddingController = new AbortController() |       const newEmbeddingController = new AbortController() | ||||||
|       let embeddingSignal = newEmbeddingController.signal |       let embeddingSignal = newEmbeddingController.signal | ||||||
| @ -714,6 +973,9 @@ export const useMessage = () => { | |||||||
|     isEmbedding, |     isEmbedding, | ||||||
|     speechToTextLanguage, |     speechToTextLanguage, | ||||||
|     setSpeechToTextLanguage, |     setSpeechToTextLanguage, | ||||||
|     regenerateLastMessage |     regenerateLastMessage, | ||||||
|  |     webSearch, | ||||||
|  |     setWebSearch, | ||||||
|  |     isSearchingInternet, | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ export default defineConfig({ | |||||||
|   outDir: "build", |   outDir: "build", | ||||||
| 
 | 
 | ||||||
|   manifest: { |   manifest: { | ||||||
|     version: "1.1.11", |     version: "1.1.12", | ||||||
|     name: |     name: | ||||||
|       process.env.TARGET === "firefox" |       process.env.TARGET === "firefox" | ||||||
|         ? "Page Assist - A Web UI for Local AI Models" |         ? "Page Assist - A Web UI for Local AI Models" | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user