Update component imports and add conditional focus in PlaygroundForm

This commit is contained in:
n4ze3m 2024-03-03 23:30:42 +05:30
parent 0351beeaae
commit a87c56061c
12 changed files with 180 additions and 25 deletions

View File

@ -1,7 +1,7 @@
import React, { useState } from "react" import React, { useState } from "react"
import { useLocation, NavLink } from "react-router-dom" import { useLocation, NavLink } from "react-router-dom"
import { Sidebar } from "./Sidebar" import { Sidebar } from "../Option/Sidebar"
import { Drawer, Select, Tooltip } from "antd" import { Drawer, Select, Tooltip } from "antd"
import { useQuery } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
import { getAllModels } from "~services/ollama" import { getAllModels } from "~services/ollama"
@ -9,10 +9,13 @@ import { useMessageOption } from "~hooks/useMessageOption"
import { import {
ChevronLeft, ChevronLeft,
CogIcon, CogIcon,
ComputerIcon,
GithubIcon, GithubIcon,
PanelLeftIcon, PanelLeftIcon,
SquarePen SquarePen,
ZapIcon
} from "lucide-react" } from "lucide-react"
import { getAllPrompts } from "~libs/db"
export default function OptionLayout({ export default function OptionLayout({
children children
@ -20,6 +23,14 @@ export default function OptionLayout({
children: React.ReactNode children: React.ReactNode
}) { }) {
const [sidebarOpen, setSidebarOpen] = useState(false) const [sidebarOpen, setSidebarOpen] = useState(false)
const {
selectedModel,
setSelectedModel,
clearChat,
selectedSystemPrompt,
setSelectedQuickPrompt,
setSelectedSystemPrompt
} = useMessageOption()
const { const {
data: models, data: models,
@ -27,12 +38,30 @@ export default function OptionLayout({
isFetching: isModelsFetching isFetching: isModelsFetching
} = useQuery({ } = useQuery({
queryKey: ["fetchModel"], queryKey: ["fetchModel"],
queryFn: getAllModels, queryFn: () => getAllModels({ returnEmpty: true }),
refetchInterval: 15000 refetchInterval: 15000
}) })
const { data: prompts, isLoading: isPromptLoading } = useQuery({
queryKey: ["fetchAllPromptsLayout"],
queryFn: getAllPrompts
})
const { pathname } = useLocation() const { pathname } = useLocation()
const { selectedModel, setSelectedModel, clearChat } = useMessageOption()
const getPromptInfoById = (id: string) => {
return prompts?.find((prompt) => prompt.id === id)
}
const handlePromptChange = (value: string) => {
const prompt = getPromptInfoById(value)
if (prompt?.is_system) {
setSelectedSystemPrompt(prompt.id)
} else {
setSelectedQuickPrompt(prompt.content)
setSelectedSystemPrompt(null)
}
}
return ( return (
<div> <div>
@ -87,6 +116,41 @@ export default function OptionLayout({
}))} }))}
/> />
</div> </div>
<span className="text-lg font-thin text-zinc-300 dark:text-zinc-600">
{"/"}
</span>
<div>
<Select
size="large"
loading={isPromptLoading}
showSearch
placeholder="Select a prompt"
className="w-60"
allowClear
onChange={handlePromptChange}
value={selectedSystemPrompt}
filterOption={(input, option) =>
option.label.key
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
}
options={prompts?.map((prompt) => ({
label: (
<span
key={prompt.title}
className="flex flex-row justify-between items-center">
{prompt.title}
{prompt.is_system ? (
<ComputerIcon className="w-4 h-4" />
) : (
<ZapIcon className="w-4 h-4" />
)}
</span>
),
value: prompt.id
}))}
/>
</div>
</div> </div>
<div className="flex flex-1 justify-end px-4"> <div className="flex flex-1 justify-end px-4">
<div className="ml-4 flex items-center md:ml-6"> <div className="ml-4 flex items-center md:ml-6">

View File

@ -10,6 +10,7 @@ import { useSpeechRecognition } from "~hooks/useSpeechRecognition"
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 } from "lucide-react"
import { getVariable } from "~utils/select-varaible"
type Props = { type Props = {
dropedFile: File | undefined dropedFile: File | undefined
@ -21,7 +22,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
const textAreaFocus = () => { const textAreaFocus = () => {
if (textareaRef.current) { if (textareaRef.current) {
textareaRef.current.focus() if (
textareaRef.current.selectionStart === textareaRef.current.selectionEnd
) {
textareaRef.current.focus()
}
} }
} }
const form = useForm({ const form = useForm({
@ -65,7 +70,9 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
stopStreamingRequest, stopStreamingRequest,
streaming: isSending, streaming: isSending,
webSearch, webSearch,
setWebSearch setWebSearch,
selectedQuickPrompt,
setSelectedQuickPrompt
} = useMessageOption() } = useMessageOption()
const { isListening, start, stop, transcript } = useSpeechRecognition() const { isListening, start, stop, transcript } = useSpeechRecognition()
@ -77,6 +84,22 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
} }
}, [transcript]) }, [transcript])
React.useEffect(() => {
if (selectedQuickPrompt) {
const word = getVariable(selectedQuickPrompt)
form.setFieldValue("message", selectedQuickPrompt)
if (word) {
textareaRef.current?.focus()
const interval = setTimeout(() => {
textareaRef.current?.setSelectionRange(word.start, word.end)
}, 100)
return () => {
clearInterval(interval)
}
}
}
}, [selectedQuickPrompt])
const queryClient = useQueryClient() const queryClient = useQueryClient()
const { mutateAsync: sendMessage } = useMutation({ const { mutateAsync: sendMessage } = useMutation({

View File

@ -2,7 +2,6 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import { import {
Skeleton, Skeleton,
Table, Table,
Tag,
Tooltip, Tooltip,
notification, notification,
Modal, Modal,
@ -10,7 +9,7 @@ import {
Form, Form,
Switch Switch
} from "antd" } from "antd"
import { Trash2, Pen } from "lucide-react" import { Trash2, Pen, Computer, Zap } from "lucide-react"
import { useState } from "react" import { useState } from "react"
import { import {
deletePromptById, deletePromptById,
@ -131,14 +130,21 @@ export const PromptBody = () => {
key: "content" key: "content"
}, },
{ {
title: "Is System Prompt", title: "Prompt Type",
dataIndex: "is_system", dataIndex: "is_system",
key: "is_system", key: "is_system",
render: (is_system) => ( render: (is_system) =>
<Tag color={is_system ? "green" : "blue"}> is_system ? (
{is_system ? "Yes" : "No"} <span className="flex justify-between">
</Tag> <Computer className="w-5 h-5 mr-3" />
) System Prompt
</span>
) : (
<span className="flex justify-between">
<Zap className="w-5 h-5 mr-3" />
Quick Prompt
</span>
)
}, },
{ {
title: "Action", title: "Action",

View File

@ -14,7 +14,12 @@ import {
SystemMessage SystemMessage
} from "@langchain/core/messages" } from "@langchain/core/messages"
import { useStoreMessageOption } from "~store/option" import { useStoreMessageOption } from "~store/option"
import { removeMessageUsingHistoryId, saveHistory, saveMessage } from "~libs/db" import {
getPromptById,
removeMessageUsingHistoryId,
saveHistory,
saveMessage
} from "~libs/db"
import { useNavigate } from "react-router-dom" import { useNavigate } from "react-router-dom"
import { notification } from "antd" import { notification } from "antd"
import { getSystemPromptForWeb } from "~web/web" import { getSystemPromptForWeb } from "~web/web"
@ -102,7 +107,11 @@ export const useMessageOption = () => {
webSearch, webSearch,
setWebSearch, setWebSearch,
isSearchingInternet, isSearchingInternet,
setIsSearchingInternet setIsSearchingInternet,
selectedQuickPrompt,
setSelectedQuickPrompt,
selectedSystemPrompt,
setSelectedSystemPrompt
} = useStoreMessageOption() } = useStoreMessageOption()
const navigate = useNavigate() const navigate = useNavigate()
@ -310,7 +319,7 @@ export const useMessageOption = () => {
setIsProcessing(false) setIsProcessing(false)
setStreaming(false) setStreaming(false)
} catch (e) { } catch (e) {
(e) e
if (e?.name === "AbortError") { if (e?.name === "AbortError") {
newMessage[appendingIndex].message = newMessage[ newMessage[appendingIndex].message = newMessage[
@ -406,6 +415,7 @@ export const useMessageOption = () => {
try { try {
const prompt = await systemPromptForNonRagOption() const prompt = await systemPromptForNonRagOption()
const selectedPrompt = await getPromptById(selectedSystemPrompt)
message = message.trim().replaceAll("\n", " ") message = message.trim().replaceAll("\n", " ")
@ -434,7 +444,7 @@ export const useMessageOption = () => {
const applicationChatHistory = generateHistory(history) const applicationChatHistory = generateHistory(history)
if (prompt) { if (prompt && !selectedPrompt) {
applicationChatHistory.unshift( applicationChatHistory.unshift(
new SystemMessage({ new SystemMessage({
content: [ content: [
@ -447,6 +457,19 @@ export const useMessageOption = () => {
) )
} }
if (selectedPrompt) {
applicationChatHistory.unshift(
new SystemMessage({
content: [
{
text: selectedPrompt.content,
type: "text"
}
]
})
)
}
const chunks = await ollama.stream( const chunks = await ollama.stream(
[...applicationChatHistory, humanMessage], [...applicationChatHistory, humanMessage],
{ {
@ -526,7 +549,6 @@ export const useMessageOption = () => {
setIsProcessing(false) setIsProcessing(false)
setStreaming(false) setStreaming(false)
} catch (e) { } catch (e) {
if (e?.name === "AbortError") { if (e?.name === "AbortError") {
newMessage[appendingIndex].message = newMessage[ newMessage[appendingIndex].message = newMessage[
appendingIndex appendingIndex
@ -644,6 +666,11 @@ export const useMessageOption = () => {
regenerateLastMessage, regenerateLastMessage,
webSearch, webSearch,
setWebSearch, setWebSearch,
isSearchingInternet isSearchingInternet,
setIsSearchingInternet,
selectedQuickPrompt,
setSelectedQuickPrompt,
selectedSystemPrompt,
setSelectedSystemPrompt
} }
} }

View File

@ -274,6 +274,7 @@ export const updatePrompt = async ({ content, id, title, is_system }: { id: stri
export const getPromptById = async (id: string) => { export const getPromptById = async (id: string) => {
if (!id || id.trim() === "") return null
const db = new PageAssitDatabase() const db = new PageAssitDatabase()
return await db.getPromptById(id) return await db.getPromptById(id)
} }

View File

@ -1,4 +1,4 @@
import OptionLayout from "~components/Option/Layout" import OptionLayout from "~components/Layouts/Layout"
import { Playground } from "~components/Option/Playground/Playground" import { Playground } from "~components/Option/Playground/Playground"
export const OptionIndex = () => { export const OptionIndex = () => {

View File

@ -1,5 +1,5 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout" import OptionLayout from "~components/Layouts/Layout"
import { ModelsBody } from "~components/Option/Models" import { ModelsBody } from "~components/Option/Models"
export const OptionModal = () => { export const OptionModal = () => {

View File

@ -1,5 +1,5 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout" import OptionLayout from "~components/Layouts/Layout"
import { PromptBody } from "~components/Option/Prompt" import { PromptBody } from "~components/Option/Prompt"
export const OptionPrompt = () => { export const OptionPrompt = () => {

View File

@ -1,5 +1,5 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout" import OptionLayout from "~components/Layouts/Layout"
import { SettingOther } from "~components/Option/Settings/other" import { SettingOther } from "~components/Option/Settings/other"
export const OptionSettings = () => { export const OptionSettings = () => {

View File

@ -1,5 +1,5 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout" import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout" import OptionLayout from "~components/Layouts/Layout"
import { SettingsOllama } from "~components/Option/Settings/ollama" import { SettingsOllama } from "~components/Option/Settings/ollama"
export const OptionOllamaSettings = () => { export const OptionOllamaSettings = () => {

View File

@ -51,6 +51,12 @@ type State = {
setWebSearch: (webSearch: boolean) => void; setWebSearch: (webSearch: boolean) => void;
isSearchingInternet: boolean; isSearchingInternet: boolean;
setIsSearchingInternet: (isSearchingInternet: boolean) => void; setIsSearchingInternet: (isSearchingInternet: boolean) => void;
selectedSystemPrompt: string | null
setSelectedSystemPrompt: (selectedSystemPrompt: string) => void
selectedQuickPrompt: string | null
setSelectedQuickPrompt: (selectedQuickPrompt: string) => void
} }
export const useStoreMessageOption = create<State>((set) => ({ export const useStoreMessageOption = create<State>((set) => ({
@ -81,4 +87,8 @@ export const useStoreMessageOption = create<State>((set) => ({
setWebSearch: (webSearch) => set({ webSearch }), setWebSearch: (webSearch) => set({ webSearch }),
isSearchingInternet: false, isSearchingInternet: false,
setIsSearchingInternet: (isSearchingInternet) => set({ isSearchingInternet }), setIsSearchingInternet: (isSearchingInternet) => set({ isSearchingInternet }),
selectedSystemPrompt: null,
setSelectedSystemPrompt: (selectedSystemPrompt) => set({ selectedSystemPrompt }),
selectedQuickPrompt: null,
setSelectedQuickPrompt: (selectedQuickPrompt) => set({ selectedQuickPrompt }),
})) }))

View File

@ -0,0 +1,24 @@
export const getVariable = (text: string) => {
const regex = /{([^}]+)}/g;
let data : {
word: string,
start: number,
end: number
} | null = null;
let m: RegExpExecArray | null;
while ((m = regex.exec(text)) !== null) {
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
data = {
word: m[1],
start: m.index,
end: m.index + m[0].length
}
}
return data;
}