Fix dropdown filter bug and add save button animation
This commit is contained in:
		
							parent
							
								
									094615498c
								
							
						
					
					
						commit
						03db4631d7
					
				| @ -1,5 +1,5 @@ | |||||||
| import { useState } from "react" | import { useState } from "react" | ||||||
| 
 | import { CheckIcon } from "lucide-react" | ||||||
| type Props = { | type Props = { | ||||||
|   onClick?: () => void |   onClick?: () => void | ||||||
|   disabled?: boolean |   disabled?: boolean | ||||||
| @ -23,13 +23,16 @@ export const SaveButton = ({ | |||||||
|       type={btnType} |       type={btnType} | ||||||
|       onClick={() => { |       onClick={() => { | ||||||
|         setClickedSave(true) |         setClickedSave(true) | ||||||
|         onClick() |         if (onClick) { | ||||||
|  |           onClick() | ||||||
|  |         } | ||||||
|         setTimeout(() => { |         setTimeout(() => { | ||||||
|           setClickedSave(false) |           setClickedSave(false) | ||||||
|         }, 1000) |         }, 1000) | ||||||
|       }} |       }} | ||||||
|       disabled={disabled} |       disabled={disabled} | ||||||
|       className={`inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm dark:bg-white dark:text-gray-800 disabled:opacity-50 ${className}`}> |       className={`inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm dark:bg-white dark:text-gray-800 disabled:opacity-50 ${className}`}> | ||||||
|  |       {clickedSave ? <CheckIcon className="w-4 h-4 mr-2" /> : null} | ||||||
|       {clickedSave ? textOnSave : text} |       {clickedSave ? textOnSave : text} | ||||||
|     </button> |     </button> | ||||||
|   ) |   ) | ||||||
|  | |||||||
| @ -262,9 +262,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { | |||||||
|                   {!isSending ? ( |                   {!isSending ? ( | ||||||
|                     <Dropdown.Button |                     <Dropdown.Button | ||||||
|                       htmlType="submit" |                       htmlType="submit" | ||||||
|                       disabled={ |                       disabled={isSending} | ||||||
|                         isSending || form.values.message.trim().length === 0 |  | ||||||
|                       } |  | ||||||
|                       className="!justify-end !w-auto" |                       className="!justify-end !w-auto" | ||||||
|                       icon={ |                       icon={ | ||||||
|                         <svg |                         <svg | ||||||
| @ -287,7 +285,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { | |||||||
|                             key: 1, |                             key: 1, | ||||||
|                             label: ( |                             label: ( | ||||||
|                               <Checkbox |                               <Checkbox | ||||||
|                                 value={sendWhenEnter} |                                 checked={sendWhenEnter} | ||||||
|                                 onChange={(e) => |                                 onChange={(e) => | ||||||
|                                   setSendWhenEnter(e.target.checked) |                                   setSendWhenEnter(e.target.checked) | ||||||
|                                 }> |                                 }> | ||||||
|  | |||||||
| @ -1,54 +1,144 @@ | |||||||
| import { useQuery } from "@tanstack/react-query" | import { useMutation, useQuery } from "@tanstack/react-query" | ||||||
| import { useEffect, useState } from "react" | import { Form, InputNumber, Select, Skeleton } from "antd" | ||||||
|  | import { useState } from "react" | ||||||
| import { SaveButton } from "~components/Common/SaveButton" | import { SaveButton } from "~components/Common/SaveButton" | ||||||
| import { getOllamaURL, setOllamaURL as saveOllamaURL } from "~services/ollama" | import { | ||||||
|  |   defaultEmbeddingChunkOverlap, | ||||||
|  |   defaultEmbeddingChunkSize, | ||||||
|  |   defaultEmbeddingModelForRag, | ||||||
|  |   getAllModels, | ||||||
|  |   getOllamaURL, | ||||||
|  |   saveForRag, | ||||||
|  |   setOllamaURL as saveOllamaURL | ||||||
|  | } from "~services/ollama" | ||||||
| 
 | 
 | ||||||
| export const SettingsOllama = () => { | export const SettingsOllama = () => { | ||||||
|   const [ollamaURL, setOllamaURL] = useState<string>("") |   const [ollamaURL, setOllamaURL] = useState<string>("") | ||||||
|   const { data: ollamaInfo } = useQuery({ |   const { data: ollamaInfo, status } = useQuery({ | ||||||
|     queryKey: ["fetchOllamURL"], |     queryKey: ["fetchOllamURL"], | ||||||
|     queryFn: async () => { |     queryFn: async () => { | ||||||
|       const ollamaURL = await getOllamaURL() |       const [ollamaURL, allModels, chunkOverlap, chunkSize, defaultEM] = | ||||||
|  |         await Promise.all([ | ||||||
|  |           getOllamaURL(), | ||||||
|  |           getAllModels(), | ||||||
|  |           defaultEmbeddingChunkOverlap(), | ||||||
|  |           defaultEmbeddingChunkSize(), | ||||||
|  |           defaultEmbeddingModelForRag() | ||||||
|  |         ]) | ||||||
|  |       setOllamaURL(ollamaURL) | ||||||
|       return { |       return { | ||||||
|         ollamaURL |         models: allModels, | ||||||
|  |         chunkOverlap, | ||||||
|  |         chunkSize, | ||||||
|  |         defaultEM | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|    |   const { mutate: saveRAG, isPending: isSaveRAGPending } = useMutation({ | ||||||
|   useEffect(() => { |     mutationFn: async (data: { | ||||||
|     if (ollamaInfo?.ollamaURL) { |       model: string | ||||||
|       setOllamaURL(ollamaInfo.ollamaURL) |       chunkSize: number | ||||||
|  |       overlap: number | ||||||
|  |     }) => { | ||||||
|  |       await saveForRag(data.model, data.chunkSize, data.overlap) | ||||||
|     } |     } | ||||||
|   }, [ollamaInfo]) |   }) | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className=""> |     <div className="flex flex-col gap-3"> | ||||||
|       <div> |       {status === "pending" && <Skeleton paragraph={{ rows: 4 }} active />} | ||||||
|         <label |       {status === "success" && ( | ||||||
|           htmlFor="ollamaURL" |         <> | ||||||
|           className="text-sm font-medium dark:text-gray-200"> |           <div> | ||||||
|           Ollama URL |             <label | ||||||
|         </label> |               htmlFor="ollamaURL" | ||||||
|         <input |               className="text-sm font-medium dark:text-gray-200"> | ||||||
|           type="url" |               Ollama URL | ||||||
|           id="ollamaURL" |             </label> | ||||||
|           value={ollamaURL} |             <input | ||||||
|           onChange={(e) => { |               type="url" | ||||||
|             setOllamaURL(e.target.value) |               id="ollamaURL" | ||||||
|           }} |               value={ollamaURL} | ||||||
|           placeholder="Your Ollama URL" |               onChange={(e) => { | ||||||
|           className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100" |                 setOllamaURL(e.target.value) | ||||||
|         /> |               }} | ||||||
|       </div> |               placeholder="Your Ollama URL" | ||||||
|       <div className="flex justify-end"> |               className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100" | ||||||
|         <SaveButton |             /> | ||||||
|           onClick={() => { |           </div> | ||||||
|             saveOllamaURL(ollamaURL) |           <div className="flex justify-end"> | ||||||
|           }} |             <SaveButton | ||||||
|           className="mt-2" |               onClick={() => { | ||||||
|         /> |                 saveOllamaURL(ollamaURL) | ||||||
|       </div> |               }} | ||||||
|  |               className="mt-2" | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  | 
 | ||||||
|  |           <Form | ||||||
|  |             layout="vertical" | ||||||
|  |             onFinish={(data) => { | ||||||
|  |               saveRAG({ | ||||||
|  |                 model: data.defaultEM, | ||||||
|  |                 chunkSize: data.chunkSize, | ||||||
|  |                 overlap: data.chunkOverlap | ||||||
|  |               }) | ||||||
|  |             }} | ||||||
|  |             initialValues={{ | ||||||
|  |               chunkSize: ollamaInfo?.chunkSize, | ||||||
|  |               chunkOverlap: ollamaInfo?.chunkOverlap, | ||||||
|  |               defaultEM: ollamaInfo?.defaultEM | ||||||
|  |             }}> | ||||||
|  |             <Form.Item | ||||||
|  |               name="defaultEM" | ||||||
|  |               label="Embedding Model" | ||||||
|  |               help="Highly recommended to use embedding models like `nomic-embed-text`." | ||||||
|  |               rules={[{ required: true, message: "Please select a model!" }]}> | ||||||
|  |               <Select | ||||||
|  |                 size="large" | ||||||
|  |                 filterOption={(input, option) => | ||||||
|  |                   option.label.toLowerCase().indexOf(input.toLowerCase()) >= | ||||||
|  |                     0 || | ||||||
|  |                   option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 | ||||||
|  |                 } | ||||||
|  |                 showSearch | ||||||
|  |                 placeholder="Select a model" | ||||||
|  |                 style={{ width: "100%" }} | ||||||
|  |                 className="mt-4" | ||||||
|  |                 options={ollamaInfo.models?.map((model) => ({ | ||||||
|  |                   label: model.name, | ||||||
|  |                   value: model.model | ||||||
|  |                 }))} | ||||||
|  |               /> | ||||||
|  |             </Form.Item> | ||||||
|  | 
 | ||||||
|  |             <Form.Item | ||||||
|  |               name="chunkSize" | ||||||
|  |               label="Chunk Size" | ||||||
|  |               rules={[ | ||||||
|  |                 { required: true, message: "Please input your chunk size!" } | ||||||
|  |               ]}> | ||||||
|  |               <InputNumber style={{ width: "100%" }} placeholder="Chunk Size" /> | ||||||
|  |             </Form.Item> | ||||||
|  |             <Form.Item | ||||||
|  |               name="chunkOverlap" | ||||||
|  |               label="Chunk Overlap" | ||||||
|  |               rules={[ | ||||||
|  |                 { required: true, message: "Please input your chunk overlap!" } | ||||||
|  |               ]}> | ||||||
|  |               <InputNumber | ||||||
|  |                 style={{ width: "100%" }} | ||||||
|  |                 placeholder="Chunk Overlap" | ||||||
|  |               /> | ||||||
|  |             </Form.Item> | ||||||
|  | 
 | ||||||
|  |             <div className="flex justify-end"> | ||||||
|  |               <SaveButton disabled={isSaveRAGPending} btnType="submit" /> | ||||||
|  |             </div> | ||||||
|  |           </Form> | ||||||
|  |         </> | ||||||
|  |       )} | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,9 +28,9 @@ export const SettingOther = () => { | |||||||
|           options={SUPPORTED_LANGUAGES} |           options={SUPPORTED_LANGUAGES} | ||||||
|           value={speechToTextLanguage} |           value={speechToTextLanguage} | ||||||
|           filterOption={(input, option) => |           filterOption={(input, option) => | ||||||
|                 option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || |             option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || | ||||||
|                 option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 |             option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 | ||||||
|               } |           } | ||||||
|           onChange={(value) => { |           onChange={(value) => { | ||||||
|             setSpeechToTextLanguage(value) |             setSpeechToTextLanguage(value) | ||||||
|           }} |           }} | ||||||
|  | |||||||
| @ -1,56 +1,146 @@ | |||||||
| import { useQuery } from "@tanstack/react-query" | import { useQuery, useQueryClient } from "@tanstack/react-query" | ||||||
| import { useEffect, useState } from "react" | import { Skeleton, Radio, Form } from "antd" | ||||||
|  | import React from "react" | ||||||
| import { SaveButton } from "~components/Common/SaveButton" | import { SaveButton } from "~components/Common/SaveButton" | ||||||
| import { | import { | ||||||
|  |   getWebSearchPrompt, | ||||||
|   setSystemPromptForNonRagOption, |   setSystemPromptForNonRagOption, | ||||||
|   systemPromptForNonRagOption |   systemPromptForNonRagOption, | ||||||
|  |   geWebSearchFollowUpPrompt, | ||||||
|  |   setWebPrompts | ||||||
| } from "~services/ollama" | } from "~services/ollama" | ||||||
| 
 | 
 | ||||||
| export const SettingPrompt = () => { | export const SettingPrompt = () => { | ||||||
|   const [ollamaPrompt, setOllamaPrompt] = useState<string>("") |   const [selectedValue, setSelectedValue] = React.useState<"normal" | "web">( | ||||||
|   const { data: ollamaInfo } = useQuery({ |     "normal" | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|  |   const queryClient = useQueryClient() | ||||||
|  | 
 | ||||||
|  |   const { status, data } = useQuery({ | ||||||
|     queryKey: ["fetchOllaPrompt"], |     queryKey: ["fetchOllaPrompt"], | ||||||
|     queryFn: async () => { |     queryFn: async () => { | ||||||
|       const prompt = await systemPromptForNonRagOption() |       const [prompt, webSearchPrompt, webSearchFollowUpPrompt] = | ||||||
|  |         await Promise.all([ | ||||||
|  |           systemPromptForNonRagOption(), | ||||||
|  |           getWebSearchPrompt(), | ||||||
|  |           geWebSearchFollowUpPrompt() | ||||||
|  |         ]) | ||||||
| 
 | 
 | ||||||
|       return { |       return { | ||||||
|         prompt |         prompt, | ||||||
|  |         webSearchPrompt, | ||||||
|  |         webSearchFollowUpPrompt | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |  | ||||||
|     if (ollamaInfo?.prompt) { |  | ||||||
|       setOllamaPrompt(ollamaInfo.prompt) |  | ||||||
|     } |  | ||||||
|   }, [ollamaInfo]) |  | ||||||
| 
 |  | ||||||
|   return ( |   return ( | ||||||
|     <div className=""> |     <div className="flex flex-col gap-3"> | ||||||
|       <div> |       {status === "pending" && <Skeleton paragraph={{ rows: 4 }} active />} | ||||||
|         <label htmlFor="ollamaPrompt" className="text-sm font-medium dark:text-gray-200"> |  | ||||||
|             System Prompt |  | ||||||
|         </label> |  | ||||||
|       <textarea |  | ||||||
|         value={ollamaPrompt} |  | ||||||
|         rows={5} |  | ||||||
|         id="ollamaPrompt" |  | ||||||
|         placeholder="Your System Prompt" |  | ||||||
|         onChange={(e) => { |  | ||||||
|           setOllamaPrompt(e.target.value) |  | ||||||
|         }} |  | ||||||
|         className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100" |  | ||||||
|       /> |  | ||||||
|       </div> |  | ||||||
| 
 | 
 | ||||||
|       <div className="flex justify-end"> |       {status === "success" && ( | ||||||
|       <SaveButton |         <div> | ||||||
|         onClick={() => { |           <h2 className="text-md font-semibold dark:text-white">Prompt</h2> | ||||||
|           setSystemPromptForNonRagOption(ollamaPrompt) |           <div className="my-3 flex justify-end"> | ||||||
|         }} |             <Radio.Group | ||||||
|         className="mt-2" |               defaultValue={selectedValue} | ||||||
|       /> |               onChange={(e) => setSelectedValue(e.target.value)}> | ||||||
|       </div> |               <Radio.Button value="normal">Normal</Radio.Button> | ||||||
|  |               <Radio.Button value="web">Web</Radio.Button> | ||||||
|  |             </Radio.Group> | ||||||
|  |           </div> | ||||||
|  | 
 | ||||||
|  |           {selectedValue === "normal" && ( | ||||||
|  |             <Form | ||||||
|  |               layout="vertical" | ||||||
|  |               onFinish={(values) => { | ||||||
|  |                 setSystemPromptForNonRagOption(values?.prompt || "") | ||||||
|  |                 queryClient.invalidateQueries({ | ||||||
|  |                   queryKey: ["fetchOllaPrompt"] | ||||||
|  |                 }) | ||||||
|  |               }} | ||||||
|  |               initialValues={{ | ||||||
|  |                 prompt: data.prompt | ||||||
|  |               }}> | ||||||
|  |               <Form.Item label="System Prompt" name="prompt"> | ||||||
|  |                 <textarea | ||||||
|  |                   value={data.prompt} | ||||||
|  |                   rows={5} | ||||||
|  |                   id="ollamaPrompt" | ||||||
|  |                   placeholder="Your System Prompt" | ||||||
|  |                   className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100" | ||||||
|  |                 /> | ||||||
|  |               </Form.Item> | ||||||
|  |               <Form.Item> | ||||||
|  |                 <div className="flex justify-end"> | ||||||
|  |                   <SaveButton btnType="submit" /> | ||||||
|  |                 </div>{" "} | ||||||
|  |               </Form.Item> | ||||||
|  |             </Form> | ||||||
|  |           )} | ||||||
|  | 
 | ||||||
|  |           {selectedValue === "web" && ( | ||||||
|  |             <Form | ||||||
|  |               layout="vertical" | ||||||
|  |               onFinish={(values) => { | ||||||
|  |                 setWebPrompts( | ||||||
|  |                   values?.webSearchPrompt || "", | ||||||
|  |                   values?.webSearchFollowUpPrompt || "" | ||||||
|  |                 ) | ||||||
|  |                 queryClient.invalidateQueries({ | ||||||
|  |                   queryKey: ["fetchOllaPrompt"] | ||||||
|  |                 }) | ||||||
|  |               }} | ||||||
|  |               initialValues={{ | ||||||
|  |                 webSearchPrompt: data.webSearchPrompt, | ||||||
|  |                 webSearchFollowUpPrompt: data.webSearchFollowUpPrompt | ||||||
|  |               }}> | ||||||
|  |               <Form.Item | ||||||
|  |                 label="Web Search Prompt" | ||||||
|  |                 name="webSearchPrompt" | ||||||
|  |                 help="Do not remove `{search_results}` from the prompt." | ||||||
|  |                 rules={[ | ||||||
|  |                   { | ||||||
|  |                     required: true, | ||||||
|  |                     message: "Please input your Web Search Prompt!" | ||||||
|  |                   } | ||||||
|  |                 ]}> | ||||||
|  |                 <textarea | ||||||
|  |                   value={data.webSearchPrompt} | ||||||
|  |                   rows={5} | ||||||
|  |                   id="ollamaWebSearchPrompt" | ||||||
|  |                   placeholder="Your Web Search Prompt" | ||||||
|  |                   className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100" | ||||||
|  |                 /> | ||||||
|  |               </Form.Item> | ||||||
|  |               <Form.Item | ||||||
|  |                 label="Web Search Follow Up Prompt" | ||||||
|  |                 name="webSearchFollowUpPrompt" | ||||||
|  |                 help="Do not remove `{chat_history}` and `{question}` from the prompt." | ||||||
|  |                 rules={[ | ||||||
|  |                   { | ||||||
|  |                     required: true, | ||||||
|  |                     message: "Please input your Web Search Follow Up Prompt!" | ||||||
|  |                   } | ||||||
|  |                 ]}> | ||||||
|  |                 <textarea | ||||||
|  |                   value={data.webSearchFollowUpPrompt} | ||||||
|  |                   rows={5} | ||||||
|  |                   id="ollamaWebSearchFollowUpPrompt" | ||||||
|  |                   placeholder="Your Web Search Follow Up Prompt" | ||||||
|  |                   className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100" | ||||||
|  |                 /> | ||||||
|  |               </Form.Item> | ||||||
|  |               <Form.Item> | ||||||
|  |                 <div className="flex justify-end"> | ||||||
|  |                   <SaveButton btnType="submit" /> | ||||||
|  |                 </div>{" "} | ||||||
|  |               </Form.Item> | ||||||
|  |             </Form> | ||||||
|  |           )} | ||||||
|  |         </div> | ||||||
|  |       )} | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -212,7 +212,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | |||||||
|                 <Dropdown.Button |                 <Dropdown.Button | ||||||
|                   htmlType="submit" |                   htmlType="submit" | ||||||
|                   disabled={ |                   disabled={ | ||||||
|                     isSending || form.values.message.trim().length === 0 |                     isSending | ||||||
|                   } |                   } | ||||||
|                   className="!justify-end !w-auto" |                   className="!justify-end !w-auto" | ||||||
|                   icon={ |                   icon={ | ||||||
| @ -236,7 +236,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | |||||||
|                         key: 1, |                         key: 1, | ||||||
|                         label: ( |                         label: ( | ||||||
|                           <Checkbox |                           <Checkbox | ||||||
|                             value={sendWhenEnter} |                             checked={sendWhenEnter} | ||||||
|                             onChange={(e) => |                             onChange={(e) => | ||||||
|                               setSendWhenEnter(e.target.checked) |                               setSendWhenEnter(e.target.checked) | ||||||
|                             }> |                             }> | ||||||
|  | |||||||
| @ -280,3 +280,9 @@ export const geWebSearchFollowUpPrompt = async () => { | |||||||
| export const setWebSearchFollowUpPrompt = async (prompt: string) => { | export const setWebSearchFollowUpPrompt = async (prompt: string) => { | ||||||
|   await storage.set("webSearchFollowUpPrompt", prompt) |   await storage.set("webSearchFollowUpPrompt", prompt) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export const setWebPrompts = async (prompt: string, followUpPrompt: string) => { | ||||||
|  |   await setWebSearchPrompt(prompt) | ||||||
|  |   await setWebSearchFollowUpPrompt(followUpPrompt) | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user