Update component imports and add conditional focus in PlaygroundForm
This commit is contained in:
parent
0351beeaae
commit
a87c56061c
@ -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">
|
@ -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({
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
@ -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 = () => {
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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 }),
|
||||||
}))
|
}))
|
||||||
|
24
src/utils/select-varaible.ts
Normal file
24
src/utils/select-varaible.ts
Normal 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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user