Update dependencies and fix import paths
This commit is contained in:
@@ -7,7 +7,7 @@ import React from "react"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { getPageShareUrl } from "~/services/ollama"
|
||||
import { cleanUrl } from "~/libs/clean-url"
|
||||
import { getUserId, saveWebshare } from "~/libs/db"
|
||||
import { getUserId, saveWebshare } from "@/db"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
SquarePen,
|
||||
ZapIcon
|
||||
} from "lucide-react"
|
||||
import { getAllPrompts } from "~/libs/db"
|
||||
import { getAllPrompts } from "@/db"
|
||||
import { ShareBtn } from "~/components/Common/ShareBtn"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
|
||||
@@ -68,12 +68,12 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
current={location.pathname}
|
||||
icon={BrainCircuit}
|
||||
/>
|
||||
{/* <LinkComponent
|
||||
<LinkComponent
|
||||
href="/settings/knowledge"
|
||||
name={t("manageKnowledge.title")}
|
||||
icon={BlocksIcon}
|
||||
current={location.pathname}
|
||||
/> */}
|
||||
/>
|
||||
<LinkComponent
|
||||
href="/settings/prompt"
|
||||
name={t("managePrompts.title")}
|
||||
|
||||
139
src/components/Option/Knowledge/AddKnowledge.tsx
Normal file
139
src/components/Option/Knowledge/AddKnowledge.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
import { Source, createKnowledge } from "@/db/knowledge"
|
||||
import { defaultEmbeddingModelForRag } from "@/services/ollama"
|
||||
import { convertToSource } from "@/utils/to-source"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { Modal, Form, Input, Upload, message, UploadFile } from "antd"
|
||||
import { InboxIcon } from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import PubSub from "pubsub-js"
|
||||
import { KNOWLEDGE_QUEUE } from "@/queue"
|
||||
|
||||
type Props = {
|
||||
open: boolean
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
export const AddKnowledge = ({ open, setOpen }: Props) => {
|
||||
const { t } = useTranslation("knowledge")
|
||||
const [form] = Form.useForm()
|
||||
|
||||
const onUploadHandler = async (data: {
|
||||
title: string
|
||||
file: UploadFile[]
|
||||
}) => {
|
||||
const defaultEM = await defaultEmbeddingModelForRag()
|
||||
|
||||
if (!defaultEM) {
|
||||
throw new Error(t("noEmbeddingModel"))
|
||||
}
|
||||
|
||||
const source: Source[] = []
|
||||
|
||||
for (const file of data.file) {
|
||||
const data = await convertToSource(file)
|
||||
source.push(data)
|
||||
}
|
||||
|
||||
const knowledge = await createKnowledge({
|
||||
embedding_model: defaultEM,
|
||||
source,
|
||||
title: data.title
|
||||
})
|
||||
|
||||
return knowledge.id
|
||||
}
|
||||
|
||||
const { mutate: saveKnowledge, isPending: isSaving } = useMutation({
|
||||
mutationFn: onUploadHandler,
|
||||
onError: (error) => {
|
||||
message.error(error.message)
|
||||
},
|
||||
onSuccess: async (id) => {
|
||||
message.success(t("form.success"))
|
||||
PubSub.publish(KNOWLEDGE_QUEUE, id)
|
||||
setOpen(false)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("addKnowledge")}
|
||||
open={open}
|
||||
footer={null}
|
||||
onCancel={() => setOpen(false)}>
|
||||
<Form onFinish={saveKnowledge} form={form} layout="vertical">
|
||||
<Form.Item
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("form.title.required")
|
||||
}
|
||||
]}
|
||||
name="title"
|
||||
label={t("form.title.label")}>
|
||||
<Input size="large" placeholder={t("form.title.placeholder")} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="file"
|
||||
label={t("form.uploadFile.label")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("form.uploadFile.required")
|
||||
}
|
||||
]}
|
||||
getValueFromEvent={(e) => {
|
||||
if (Array.isArray(e)) {
|
||||
return e
|
||||
}
|
||||
return e?.fileList
|
||||
}}>
|
||||
<Upload.Dragger
|
||||
accept={".pdf, .csv, .txt"}
|
||||
multiple={true}
|
||||
maxCount={10}
|
||||
beforeUpload={(file) => {
|
||||
const allowedTypes = [
|
||||
"application/pdf",
|
||||
// "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"text/csv",
|
||||
"text/plain"
|
||||
]
|
||||
.map((type) => type.toLowerCase())
|
||||
.join(", ")
|
||||
|
||||
if (!allowedTypes.includes(file.type.toLowerCase())) {
|
||||
message.error(
|
||||
t("form.uploadFile.uploadError", { allowedTypes })
|
||||
)
|
||||
return Upload.LIST_IGNORE
|
||||
}
|
||||
|
||||
return false
|
||||
}}>
|
||||
<div className="p-3">
|
||||
<p className="ant-upload-drag-icon justify-center flex">
|
||||
<InboxIcon className="h-10 w-10 text-gray-400" />
|
||||
</p>
|
||||
<p className="ant-upload-text">
|
||||
{t("form.uploadFile.uploadText")}
|
||||
</p>
|
||||
<p className="ant-upload-hint">
|
||||
{t("form.uploadFile.uploadHint")}
|
||||
</p>
|
||||
</div>
|
||||
</Upload.Dragger>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSaving}
|
||||
className="inline-flex w-full text-center justify-center items-center rounded-md border border-transparent bg-black px-2 py-2 text-md font-medium leading-4 text-white shadow-sm hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50">
|
||||
{t("form.submit")}
|
||||
</button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
51
src/components/Option/Knowledge/KnowledgeSelect.tsx
Normal file
51
src/components/Option/Knowledge/KnowledgeSelect.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { getAllKnowledge } from "@/db/knowledge"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { Dropdown, Tooltip } from "antd"
|
||||
import { Blocks } from "lucide-react"
|
||||
import React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
export const KnowledgeSelect: React.FC = () => {
|
||||
const { t } = useTranslation("playground")
|
||||
const { data } = useQuery({
|
||||
queryKey: ["getAllKnowledge"],
|
||||
queryFn: async () => {
|
||||
const data = await getAllKnowledge("finished")
|
||||
return data
|
||||
},
|
||||
refetchInterval: 1000
|
||||
})
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
menu={{
|
||||
items:
|
||||
data?.map((d) => ({
|
||||
key: d.id,
|
||||
label: (
|
||||
<div className="w-52 gap-2 text-lg truncate inline-flex line-clamp-3 items-center dark:border-gray-700">
|
||||
<div>
|
||||
<Blocks className="h-6 w-6 text-gray-400" />
|
||||
</div>
|
||||
{d.title}
|
||||
</div>
|
||||
),
|
||||
onClick: () => {}
|
||||
})) || [],
|
||||
style: {
|
||||
maxHeight: 500,
|
||||
overflowY: "scroll"
|
||||
},
|
||||
// hidescrollbars: true
|
||||
className: "no-scrollbar"
|
||||
}}
|
||||
placement={"topLeft"}
|
||||
trigger={["click"]}>
|
||||
<Tooltip title={t("tooltip.knowledge")}>
|
||||
<button type="button" className="dark:text-gray-300">
|
||||
<Blocks className="h-6 w-6" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
138
src/components/Option/Knowledge/index.tsx
Normal file
138
src/components/Option/Knowledge/index.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { AddKnowledge } from "./AddKnowledge"
|
||||
import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient
|
||||
} from "@tanstack/react-query"
|
||||
import { deleteKnowledge, getAllKnowledge } from "@/db/knowledge"
|
||||
import { Skeleton, Table, Tag, Tooltip, message } from "antd"
|
||||
import { Trash2 } from "lucide-react"
|
||||
|
||||
export const KnowledgeSettings = () => {
|
||||
const { t } = useTranslation(["knownledge", "common"])
|
||||
const [open, setOpen] = useState(false)
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { data, status } = useQuery({
|
||||
queryKey: ["fetchAllKnowledge"],
|
||||
queryFn: () => getAllKnowledge(),
|
||||
refetchInterval: 1000
|
||||
})
|
||||
|
||||
const { mutate: deleteKnowledgeMutation, isPending: isDeleting } =
|
||||
useMutation({
|
||||
mutationFn: deleteKnowledge,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["fetchAllKnowledge"]
|
||||
})
|
||||
|
||||
message.success(t("deleteSuccess"))
|
||||
},
|
||||
onError: (error) => {
|
||||
message.error(error.message)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{/* Add new model button */}
|
||||
<div className="mb-6">
|
||||
<div className="-ml-4 -mt-2 flex flex-wrap items-center justify-end sm:flex-nowrap">
|
||||
<div className="ml-4 mt-2 flex-shrink-0">
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="inline-flex items-center rounded-md border border-transparent bg-black px-2 py-2 text-md font-medium leading-4 text-white shadow-sm hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50">
|
||||
{t("addBtn")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{status === "pending" && <Skeleton paragraph={{ rows: 8 }} />}
|
||||
|
||||
{status === "success" && (
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
title: t("columns.title"),
|
||||
dataIndex: "title",
|
||||
key: "title"
|
||||
},
|
||||
{
|
||||
title: t("columns.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
render: (text: string) => (
|
||||
<Tag color="blue">{t(`status.${text}`)}</Tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t("columns.embeddings"),
|
||||
dataIndex: "embedding_model",
|
||||
key: "embedding_model"
|
||||
},
|
||||
{
|
||||
title: t("columns.createdAt"),
|
||||
dataIndex: "createdAt",
|
||||
key: "createdAt",
|
||||
render: (text: number) => new Date(text).toLocaleString()
|
||||
},
|
||||
{
|
||||
title: t("columns.action"),
|
||||
key: "action",
|
||||
render: (text: string, record: any) => (
|
||||
<div className="flex gap-4">
|
||||
<Tooltip title={t("tooltip.delete")}>
|
||||
<button
|
||||
disabled={isDeleting}
|
||||
onClick={() => {
|
||||
if (window.confirm(t("confirm.delete"))) {
|
||||
deleteKnowledgeMutation(record.id)
|
||||
}
|
||||
}}
|
||||
className="text-red-500 dark:text-red-400">
|
||||
<Trash2 className="w-5 h-5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]}
|
||||
expandable={{
|
||||
expandedRowRender: (record) => (
|
||||
<Table
|
||||
pagination={false}
|
||||
columns={[
|
||||
{
|
||||
title: t("expandedColumns.name"),
|
||||
key: "filename",
|
||||
dataIndex: "filename"
|
||||
},
|
||||
{
|
||||
title: t("expandedColumns.type"),
|
||||
key: "type",
|
||||
dataIndex: "type"
|
||||
}
|
||||
]}
|
||||
dataSource={record.source}
|
||||
locale={{
|
||||
emptyText: t("common:noData")
|
||||
}}
|
||||
/>
|
||||
),
|
||||
defaultExpandAllRows: false
|
||||
}}
|
||||
bordered
|
||||
dataSource={data}
|
||||
rowKey={(record) => `${record.name}-${record.id}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<AddKnowledge open={open} setOpen={setOpen} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import React from "react"
|
||||
import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
|
||||
import { toBase64 } from "~/libs/to-base64"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import { Checkbox, Dropdown, Switch, Tooltip } from "antd"
|
||||
import { Checkbox, Dropdown, Select, Switch, Tooltip } from "antd"
|
||||
import { Image } from "antd"
|
||||
import { useSpeechRecognition } from "~/hooks/useSpeechRecognition"
|
||||
import { useWebUI } from "~/store/webui"
|
||||
@@ -12,6 +12,7 @@ import { defaultEmbeddingModelForRag } from "~/services/ollama"
|
||||
import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
|
||||
import { getVariable } from "~/utils/select-varaible"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect"
|
||||
|
||||
type Props = {
|
||||
dropedFile: File | undefined
|
||||
@@ -249,6 +250,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex !justify-end gap-3">
|
||||
<KnowledgeSelect />
|
||||
<Tooltip title={t("tooltip.speechToText")}>
|
||||
<button
|
||||
type="button"
|
||||
@@ -273,6 +275,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title={t("tooltip.uploadImage")}>
|
||||
<button
|
||||
type="button"
|
||||
@@ -284,7 +287,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
}`}>
|
||||
<ImageIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
{!isSending ? (
|
||||
<Dropdown.Button
|
||||
htmlType="submit"
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
getAllPrompts,
|
||||
savePrompt,
|
||||
updatePrompt
|
||||
} from "~/libs/db"
|
||||
} from "@/db"
|
||||
|
||||
export const PromptBody = () => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useQueryClient } from "@tanstack/react-query"
|
||||
import { useDarkMode } from "~/hooks/useDarkmode"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import { PageAssitDatabase } from "~/libs/db"
|
||||
import { PageAssitDatabase } from "@/db"
|
||||
import { Select } from "antd"
|
||||
import { SUPPORTED_LANGUAGES } from "~/utils/supporetd-languages"
|
||||
import { MoonIcon, SunIcon } from "lucide-react"
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Form, Input, Skeleton, Table, Tooltip, message } from "antd"
|
||||
import { Trash2 } from "lucide-react"
|
||||
import { Trans, useTranslation } from "react-i18next"
|
||||
import { SaveButton } from "~/components/Common/SaveButton"
|
||||
import { deleteWebshare, getAllWebshares, getUserId } from "~/libs/db"
|
||||
import { deleteWebshare, getAllWebshares, getUserId } from "@/db"
|
||||
import { getPageShareUrl, setPageShareUrl } from "~/services/ollama"
|
||||
import { verifyPageShareURL } from "~/utils/verify-page-share"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
formatToMessage,
|
||||
deleteByHistoryId,
|
||||
updateHistory
|
||||
} from "~/libs/db"
|
||||
} from "@/db"
|
||||
import { Empty, Skeleton } from "antd"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import { PencilIcon, Trash2 } from "lucide-react"
|
||||
|
||||
Reference in New Issue
Block a user