Fix dropdown filter bug and add save button animation

This commit is contained in:
n4ze3m 2024-02-25 23:45:07 +05:30
parent 094615498c
commit 03db4631d7
7 changed files with 272 additions and 85 deletions

View File

@ -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>
) )

View File

@ -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)
}> }>

View File

@ -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>
) )
} }

View File

@ -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)
}} }}

View File

@ -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>
) )
} }

View File

@ -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)
}> }>

View File

@ -279,4 +279,10 @@ 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)
} }