mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-08-10 20:52:33 +00:00
代码清理
This commit is contained in:
parent
ad4fd6c0fe
commit
3f733a302b
@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
35
datacenter/.gitignore
vendored
35
datacenter/.gitignore
vendored
@ -1,35 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.prod
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
@ -1,34 +0,0 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
@ -1,13 +0,0 @@
|
||||
import { Box } from '@/lib/mui';
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
"use client"
|
||||
import ChatBoxComp from '@/components/chatBox';
|
||||
import { Chart, LineAdvance, Interval, Tooltip, getTheme } from 'bizcharts';
|
||||
import { Card, CardContent, Typography, Grid, styled, Sheet } from '@/lib/mui';
|
||||
import { Stack } from '@mui/material';
|
||||
import useAgentChat from '@/hooks/useAgentChat';
|
||||
|
||||
|
||||
const Item = styled(Sheet)(({ theme }) => ({
|
||||
...theme.typography.body2,
|
||||
padding: theme.spacing(1),
|
||||
textAlign: 'center',
|
||||
borderRadius: 4,
|
||||
color: theme.vars.palette.text.secondary,
|
||||
}));
|
||||
|
||||
const Agents = () => {
|
||||
const { handleChatSubmit, history } = useAgentChat({
|
||||
queryAgentURL: `/v1/chat/completions`,
|
||||
});
|
||||
|
||||
const data = [
|
||||
{
|
||||
month: "Jan",
|
||||
city: "Tokyo",
|
||||
temperature: 7
|
||||
},
|
||||
{
|
||||
month: "Feb",
|
||||
city: "Tokyo",
|
||||
temperature: 13
|
||||
},
|
||||
{
|
||||
month: "Mar",
|
||||
city: "Tokyo",
|
||||
temperature: 16.5
|
||||
},
|
||||
{
|
||||
month: "Apr",
|
||||
city: "Tokyo",
|
||||
temperature: 14.5
|
||||
},
|
||||
{
|
||||
month: "May",
|
||||
city: "Tokyo",
|
||||
temperature: 10
|
||||
},
|
||||
{
|
||||
month: "Jun",
|
||||
city: "Tokyo",
|
||||
temperature: 7.5
|
||||
},
|
||||
{
|
||||
month: "Jul",
|
||||
city: "Tokyo",
|
||||
temperature: 9.2
|
||||
},
|
||||
{
|
||||
month: "Aug",
|
||||
city: "Tokyo",
|
||||
temperature: 14.5
|
||||
},
|
||||
{
|
||||
month: "Sep",
|
||||
city: "Tokyo",
|
||||
temperature: 9.3
|
||||
},
|
||||
{
|
||||
month: "Oct",
|
||||
city: "Tokyo",
|
||||
temperature: 8.3
|
||||
},
|
||||
{
|
||||
month: "Nov",
|
||||
city: "Tokyo",
|
||||
temperature: 8.9
|
||||
},
|
||||
{
|
||||
month: "Dec",
|
||||
city: "Tokyo",
|
||||
temperature: 5.6
|
||||
},
|
||||
];
|
||||
|
||||
const d1 = [
|
||||
{ year: '1951 年', sales: 0 },
|
||||
{ year: '1952 年', sales: 52 },
|
||||
{ year: '1956 年', sales: 61 },
|
||||
{ year: '1957 年', sales: 45 },
|
||||
{ year: '1958 年', sales: 48 },
|
||||
{ year: '1959 年', sales: 38 },
|
||||
{ year: '1960 年', sales: 38 },
|
||||
{ year: '1962 年', sales: 38 },
|
||||
];
|
||||
|
||||
const topCard = [{
|
||||
label: 'Revenue Won',
|
||||
value: '$7,811,851'
|
||||
}, {
|
||||
label: 'Close %',
|
||||
value: '37.7%'
|
||||
}, {
|
||||
label: 'AVG Days to Close',
|
||||
value: '121'
|
||||
}, {
|
||||
label: 'Opportunities Won',
|
||||
value: '526'
|
||||
}];
|
||||
|
||||
return (
|
||||
<div className='p-4 flex flex-row gap-6 min-h-full w-full'>
|
||||
<div className='flex w-full'>
|
||||
<Grid container spacing={2} sx={{ flexGrow: 1 }}>
|
||||
<Grid xs={8}>
|
||||
<Stack spacing={2} className='h-full'>
|
||||
<Item>
|
||||
<Grid container spacing={2}>
|
||||
{topCard.map((item) => (
|
||||
<Grid key={item.label} xs={3}>
|
||||
<Card className="flex-1 h-full">
|
||||
<CardContent className="justify-around">
|
||||
<Typography gutterBottom component="div">
|
||||
{item.label}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{item.value}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Item>
|
||||
<Item className='flex-1'>
|
||||
<Card className='h-full'>
|
||||
<CardContent className='h-full'>
|
||||
<Typography gutterBottom component="div">
|
||||
Revenue Won by Month
|
||||
</Typography>
|
||||
<div className='flex-1'>
|
||||
<Chart padding={[10, 20, 50, 40]} autoFit data={data} >
|
||||
<LineAdvance
|
||||
shape="smooth"
|
||||
point
|
||||
area
|
||||
position="month*temperature"
|
||||
color="city"
|
||||
/>
|
||||
</Chart>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Item>
|
||||
<Item className='flex-1'>
|
||||
<Grid container spacing={2} className='h-full'>
|
||||
<Grid xs={4} className='h-full'>
|
||||
<Card className='flex-1 h-full'>
|
||||
<CardContent className='h-full'>
|
||||
<Typography gutterBottom component="div">
|
||||
Close % by Month
|
||||
</Typography>
|
||||
<div className='flex-1'>
|
||||
<Chart autoFit data={d1} >
|
||||
<Interval position="year*sales" style={{ lineWidth: 3, stroke: getTheme().colors10[0] }} />
|
||||
<Tooltip shared />
|
||||
</Chart>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid xs={4} className='h-full'>
|
||||
<Card className='flex-1 h-full'>
|
||||
<CardContent className='h-full'>
|
||||
<Typography gutterBottom component="div">
|
||||
Close % by Month
|
||||
</Typography>
|
||||
<div className='flex-1'>
|
||||
<Chart autoFit data={d1} >
|
||||
<Interval position="year*sales" style={{ lineWidth: 3, stroke: getTheme().colors10[0] }} />
|
||||
<Tooltip shared />
|
||||
</Chart>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid xs={4} className='h-full'>
|
||||
<Card className='flex-1 h-full'>
|
||||
<CardContent className='h-full'>
|
||||
<Typography gutterBottom component="div">
|
||||
Close % by Month
|
||||
</Typography>
|
||||
<div className='flex-1'>
|
||||
<Chart autoFit data={d1} >
|
||||
<Interval position="year*sales" style={{ lineWidth: 3, stroke: getTheme().colors10[0] }} />
|
||||
<Tooltip shared />
|
||||
</Chart>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Item>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid xs={4}>
|
||||
<ChatBoxComp messages={history} onSubmit={handleChatSubmit}/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Agents;
|
@ -1,50 +0,0 @@
|
||||
"use client"
|
||||
import { useRequest } from 'ahooks';
|
||||
import { sendGetRequest, sendPostRequest } from '@/utils/request';
|
||||
import useAgentChat from '@/hooks/useAgentChat';
|
||||
import ChatBoxComp from '@/components/chatBoxTemp';
|
||||
import { useDialogueContext } from '@/app/context/dialogue';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
const AgentPage = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const { refreshDialogList } = useDialogueContext();
|
||||
const id = searchParams.get('id');
|
||||
const scene = searchParams.get('scene');
|
||||
|
||||
const { data: historyList } = useRequest(async () => await sendGetRequest('/v1/chat/dialogue/messages/history', {
|
||||
con_uid: id
|
||||
}), {
|
||||
ready: !!id,
|
||||
refreshDeps: [id]
|
||||
});
|
||||
|
||||
const { data: paramsList } = useRequest(async () => await sendPostRequest(`/v1/chat/mode/params/list?chat_mode=${scene}`), {
|
||||
ready: !!scene,
|
||||
refreshDeps: [id, scene]
|
||||
});
|
||||
|
||||
const { history, handleChatSubmit } = useAgentChat({
|
||||
queryAgentURL: `/v1/chat/completions`,
|
||||
queryBody: {
|
||||
conv_uid: id,
|
||||
chat_mode: scene || 'chat_normal',
|
||||
},
|
||||
initHistory: historyList?.data
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChatBoxComp
|
||||
clearIntialMessage={async () => {
|
||||
await refreshDialogList();
|
||||
}}
|
||||
messages={history || []}
|
||||
onSubmit={handleChatSubmit}
|
||||
paramsList={paramsList?.data}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AgentPage;
|
@ -1,37 +0,0 @@
|
||||
import { useRequest } from 'ahooks';
|
||||
import { sendGetRequest } from '@/utils/request';
|
||||
import { createCtx } from '@/utils/ctx-helper';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import React from 'react';
|
||||
|
||||
export const [useDialogueContext, DialogueProvider] = createCtx<{
|
||||
dialogueList?: void | AxiosResponse<any, any> | undefined;
|
||||
queryDialogueList: () => void;
|
||||
refreshDialogList: () => void;
|
||||
}>();
|
||||
|
||||
const DialogueContext = ({ children }: {
|
||||
children: React.ReactElement
|
||||
}) => {
|
||||
const {
|
||||
run: queryDialogueList,
|
||||
data: dialogueList,
|
||||
refresh: refreshDialogList,
|
||||
} = useRequest(async () => await sendGetRequest('/v1/chat/dialogue/list'), {
|
||||
manual: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<DialogueProvider
|
||||
value={{
|
||||
dialogueList,
|
||||
queryDialogueList,
|
||||
refreshDialogList
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</DialogueProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default DialogueContext;
|
@ -1,173 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useSearchParams, useRouter } from 'next/navigation'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import {
|
||||
useColorScheme,
|
||||
Table,
|
||||
Stack,
|
||||
Typography,
|
||||
Breadcrumbs,
|
||||
Link
|
||||
} from '@/lib/mui'
|
||||
import { Popover, Pagination } from 'antd'
|
||||
import { sendSpacePostRequest } from '@/utils/request'
|
||||
const page_size = 20
|
||||
|
||||
const ChunkList = () => {
|
||||
const router = useRouter()
|
||||
const { mode } = useColorScheme()
|
||||
const spaceName = useSearchParams().get('spacename')
|
||||
const documentId = useSearchParams().get('documentid')
|
||||
const [total, setTotal] = useState<number>(0)
|
||||
const [current, setCurrent] = useState<number>(0)
|
||||
const [chunkList, setChunkList] = useState<any>([])
|
||||
useEffect(() => {
|
||||
async function fetchChunks() {
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/chunk/list`,
|
||||
{
|
||||
document_id: documentId,
|
||||
page: 1,
|
||||
page_size
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
setChunkList(data.data.data)
|
||||
setTotal(data.data.total)
|
||||
setCurrent(data.data.page)
|
||||
}
|
||||
}
|
||||
fetchChunks()
|
||||
}, [])
|
||||
return (
|
||||
<div className="p-4">
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
sx={{ marginBottom: '20px' }}
|
||||
>
|
||||
<Breadcrumbs aria-label="breadcrumbs">
|
||||
<Link
|
||||
onClick={() => {
|
||||
router.push('/datastores')
|
||||
}}
|
||||
key="Knowledge Space"
|
||||
underline="hover"
|
||||
color="neutral"
|
||||
fontSize="inherit"
|
||||
>
|
||||
Knowledge Space
|
||||
</Link>
|
||||
<Link
|
||||
onClick={() => {
|
||||
router.push(`/datastores/documents?name=${spaceName}`)
|
||||
}}
|
||||
key="Knowledge Space"
|
||||
underline="hover"
|
||||
color="neutral"
|
||||
fontSize="inherit"
|
||||
>
|
||||
Documents
|
||||
</Link>
|
||||
<Typography fontSize="inherit">Chunks</Typography>
|
||||
</Breadcrumbs>
|
||||
</Stack>
|
||||
<div className="p-4">
|
||||
{chunkList.length ? (
|
||||
<>
|
||||
<Table
|
||||
color="primary"
|
||||
variant="plain"
|
||||
size="lg"
|
||||
sx={{
|
||||
'& tbody tr: hover': {
|
||||
backgroundColor:
|
||||
mode === 'light' ? 'rgb(246, 246, 246)' : 'rgb(33, 33, 40)'
|
||||
},
|
||||
'& tbody tr: hover a': {
|
||||
textDecoration: 'underline'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Content</th>
|
||||
<th>Meta Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{chunkList.map((row: any) => (
|
||||
<tr key={row.id}>
|
||||
<td>{row.doc_name}</td>
|
||||
<td>
|
||||
{
|
||||
<Popover content={row.content} trigger="hover">
|
||||
{row.content.length > 10
|
||||
? `${row.content.slice(0, 10)}...`
|
||||
: row.content}
|
||||
</Popover>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
{
|
||||
<Popover
|
||||
content={JSON.stringify(
|
||||
row.meta_info || '{}',
|
||||
null,
|
||||
2
|
||||
)}
|
||||
trigger="hover"
|
||||
>
|
||||
{row.meta_info.length > 10
|
||||
? `${row.meta_info.slice(0, 10)}...`
|
||||
: row.meta_info}
|
||||
</Popover>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
sx={{
|
||||
marginTop: '20px'
|
||||
}}
|
||||
>
|
||||
<Pagination
|
||||
defaultPageSize={20}
|
||||
showSizeChanger={false}
|
||||
current={current}
|
||||
total={total}
|
||||
onChange={async (page) => {
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/chunk/list`,
|
||||
{
|
||||
document_id: documentId,
|
||||
page,
|
||||
page_size
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
setChunkList(data.data.data)
|
||||
setTotal(data.data.total)
|
||||
setCurrent(data.data.page)
|
||||
}
|
||||
}}
|
||||
hideOnSinglePage
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChunkList
|
@ -1,602 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import {
|
||||
useColorScheme,
|
||||
Button,
|
||||
Table,
|
||||
Sheet,
|
||||
Modal,
|
||||
Box,
|
||||
Stack,
|
||||
Input,
|
||||
Textarea,
|
||||
Chip,
|
||||
Switch,
|
||||
Typography,
|
||||
Breadcrumbs,
|
||||
Link,
|
||||
styled
|
||||
} from '@/lib/mui'
|
||||
import moment from 'moment'
|
||||
import { InboxOutlined } from '@ant-design/icons'
|
||||
import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined'
|
||||
import CachedIcon from '@mui/icons-material/Cached'
|
||||
import type { UploadProps } from 'antd'
|
||||
import { Upload, Pagination, Popover, message } from 'antd'
|
||||
import {
|
||||
sendSpacePostRequest,
|
||||
sendSpaceUploadPostRequest
|
||||
} from '@/utils/request'
|
||||
|
||||
const { Dragger } = Upload
|
||||
const Item = styled(Sheet)(({ theme }) => ({
|
||||
width: '50%',
|
||||
backgroundColor:
|
||||
theme.palette.mode === 'dark' ? theme.palette.background.level1 : '#fff',
|
||||
...theme.typography.body2,
|
||||
padding: theme.spacing(1),
|
||||
textAlign: 'center',
|
||||
borderRadius: 4,
|
||||
color: theme.vars.palette.text.secondary
|
||||
}))
|
||||
const stepsOfAddingDocument = [
|
||||
'Choose a Datasource type',
|
||||
'Setup the Datasource'
|
||||
]
|
||||
const documentTypeList = [
|
||||
{
|
||||
type: 'text',
|
||||
title: 'Text',
|
||||
subTitle: 'Fill your raw text'
|
||||
},
|
||||
{
|
||||
type: 'webPage',
|
||||
title: 'URL',
|
||||
subTitle: 'Fetch the content of a URL'
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
title: 'Document',
|
||||
subTitle:
|
||||
'Upload a document, document type can be PDF, CSV, Text, PowerPoint, Word, Markdown'
|
||||
}
|
||||
]
|
||||
const page_size = 20
|
||||
|
||||
const Documents = () => {
|
||||
const router = useRouter()
|
||||
const spaceName = useSearchParams().get('name')
|
||||
const { mode } = useColorScheme()
|
||||
const [isAddDocumentModalShow, setIsAddDocumentModalShow] =
|
||||
useState<boolean>(false)
|
||||
const [activeStep, setActiveStep] = useState<number>(0)
|
||||
const [documentType, setDocumentType] = useState<string>('')
|
||||
const [documents, setDocuments] = useState<any>([])
|
||||
const [webPageUrl, setWebPageUrl] = useState<string>('')
|
||||
const [documentName, setDocumentName] = useState<any>('')
|
||||
const [textSource, setTextSource] = useState<string>('')
|
||||
const [text, setText] = useState<string>('')
|
||||
const [originFileObj, setOriginFileObj] = useState<any>(null)
|
||||
const [total, setTotal] = useState<number>(0)
|
||||
const [current, setCurrent] = useState<number>(0)
|
||||
const [synchChecked, setSynchChecked] = useState<boolean>(true)
|
||||
const props: UploadProps = {
|
||||
name: 'file',
|
||||
multiple: false,
|
||||
onChange(info) {
|
||||
console.log(info)
|
||||
if (info.fileList.length === 0) {
|
||||
setOriginFileObj(null)
|
||||
setDocumentName('')
|
||||
return
|
||||
}
|
||||
setOriginFileObj(info.file.originFileObj)
|
||||
setDocumentName(info.file.originFileObj?.name)
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
async function fetchDocuments() {
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/list`,
|
||||
{
|
||||
page: 1,
|
||||
page_size
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
setDocuments(data.data.data)
|
||||
setTotal(data.data.total)
|
||||
setCurrent(data.data.page)
|
||||
}
|
||||
}
|
||||
fetchDocuments()
|
||||
}, [])
|
||||
return (
|
||||
<div className="p-4">
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
sx={{ marginBottom: '20px' }}
|
||||
>
|
||||
<Breadcrumbs aria-label="breadcrumbs">
|
||||
<Link
|
||||
onClick={() => {
|
||||
router.push('/datastores')
|
||||
}}
|
||||
key="Knowledge Space"
|
||||
underline="hover"
|
||||
color="neutral"
|
||||
fontSize="inherit"
|
||||
>
|
||||
Knowledge Space
|
||||
</Link>
|
||||
<Typography fontSize="inherit">Documents</Typography>
|
||||
</Breadcrumbs>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => setIsAddDocumentModalShow(true)}
|
||||
>
|
||||
+ Add Datasource
|
||||
</Button>
|
||||
</Stack>
|
||||
{documents.length ? (
|
||||
<>
|
||||
<Table
|
||||
color="primary"
|
||||
variant="plain"
|
||||
size="lg"
|
||||
sx={{
|
||||
'& tbody tr: hover': {
|
||||
backgroundColor:
|
||||
mode === 'light' ? 'rgb(246, 246, 246)' : 'rgb(33, 33, 40)'
|
||||
},
|
||||
'& tbody tr: hover a': {
|
||||
textDecoration: 'underline'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Size</th>
|
||||
<th>Last Synch</th>
|
||||
<th>Status</th>
|
||||
<th>Result</th>
|
||||
<th>Operation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{documents.map((row: any) => (
|
||||
<tr key={row.id}>
|
||||
<td>{row.doc_name}</td>
|
||||
<td>
|
||||
<Chip variant="solid" color="neutral" sx={{ opacity: 0.5 }}>
|
||||
{row.doc_type}
|
||||
</Chip>
|
||||
</td>
|
||||
<td>{row.chunk_size} chunks</td>
|
||||
<td>{moment(row.last_sync).format('YYYY-MM-DD HH:MM:SS')}</td>
|
||||
<td>
|
||||
<Chip
|
||||
sx={{ opacity: 0.5 }}
|
||||
variant="solid"
|
||||
color={(function () {
|
||||
switch (row.status) {
|
||||
case 'TODO':
|
||||
return 'neutral'
|
||||
case 'RUNNING':
|
||||
return 'primary'
|
||||
case 'FINISHED':
|
||||
return 'success'
|
||||
case 'FAILED':
|
||||
return 'danger'
|
||||
}
|
||||
})()}
|
||||
>
|
||||
{row.status}
|
||||
</Chip>
|
||||
</td>
|
||||
<td>
|
||||
{(function () {
|
||||
if (row.status === 'TODO' || row.status === 'RUNNING') {
|
||||
return ''
|
||||
} else if (row.status === 'FINISHED') {
|
||||
return (
|
||||
<Popover content={row.result} trigger="hover">
|
||||
<Chip
|
||||
variant="solid"
|
||||
color="success"
|
||||
sx={{ opacity: 0.5 }}
|
||||
>
|
||||
SUCCESS
|
||||
</Chip>
|
||||
</Popover>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Popover content={row.result} trigger="hover">
|
||||
<Chip
|
||||
variant="solid"
|
||||
color="danger"
|
||||
sx={{ opacity: 0.5 }}
|
||||
>
|
||||
FAILED
|
||||
</Chip>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
})()}
|
||||
</td>
|
||||
<td>
|
||||
{
|
||||
<>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="sm"
|
||||
sx={{
|
||||
marginRight: '20px'
|
||||
}}
|
||||
onClick={async () => {
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/sync`,
|
||||
{
|
||||
doc_ids: [row.id]
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
}}
|
||||
>
|
||||
Synch
|
||||
<CachedIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
router.push(
|
||||
`/datastores/documents/chunklist?spacename=${spaceName}&documentid=${row.id}`
|
||||
)
|
||||
}}
|
||||
>
|
||||
Details
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
sx={{
|
||||
marginTop: '20px'
|
||||
}}
|
||||
>
|
||||
<Pagination
|
||||
defaultPageSize={20}
|
||||
showSizeChanger={false}
|
||||
current={current}
|
||||
total={total}
|
||||
onChange={async (page) => {
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/list`,
|
||||
{
|
||||
page,
|
||||
page_size
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
setDocuments(data.data.data)
|
||||
setTotal(data.data.total)
|
||||
setCurrent(data.data.page)
|
||||
}
|
||||
}}
|
||||
hideOnSinglePage
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Modal
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
'z-index': 1000
|
||||
}}
|
||||
open={isAddDocumentModalShow}
|
||||
onClose={() => setIsAddDocumentModalShow(false)}
|
||||
>
|
||||
<Sheet
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: 800,
|
||||
borderRadius: 'md',
|
||||
p: 3,
|
||||
boxShadow: 'lg'
|
||||
}}
|
||||
>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Stack spacing={2} direction="row">
|
||||
{stepsOfAddingDocument.map((item: any, index: number) => (
|
||||
<Item
|
||||
key={item}
|
||||
sx={{
|
||||
fontWeight: activeStep === index ? 'bold' : '',
|
||||
color: activeStep === index ? '#2AA3FF' : ''
|
||||
}}
|
||||
>
|
||||
{index < activeStep ? (
|
||||
<CheckCircleOutlinedIcon />
|
||||
) : (
|
||||
`${index + 1}.`
|
||||
)}
|
||||
{`${item}`}
|
||||
</Item>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
{activeStep === 0 ? (
|
||||
<>
|
||||
<Box sx={{ margin: '30px auto' }}>
|
||||
{documentTypeList.map((item: any) => (
|
||||
<Sheet
|
||||
key={item.type}
|
||||
sx={{
|
||||
boxSizing: 'border-box',
|
||||
height: '80px',
|
||||
padding: '12px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
border: '1px solid gray',
|
||||
borderRadius: '6px',
|
||||
marginBottom: '20px',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={() => {
|
||||
setDocumentType(item.type)
|
||||
setActiveStep(1)
|
||||
}}
|
||||
>
|
||||
<Sheet sx={{ fontSize: '20px', fontWeight: 'bold' }}>
|
||||
{item.title}
|
||||
</Sheet>
|
||||
<Sheet>{item.subTitle}</Sheet>
|
||||
</Sheet>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Box sx={{ margin: '30px auto' }}>
|
||||
Name:
|
||||
<Input
|
||||
placeholder="Please input the name"
|
||||
onChange={(e: any) => setDocumentName(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
{documentType === 'webPage' ? (
|
||||
<>
|
||||
Web Page URL:
|
||||
<Input
|
||||
placeholder="Please input the Web Page URL"
|
||||
onChange={(e: any) => setWebPageUrl(e.target.value)}
|
||||
/>
|
||||
</>
|
||||
) : documentType === 'file' ? (
|
||||
<>
|
||||
<Dragger {...props}>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<InboxOutlined />
|
||||
</p>
|
||||
<p
|
||||
style={{ color: 'rgb(22, 108, 255)', fontSize: '20px' }}
|
||||
>
|
||||
Select or Drop file
|
||||
</p>
|
||||
<p
|
||||
className="ant-upload-hint"
|
||||
style={{ color: 'rgb(22, 108, 255)' }}
|
||||
>
|
||||
PDF, PowerPoint, Excel, Word, Text, Markdown,
|
||||
</p>
|
||||
</Dragger>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Text Source(Optional):
|
||||
<Input
|
||||
placeholder="Please input the text source"
|
||||
onChange={(e: any) => setTextSource(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
Text:
|
||||
<Textarea
|
||||
onChange={(e: any) => setText(e.target.value)}
|
||||
minRows={4}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Typography
|
||||
component="label"
|
||||
sx={{
|
||||
marginTop: '20px'
|
||||
}}
|
||||
endDecorator={
|
||||
<Switch
|
||||
checked={synchChecked}
|
||||
onChange={(event: any) =>
|
||||
setSynchChecked(event.target.checked)
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Synch:
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
sx={{ marginBottom: '20px' }}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
sx={{ marginRight: '20px' }}
|
||||
onClick={() => setActiveStep(0)}
|
||||
>
|
||||
{'< Back'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={async () => {
|
||||
if (documentName === '') {
|
||||
message.error('Please input the name')
|
||||
return
|
||||
}
|
||||
if (documentType === 'webPage') {
|
||||
if (webPageUrl === '') {
|
||||
message.error('Please input the Web Page URL')
|
||||
return
|
||||
}
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/add`,
|
||||
{
|
||||
doc_name: documentName,
|
||||
content: webPageUrl,
|
||||
doc_type: 'URL'
|
||||
}
|
||||
)
|
||||
data.success &&
|
||||
synchChecked &&
|
||||
sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/sync`,
|
||||
{
|
||||
doc_ids: [data.data]
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddDocumentModalShow(false)
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/list`,
|
||||
{
|
||||
page: current,
|
||||
page_size
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
setDocuments(data.data.data)
|
||||
setTotal(data.data.total)
|
||||
setCurrent(data.data.page)
|
||||
}
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
} else if (documentType === 'file') {
|
||||
if (!originFileObj) {
|
||||
message.error('Please select a file')
|
||||
return
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('doc_name', documentName)
|
||||
formData.append('doc_file', originFileObj)
|
||||
formData.append('doc_type', 'DOCUMENT')
|
||||
const data = await sendSpaceUploadPostRequest(
|
||||
`/knowledge/${spaceName}/document/upload`,
|
||||
formData
|
||||
)
|
||||
data.success &&
|
||||
synchChecked &&
|
||||
sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/sync`,
|
||||
{
|
||||
doc_ids: [data.data]
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddDocumentModalShow(false)
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/list`,
|
||||
{
|
||||
page: current,
|
||||
page_size
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
setDocuments(data.data.data)
|
||||
setTotal(data.data.total)
|
||||
setCurrent(data.data.page)
|
||||
}
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
} else {
|
||||
if (text === '') {
|
||||
message.error('Please input the text')
|
||||
return
|
||||
}
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/add`,
|
||||
{
|
||||
doc_name: documentName,
|
||||
source: textSource,
|
||||
content: text,
|
||||
doc_type: 'TEXT'
|
||||
}
|
||||
)
|
||||
data.success &&
|
||||
synchChecked &&
|
||||
sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/sync`,
|
||||
{
|
||||
doc_ids: [data.data]
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddDocumentModalShow(false)
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${spaceName}/document/list`,
|
||||
{
|
||||
page: current,
|
||||
page_size
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
setDocuments(data.data.data)
|
||||
setTotal(data.data.total)
|
||||
setCurrent(data.data.page)
|
||||
}
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</Sheet>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Documents
|
@ -1,601 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { InboxOutlined } from '@ant-design/icons'
|
||||
import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined'
|
||||
import ContentPasteSearchOutlinedIcon from '@mui/icons-material/ContentPasteSearchOutlined'
|
||||
import type { UploadProps } from 'antd'
|
||||
import { message, Upload } from 'antd'
|
||||
import {
|
||||
useColorScheme,
|
||||
Modal,
|
||||
Button,
|
||||
Sheet,
|
||||
Stack,
|
||||
Box,
|
||||
Input,
|
||||
Textarea,
|
||||
Switch,
|
||||
Typography,
|
||||
styled
|
||||
} from '@/lib/mui'
|
||||
import {
|
||||
sendSpacePostRequest,
|
||||
sendSpaceUploadPostRequest
|
||||
} from '@/utils/request'
|
||||
|
||||
const { Dragger } = Upload
|
||||
|
||||
const Item = styled(Sheet)(({ theme }) => ({
|
||||
width: '33%',
|
||||
backgroundColor:
|
||||
theme.palette.mode === 'dark' ? theme.palette.background.level1 : '#fff',
|
||||
...theme.typography.body2,
|
||||
padding: theme.spacing(1),
|
||||
textAlign: 'center',
|
||||
borderRadius: 4,
|
||||
color: theme.vars.palette.text.secondary
|
||||
}))
|
||||
|
||||
const stepsOfAddingSpace = [
|
||||
'Knowledge Space Config',
|
||||
'Choose a Datasource type',
|
||||
'Setup the Datasource'
|
||||
]
|
||||
const documentTypeList = [
|
||||
{
|
||||
type: 'text',
|
||||
title: 'Text',
|
||||
subTitle: 'Fill your raw text'
|
||||
},
|
||||
{
|
||||
type: 'webPage',
|
||||
title: 'URL',
|
||||
subTitle: 'Fetch the content of a URL'
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
title: 'Document',
|
||||
subTitle:
|
||||
'Upload a document, document type can be PDF, CSV, Text, PowerPoint, Word, Markdown'
|
||||
}
|
||||
]
|
||||
|
||||
const Index = () => {
|
||||
const router = useRouter()
|
||||
const { mode } = useColorScheme()
|
||||
const [activeStep, setActiveStep] = useState<number>(0)
|
||||
const [documentType, setDocumentType] = useState<string>('')
|
||||
const [knowledgeSpaceList, setKnowledgeSpaceList] = useState<any>([])
|
||||
const [isAddKnowledgeSpaceModalShow, setIsAddKnowledgeSpaceModalShow] =
|
||||
useState<boolean>(false)
|
||||
const [knowledgeSpaceName, setKnowledgeSpaceName] = useState<string>('')
|
||||
const [owner, setOwner] = useState<string>('')
|
||||
const [description, setDescription] = useState<string>('')
|
||||
const [webPageUrl, setWebPageUrl] = useState<string>('')
|
||||
const [documentName, setDocumentName] = useState<any>('')
|
||||
const [textSource, setTextSource] = useState<string>('')
|
||||
const [text, setText] = useState<string>('')
|
||||
const [originFileObj, setOriginFileObj] = useState<any>(null)
|
||||
const [synchChecked, setSynchChecked] = useState<boolean>(true)
|
||||
const props: UploadProps = {
|
||||
name: 'file',
|
||||
multiple: false,
|
||||
onChange(info) {
|
||||
console.log(info)
|
||||
if (info.fileList.length === 0) {
|
||||
setOriginFileObj(null)
|
||||
setDocumentName('')
|
||||
return
|
||||
}
|
||||
setOriginFileObj(info.file.originFileObj)
|
||||
setDocumentName(info.file.originFileObj?.name)
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
const data = await sendSpacePostRequest('/knowledge/space/list', {})
|
||||
if (data.success) {
|
||||
setKnowledgeSpaceList(data.data)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}}
|
||||
className="bg-[#F1F2F5] dark:bg-[#212121]"
|
||||
>
|
||||
<Box
|
||||
className="page-body p-4"
|
||||
sx={{
|
||||
'&': {
|
||||
height: '90%',
|
||||
overflow: 'auto'
|
||||
},
|
||||
'&::-webkit-scrollbar': {
|
||||
display: 'none'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
flexWrap="wrap"
|
||||
sx={{
|
||||
'& i': {
|
||||
width: '430px',
|
||||
marginRight: '30px'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignContent: 'start',
|
||||
boxSizing: 'content-box',
|
||||
width: '390px',
|
||||
height: '79px',
|
||||
padding: '33px 20px 40px',
|
||||
marginRight: '30px',
|
||||
marginBottom: '30px',
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold',
|
||||
color: 'black',
|
||||
flexShrink: 0,
|
||||
flexGrow: 0,
|
||||
cursor: 'pointer',
|
||||
borderRadius: '16px',
|
||||
'&: hover': {
|
||||
boxShadow:
|
||||
'0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);'
|
||||
}
|
||||
}}
|
||||
onClick={() => setIsAddKnowledgeSpaceModalShow(true)}
|
||||
className="bg-[#E9EBEE] dark:bg-[#484848]"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
lineHeight: '28px',
|
||||
border: '1px solid #2AA3FF',
|
||||
textAlign: 'center',
|
||||
borderRadius: '5px',
|
||||
marginRight: '5px',
|
||||
fontWeight: '300',
|
||||
color: '#2AA3FF'
|
||||
}}
|
||||
>
|
||||
+
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: '16px'
|
||||
}}
|
||||
>
|
||||
space
|
||||
</Box>
|
||||
</Box>
|
||||
{knowledgeSpaceList.map((item: any, index: number) => (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
padding: '30px 20px 40px',
|
||||
marginRight: '30px',
|
||||
marginBottom: '30px',
|
||||
borderTop: '4px solid rgb(84, 164, 248)',
|
||||
flexShrink: 0,
|
||||
flexGrow: 0,
|
||||
cursor: 'pointer',
|
||||
borderRadius: '10px',
|
||||
'&: hover': {
|
||||
boxShadow:
|
||||
'0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);'
|
||||
}
|
||||
}}
|
||||
onClick={() => {
|
||||
router.push(`/datastores/documents?name=${item.name}`)
|
||||
}}
|
||||
className="bg-[#FFFFFF] dark:bg-[#484848]"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: '18px',
|
||||
marginBottom: '10px',
|
||||
fontWeight: 'bold',
|
||||
color: 'black'
|
||||
}}
|
||||
>
|
||||
<ContentPasteSearchOutlinedIcon
|
||||
sx={{ marginRight: '5px', color: '#2AA3FF' }}
|
||||
/>
|
||||
{item.name}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '130px',
|
||||
flexGrow: 0,
|
||||
flexShrink: 0
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
color: '#2AA3FF'
|
||||
}}
|
||||
>
|
||||
{item.vector_type}
|
||||
</Box>
|
||||
<Box sx={{ fontSize: '12px', color: 'black' }}>Vector</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: '130px',
|
||||
flexGrow: 0,
|
||||
flexShrink: 0
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
color: '#2AA3FF'
|
||||
}}
|
||||
>
|
||||
{item.owner}
|
||||
</Box>
|
||||
<Box sx={{ fontSize: '12px', color: 'black' }}>Owner</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: '130px',
|
||||
flexGrow: 0,
|
||||
flexShrink: 0
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
color: '#2AA3FF'
|
||||
}}
|
||||
>
|
||||
{item.docs || 0}
|
||||
</Box>
|
||||
<Box sx={{ fontSize: '12px', color: 'black' }}>Docs</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
<i></i>
|
||||
<i></i>
|
||||
<i></i>
|
||||
<i></i>
|
||||
<i></i>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Modal
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
'z-index': 1000
|
||||
}}
|
||||
open={isAddKnowledgeSpaceModalShow}
|
||||
onClose={() => setIsAddKnowledgeSpaceModalShow(false)}
|
||||
>
|
||||
<Sheet
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: 800,
|
||||
borderRadius: 'md',
|
||||
p: 3,
|
||||
boxShadow: 'lg'
|
||||
}}
|
||||
>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Stack spacing={2} direction="row">
|
||||
{stepsOfAddingSpace.map((item: any, index: number) => (
|
||||
<Item
|
||||
key={item}
|
||||
sx={{
|
||||
fontWeight: activeStep === index ? 'bold' : '',
|
||||
color: activeStep === index ? '#2AA3FF' : ''
|
||||
}}
|
||||
>
|
||||
{index < activeStep ? (
|
||||
<CheckCircleOutlinedIcon />
|
||||
) : (
|
||||
`${index + 1}.`
|
||||
)}
|
||||
{`${item}`}
|
||||
</Item>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
{activeStep === 0 ? (
|
||||
<>
|
||||
<Box sx={{ margin: '30px auto' }}>
|
||||
Knowledge Space Name:
|
||||
<Input
|
||||
placeholder="Please input the name"
|
||||
onChange={(e: any) => setKnowledgeSpaceName(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
Owner:
|
||||
<Input
|
||||
placeholder="Please input the owner"
|
||||
onChange={(e: any) => setOwner(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
Description:
|
||||
<Input
|
||||
placeholder="Please input the description"
|
||||
onChange={(e: any) => setDescription(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={async () => {
|
||||
if (knowledgeSpaceName === '') {
|
||||
message.error('please input the name')
|
||||
return
|
||||
}
|
||||
if (owner === '') {
|
||||
message.error('please input the owner')
|
||||
return
|
||||
}
|
||||
if (description === '') {
|
||||
message.error('please input the description')
|
||||
return
|
||||
}
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/space/add`,
|
||||
{
|
||||
name: knowledgeSpaceName,
|
||||
vector_type: 'Chroma',
|
||||
owner,
|
||||
desc: description
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setActiveStep(1)
|
||||
const data = await sendSpacePostRequest(
|
||||
'/knowledge/space/list',
|
||||
{}
|
||||
)
|
||||
if (data.success) {
|
||||
setKnowledgeSpaceList(data.data)
|
||||
}
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</>
|
||||
) : activeStep === 1 ? (
|
||||
<>
|
||||
<Box sx={{ margin: '30px auto' }}>
|
||||
{documentTypeList.map((item: any) => (
|
||||
<Sheet
|
||||
key={item.type}
|
||||
sx={{
|
||||
boxSizing: 'border-box',
|
||||
height: '80px',
|
||||
padding: '12px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
border: '1px solid gray',
|
||||
borderRadius: '6px',
|
||||
marginBottom: '20px',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={() => {
|
||||
setDocumentType(item.type)
|
||||
setActiveStep(2)
|
||||
}}
|
||||
>
|
||||
<Sheet sx={{ fontSize: '20px', fontWeight: 'bold' }}>
|
||||
{item.title}
|
||||
</Sheet>
|
||||
<Sheet>{item.subTitle}</Sheet>
|
||||
</Sheet>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Box sx={{ margin: '30px auto' }}>
|
||||
Name:
|
||||
<Input
|
||||
placeholder="Please input the name"
|
||||
onChange={(e: any) => setDocumentName(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
{documentType === 'webPage' ? (
|
||||
<>
|
||||
Web Page URL:
|
||||
<Input
|
||||
placeholder="Please input the Web Page URL"
|
||||
onChange={(e: any) => setWebPageUrl(e.target.value)}
|
||||
/>
|
||||
</>
|
||||
) : documentType === 'file' ? (
|
||||
<>
|
||||
<Dragger {...props}>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<InboxOutlined />
|
||||
</p>
|
||||
<p
|
||||
style={{ color: 'rgb(22, 108, 255)', fontSize: '20px' }}
|
||||
>
|
||||
Select or Drop file
|
||||
</p>
|
||||
<p
|
||||
className="ant-upload-hint"
|
||||
style={{ color: 'rgb(22, 108, 255)' }}
|
||||
>
|
||||
PDF, PowerPoint, Excel, Word, Text, Markdown,
|
||||
</p>
|
||||
</Dragger>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Text Source(Optional):
|
||||
<Input
|
||||
placeholder="Please input the text source"
|
||||
onChange={(e: any) => setTextSource(e.target.value)}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
Text:
|
||||
<Textarea
|
||||
onChange={(e: any) => setText(e.target.value)}
|
||||
minRows={4}
|
||||
sx={{ marginBottom: '20px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Typography
|
||||
component="label"
|
||||
sx={{
|
||||
marginTop: '20px'
|
||||
}}
|
||||
endDecorator={
|
||||
<Switch
|
||||
checked={synchChecked}
|
||||
onChange={(event: any) =>
|
||||
setSynchChecked(event.target.checked)
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Synch:
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-start"
|
||||
alignItems="center"
|
||||
sx={{ marginBottom: '20px' }}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
sx={{ marginRight: '20px' }}
|
||||
onClick={() => setActiveStep(1)}
|
||||
>
|
||||
{'< Back'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={async () => {
|
||||
if (documentName === '') {
|
||||
message.error('Please input the name')
|
||||
return
|
||||
}
|
||||
if (documentType === 'webPage') {
|
||||
if (webPageUrl === '') {
|
||||
message.error('Please input the Web Page URL')
|
||||
return
|
||||
}
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${knowledgeSpaceName}/document/add`,
|
||||
{
|
||||
doc_name: documentName,
|
||||
content: webPageUrl,
|
||||
doc_type: 'URL'
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddKnowledgeSpaceModalShow(false)
|
||||
synchChecked &&
|
||||
sendSpacePostRequest(
|
||||
`/knowledge/${knowledgeSpaceName}/document/sync`,
|
||||
{
|
||||
doc_ids: [data.data]
|
||||
}
|
||||
)
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
} else if (documentType === 'file') {
|
||||
if (!originFileObj) {
|
||||
message.error('Please select a file')
|
||||
return
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('doc_name', documentName)
|
||||
formData.append('doc_file', originFileObj)
|
||||
formData.append('doc_type', 'DOCUMENT')
|
||||
|
||||
const data = await sendSpaceUploadPostRequest(
|
||||
`/knowledge/${knowledgeSpaceName}/document/upload`,
|
||||
formData
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddKnowledgeSpaceModalShow(false)
|
||||
synchChecked &&
|
||||
sendSpacePostRequest(
|
||||
`/knowledge/${knowledgeSpaceName}/document/sync`,
|
||||
{
|
||||
doc_ids: [data.data]
|
||||
}
|
||||
)
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
} else {
|
||||
if (text === '') {
|
||||
message.error('Please input the text')
|
||||
return
|
||||
}
|
||||
const data = await sendSpacePostRequest(
|
||||
`/knowledge/${knowledgeSpaceName}/document/add`,
|
||||
{
|
||||
doc_name: documentName,
|
||||
source: textSource,
|
||||
content: text,
|
||||
doc_type: 'TEXT'
|
||||
}
|
||||
)
|
||||
if (data.success) {
|
||||
message.success('success')
|
||||
setIsAddKnowledgeSpaceModalShow(false)
|
||||
synchChecked &&
|
||||
sendSpacePostRequest(
|
||||
`/knowledge/${knowledgeSpaceName}/document/sync`,
|
||||
{
|
||||
doc_ids: [data.data]
|
||||
}
|
||||
)
|
||||
} else {
|
||||
message.error(data.err_msg || 'failed')
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</Button>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</Sheet>
|
||||
</Modal>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Index
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
@ -1,34 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: var(--joy-palette-text-primary, var(--joy-palette-neutral-800, #25252D));
|
||||
font-family: var(--joy-fontFamily-body, var(--joy-Josefin Sans, sans-serif));
|
||||
font-size: var(--joy-fontSize-md, 1rem);
|
||||
line-height: var(--joy-lineHeight-md, 1.5);
|
||||
background-color: var(--joy-palette-background-body);
|
||||
}
|
||||
|
||||
body .ant-btn-primary {
|
||||
background-color: #1677ff;
|
||||
}
|
||||
|
||||
.ant-pagination .ant-pagination-prev * {
|
||||
color: rgb(145, 35, 167) !important;
|
||||
}
|
||||
|
||||
.ant-pagination .ant-pagination-next * {
|
||||
color: rgb(145, 35, 167) !important;
|
||||
}
|
||||
|
||||
.ant-pagination .ant-pagination-item {
|
||||
border-color: rgb(145, 35, 167) !important;
|
||||
background-color: rgb(145, 35, 167) !important;
|
||||
}
|
||||
|
||||
.ant-pagination .ant-pagination-item.ant-pagination-item-active a {
|
||||
color: white !important;
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
"use client"
|
||||
import './globals.css'
|
||||
import '@/nprogress.css';
|
||||
import React from 'react';
|
||||
import LeftSider from '@/components/leftSider';
|
||||
import { CssVarsProvider, ThemeProvider } from '@mui/joy/styles';
|
||||
import { useColorScheme } from '@/lib/mui';
|
||||
import { joyTheme } from '@/defaultTheme';
|
||||
import TopProgressBar from '@/components/topProgressBar';
|
||||
import DialogueContext from './context/dialogue';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
function CssWrapper({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const { mode } = useColorScheme();
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref?.current && mode) {
|
||||
ref?.current?.classList?.add(mode);
|
||||
if (mode === 'light') {
|
||||
ref?.current?.classList?.remove('dark');
|
||||
} else {
|
||||
ref?.current?.classList?.remove('light');
|
||||
}
|
||||
}
|
||||
}, [ref, mode]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className='h-full'>
|
||||
<TopProgressBar />
|
||||
<DialogueContext>
|
||||
<div className={`contents h-full`}>
|
||||
<div className="grid h-full w-screen grid-cols-1 grid-rows-[auto,1fr] overflow-hidden text-smd dark:text-gray-300 md:grid-cols-[280px,1fr] md:grid-rows-[1fr]">
|
||||
<LeftSider />
|
||||
<div className='relative min-h-0 min-w-0'>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogueContext>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
|
||||
return (
|
||||
<html lang="en" className="h-full font-sans">
|
||||
<body className={`h-full font-sans`}>
|
||||
<ThemeProvider theme={joyTheme}>
|
||||
<CssVarsProvider theme={joyTheme} defaultMode="light">
|
||||
<CssWrapper>
|
||||
{children}
|
||||
</CssWrapper>
|
||||
</CssVarsProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
export default RootLayout;
|
@ -1,122 +0,0 @@
|
||||
"use client";
|
||||
import { useRequest } from 'ahooks';
|
||||
import { useState } from 'react';
|
||||
import { Button, Input, Box, buttonClasses, Divider, Typography } from '@/lib/mui';
|
||||
import IconButton from '@mui/joy/IconButton';
|
||||
import SendRoundedIcon from '@mui/icons-material/SendRounded';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { sendPostRequest } from '@/utils/request';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
export default function Home() {
|
||||
const Schema = z.object({ query: z.string().min(1) });
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const methods = useForm<z.infer<typeof Schema>>({
|
||||
resolver: zodResolver(Schema),
|
||||
defaultValues: {},
|
||||
});
|
||||
const { data: scenesList } = useRequest(async () => await sendPostRequest('/v1/chat/dialogue/scenes'));
|
||||
|
||||
const submit = async ({ query }: z.infer<typeof Schema>) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
methods.reset();
|
||||
const res = await sendPostRequest('/v1/chat/dialogue/new', {
|
||||
chat_mode: 'chat_normal'
|
||||
});
|
||||
if (res?.success && res?.data?.conv_uid) {
|
||||
router.push(`/chat?id=${res?.data?.conv_uid}&initMessage=${query}`);
|
||||
}
|
||||
} catch (err) {
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='mx-auto h-full justify-center flex max-w-3xl flex-col gap-8 px-5 pt-6 xl:max-w-4xl'>
|
||||
<div className='max-w-xs my-0 mx-auto'>
|
||||
<Typography level="h3" className="text-center">DB-GPT</Typography>
|
||||
<Typography level="body1" className="text-center pt-4">
|
||||
Revolutionizing Database Interactions with Private LLM Technology
|
||||
</Typography>
|
||||
</div>
|
||||
<div className='grid gap-8 lg:grid-cols-3'>
|
||||
<div className='lg:col-span-3'>
|
||||
<Divider className="text-[#878c93]">Quick Start</Divider>
|
||||
<Box
|
||||
className='grid pt-7 rounded-xl gap-2 lg:grid-cols-3 lg:gap-6'
|
||||
sx={{
|
||||
[`& .${buttonClasses.root}`]: {
|
||||
color: 'var(--joy-palette-primary-solidColor)',
|
||||
backgroundColor: 'var(--joy-palette-primary-solidBg)',
|
||||
height: '52px',
|
||||
'&: hover': {
|
||||
backgroundColor: 'var(--joy-palette-primary-solidHoverBg)',
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{scenesList?.data?.map(scene => (
|
||||
<Button
|
||||
key={scene['chat_scene']}
|
||||
size="md"
|
||||
variant="solid"
|
||||
className='text-base rounded-none'
|
||||
onClick={async () => {
|
||||
const res = await sendPostRequest('/v1/chat/dialogue/new', {
|
||||
chat_mode: scene['chat_scene']
|
||||
});
|
||||
if (res?.success && res?.data?.conv_uid) {
|
||||
router.push(`/chat?id=${res?.data?.conv_uid}&scene=${scene['chat_scene']}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{scene['scene_name']
|
||||
}</Button>
|
||||
))}
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-6 mb-[10%] pointer-events-none inset-x-0 bottom-0 z-0 mx-auto flex w-full max-w-3xl flex-col items-center justify-center max-md:border-t xl:max-w-4xl [&>*]:pointer-events-auto'>
|
||||
<form
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
marginTop: 'auto',
|
||||
overflow: 'visible',
|
||||
background: 'none',
|
||||
justifyContent: 'center',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
height: '52px'
|
||||
}}
|
||||
onSubmit={(e) => {
|
||||
methods.handleSubmit(submit)(e);
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
sx={{ width: '100%' }}
|
||||
variant="outlined"
|
||||
placeholder='Ask anything'
|
||||
endDecorator={
|
||||
<IconButton type="submit" disabled={isLoading}>
|
||||
<SendRoundedIcon />
|
||||
</IconButton>
|
||||
}
|
||||
{...methods.register('query')}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import SendRoundedIcon from '@mui/icons-material/SendRounded';
|
||||
import { Card, CircularProgress, IconButton, Input, Stack, Select, Option, Tooltip } from '@/lib/mui';
|
||||
import React, { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { Message } from '@/types';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
import Markdown from 'markdown-to-jsx';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { okaidia } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
|
||||
type Props = {
|
||||
messages: Message[];
|
||||
onSubmit: (message: string, otherQueryBody?: any) => Promise<any>;
|
||||
initialMessage?: string;
|
||||
readOnly?: boolean;
|
||||
paramsList?: { [key: string]: string };
|
||||
clearIntialMessage?: () => void;
|
||||
};
|
||||
|
||||
const Schema = z.object({ query: z.string().min(1) });
|
||||
|
||||
const ChatBoxComp = ({
|
||||
messages,
|
||||
onSubmit,
|
||||
initialMessage,
|
||||
readOnly,
|
||||
paramsList,
|
||||
clearIntialMessage
|
||||
}: Props) => {
|
||||
const scrollableRef = React.useRef<HTMLDivElement>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [firstMsg, setFirstMsg] = useState<Message>();
|
||||
const [currentParam, setCurrentParam] = useState<string | undefined | null>();
|
||||
|
||||
const methods = useForm<z.infer<typeof Schema>>({
|
||||
resolver: zodResolver(Schema),
|
||||
defaultValues: {},
|
||||
});
|
||||
|
||||
const submit = async ({ query }: z.infer<typeof Schema>) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
methods.reset();
|
||||
await onSubmit(query, {
|
||||
select_param: paramsList?.[currentParam]
|
||||
});
|
||||
} catch (err) {
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInitMessage = async () => {
|
||||
try {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.delete('initMessage');
|
||||
window.history.replaceState(null, null, `?${searchParams.toString()}`);
|
||||
await submit({ query: (initialMessage as string) });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
clearIntialMessage?.();
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
overrides: {
|
||||
code: ({ children }) => (
|
||||
<SyntaxHighlighter language="javascript" style={okaidia}>
|
||||
{children}
|
||||
</SyntaxHighlighter>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!scrollableRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollableRef.current.scrollTo(0, scrollableRef.current.scrollHeight);
|
||||
}, [messages?.length]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (initialMessage && messages.length <= 0) {
|
||||
handleInitMessage();
|
||||
}
|
||||
}, [initialMessage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (paramsList && Object.keys(paramsList || {})?.length > 0) {
|
||||
setCurrentParam(Object.keys(paramsList || {})?.[0]);
|
||||
}
|
||||
}, [paramsList]);
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 py-6 sm:gap-8 xl:max-w-5xl'>
|
||||
<Stack
|
||||
direction={'column'}
|
||||
gap={2}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
flexBasis: '100%',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
maxHeight: '100%',
|
||||
minHeight: '100%',
|
||||
mx: 'auto',
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
ref={scrollableRef}
|
||||
direction={'column'}
|
||||
gap={2}
|
||||
sx={{
|
||||
boxSizing: 'border-box',
|
||||
maxWidth: '100%',
|
||||
width: '100%',
|
||||
mx: 'auto',
|
||||
flex: 1,
|
||||
maxHeight: '100%',
|
||||
overflowY: 'auto',
|
||||
p: 2,
|
||||
border: '1px solid',
|
||||
borderColor: 'var(--joy-palette-divider)'
|
||||
}}
|
||||
>
|
||||
{firstMsg && (
|
||||
<Card
|
||||
size="sm"
|
||||
variant={'outlined'}
|
||||
color={'primary'}
|
||||
className="message-agent"
|
||||
sx={{
|
||||
mr: 'auto',
|
||||
ml: 'none',
|
||||
whiteSpace: 'pre-wrap',
|
||||
}}
|
||||
>
|
||||
{firstMsg?.context}
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{messages.filter(item => ['view', 'human'].includes(item.role)).map((each, index) => (
|
||||
<Stack
|
||||
key={index}
|
||||
sx={{
|
||||
mr: each.role === 'view' ? 'auto' : 'none',
|
||||
ml: each.role === 'human' ? 'auto' : 'none',
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
size="sm"
|
||||
variant={'outlined'}
|
||||
className={
|
||||
each.role === 'view' ? 'message-agent' : 'message-human'
|
||||
}
|
||||
color={each.role === 'view' ? 'primary' : 'neutral'}
|
||||
sx={(theme) => ({
|
||||
px: 2,
|
||||
'ol, ul': {
|
||||
my: 0,
|
||||
pl: 2,
|
||||
},
|
||||
ol: {
|
||||
listStyle: 'numeric',
|
||||
},
|
||||
ul: {
|
||||
listStyle: 'disc',
|
||||
mb: 2,
|
||||
},
|
||||
li: {
|
||||
my: 1,
|
||||
},
|
||||
a: {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
})}
|
||||
>
|
||||
<Markdown options={options}>
|
||||
{each.context?.replaceAll('\\n', '\n')}
|
||||
</Markdown>
|
||||
</Card>
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
{isLoading && (
|
||||
<CircularProgress
|
||||
variant="soft"
|
||||
color="neutral"
|
||||
size="sm"
|
||||
sx={{ mx: 'auto', my: 2 }}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
{!readOnly && (
|
||||
<form
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
marginTop: 'auto',
|
||||
overflow: 'visible',
|
||||
background: 'none',
|
||||
justifyContent: 'center',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
flexDirection: 'column',
|
||||
gap: '12px'
|
||||
}}
|
||||
onSubmit={(e) => {
|
||||
e.stopPropagation();
|
||||
methods.handleSubmit(submit)(e);
|
||||
}}
|
||||
>
|
||||
{(Object.keys(paramsList || {}).length > 0) && (
|
||||
<div className='flex items-center gap-3'>
|
||||
<Select
|
||||
value={currentParam}
|
||||
onChange={(e, newValue) => {
|
||||
setCurrentParam(newValue);
|
||||
}}
|
||||
className='max-w-xs'
|
||||
>
|
||||
{Object.keys(paramsList || {}).map(paramItem => (
|
||||
<Option
|
||||
key={paramItem}
|
||||
value={paramItem}
|
||||
>
|
||||
{paramsList?.[paramItem]}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<Tooltip
|
||||
className="cursor-pointer"
|
||||
title={currentParam}
|
||||
placement="top"
|
||||
variant="outlined"
|
||||
>
|
||||
<InfoOutlinedIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Input
|
||||
sx={{ width: '100%' }}
|
||||
variant="outlined"
|
||||
endDecorator={
|
||||
<IconButton type="submit" disabled={isLoading}>
|
||||
<SendRoundedIcon />
|
||||
</IconButton>
|
||||
}
|
||||
{...methods.register('query')}
|
||||
/>
|
||||
</form>
|
||||
)}
|
||||
</Stack>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default ChatBoxComp;
|
@ -1,253 +0,0 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import SendRoundedIcon from '@mui/icons-material/SendRounded';
|
||||
import { Card, CircularProgress, IconButton, Input, Stack, Select, Option, Tooltip, Box, useColorScheme } from '@/lib/mui';
|
||||
import React, { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { Message } from '@/types';
|
||||
import FaceRetouchingNaturalOutlinedIcon from '@mui/icons-material/FaceRetouchingNaturalOutlined';
|
||||
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
|
||||
import Markdown from 'markdown-to-jsx';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { okaidia } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
type Props = {
|
||||
messages: Message[];
|
||||
onSubmit: (message: string, otherQueryBody?: any) => Promise<any>;
|
||||
readOnly?: boolean;
|
||||
paramsList?: { [key: string]: string };
|
||||
clearIntialMessage?: () => void;
|
||||
};
|
||||
|
||||
const Schema = z.object({ query: z.string().min(1) });
|
||||
|
||||
const ChatBoxComp = ({
|
||||
messages,
|
||||
onSubmit,
|
||||
readOnly,
|
||||
paramsList,
|
||||
clearIntialMessage
|
||||
}: Props) => {
|
||||
const searchParams = useSearchParams();
|
||||
const initMessage = searchParams.get('initMessage');
|
||||
const scrollableRef = React.useRef<HTMLDivElement>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [currentParam, setCurrentParam] = useState<string | undefined | null>();
|
||||
|
||||
const methods = useForm<z.infer<typeof Schema>>({
|
||||
resolver: zodResolver(Schema),
|
||||
defaultValues: {},
|
||||
});
|
||||
|
||||
const submit = async ({ query }: z.infer<typeof Schema>) => {
|
||||
try {
|
||||
console.log('submit');
|
||||
setIsLoading(true);
|
||||
methods.reset();
|
||||
await onSubmit(query, {
|
||||
select_param: paramsList?.[currentParam]
|
||||
});
|
||||
} catch (err) {
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInitMessage = async () => {
|
||||
try {
|
||||
const searchParamsTemp = new URLSearchParams(window.location.search);
|
||||
const initMessage = searchParamsTemp.get('initMessage');
|
||||
searchParamsTemp.delete('initMessage');
|
||||
window.history.replaceState(null, null, `?${searchParamsTemp.toString()}`);
|
||||
await submit({ query: (initMessage as string) });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
clearIntialMessage?.();
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
overrides: {
|
||||
code: ({ children }) => (
|
||||
<SyntaxHighlighter language="javascript" style={okaidia}>
|
||||
{children}
|
||||
</SyntaxHighlighter>
|
||||
),
|
||||
},
|
||||
wrapper: React.Fragment,
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!scrollableRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollableRef.current.scrollTo(0, scrollableRef.current.scrollHeight);
|
||||
}, [messages?.length]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (initMessage && messages.length <= 0) {
|
||||
handleInitMessage();
|
||||
}
|
||||
}, [initMessage, messages.length]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (paramsList && Object.keys(paramsList || {})?.length > 0) {
|
||||
setCurrentParam(Object.keys(paramsList || {})?.[0]);
|
||||
}
|
||||
}, [paramsList]);
|
||||
|
||||
return (
|
||||
<div className='w-full h-full'>
|
||||
<Stack
|
||||
className="w-full h-full bg-[#fefefe] dark:bg-[#212121]"
|
||||
sx={{
|
||||
'table': {
|
||||
borderCollapse: 'collapse',
|
||||
border: '1px solid #ccc',
|
||||
width: '100%',
|
||||
},
|
||||
'th, td': {
|
||||
border: '1px solid #ccc',
|
||||
padding: '10px',
|
||||
textAlign: 'center'
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
ref={scrollableRef}
|
||||
direction={'column'}
|
||||
sx={{
|
||||
overflowY: 'auto',
|
||||
maxHeight: '100%',
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
{messages.filter(item => ['view', 'human'].includes(item.role))?.map((each, index) => {
|
||||
return (
|
||||
<Stack
|
||||
key={index}
|
||||
>
|
||||
<Card
|
||||
size="sm"
|
||||
variant={'outlined'}
|
||||
color={each.role === 'view' ? 'primary' : 'neutral'}
|
||||
sx={(theme) => ({
|
||||
background: each.role === 'view' ? 'var(--joy-palette-primary-softBg, var(--joy-palette-primary-100, #DDF1FF))': 'unset',
|
||||
border: 'unset',
|
||||
borderRadius: 'unset',
|
||||
padding: '24px 0 26px 0',
|
||||
lineHeight: '24px',
|
||||
})}
|
||||
>
|
||||
<Box sx={{ width: '76%', margin: '0 auto' }} className="flex flex-row">
|
||||
<div className='mr-3 inline'>
|
||||
{each.role === 'view' ? (
|
||||
<SmartToyOutlinedIcon />
|
||||
) : (
|
||||
<FaceRetouchingNaturalOutlinedIcon />
|
||||
)}
|
||||
</div>
|
||||
<div className='inline align-middle mt-0.5 max-w-full flex-1 overflow-auto'>
|
||||
<Markdown options={options}>
|
||||
{each.context?.replaceAll('\\n', '\n')}
|
||||
</Markdown>
|
||||
</div>
|
||||
</Box>
|
||||
</Card>
|
||||
</Stack>
|
||||
)
|
||||
})}
|
||||
{isLoading && (
|
||||
<CircularProgress
|
||||
variant="soft"
|
||||
color="neutral"
|
||||
size="sm"
|
||||
sx={{ mx: 'auto', my: 2 }}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
{!readOnly && (
|
||||
<Box
|
||||
className="bg-[#fefefe] dark:bg-[#212121] before:bg-[#fefefe] before:dark:bg-[#212121]"
|
||||
sx={{
|
||||
position: 'relative',
|
||||
'&::before': {
|
||||
content: '" "',
|
||||
position: 'absolute',
|
||||
top: '-18px',
|
||||
left: '0',
|
||||
right: '0',
|
||||
width: '100%',
|
||||
margin: '0 auto',
|
||||
height: '20px',
|
||||
filter: 'blur(10px)',
|
||||
zIndex: 2,
|
||||
}
|
||||
}}
|
||||
>
|
||||
<form
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
width: '76%',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
marginTop: 'auto',
|
||||
overflow: 'visible',
|
||||
background: 'none',
|
||||
justifyContent: 'center',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
flexDirection: 'column',
|
||||
gap: '12px',
|
||||
paddingBottom: '58px',
|
||||
paddingTop: '20px'
|
||||
}}
|
||||
onSubmit={(e) => {
|
||||
e.stopPropagation();
|
||||
methods.handleSubmit(submit)(e);
|
||||
}}
|
||||
>
|
||||
{(Object.keys(paramsList || {}).length > 0) && (
|
||||
<div className='flex items-center gap-3'>
|
||||
<Select
|
||||
value={currentParam}
|
||||
onChange={(e, newValue) => {
|
||||
setCurrentParam(newValue);
|
||||
}}
|
||||
sx={{ maxWidth: '100%' }}
|
||||
>
|
||||
{Object.keys(paramsList || {}).map(paramItem => (
|
||||
<Option
|
||||
key={paramItem}
|
||||
value={paramItem}
|
||||
>
|
||||
{paramItem}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Input
|
||||
className='w-full h-12'
|
||||
variant="outlined"
|
||||
endDecorator={
|
||||
<IconButton type="submit" disabled={isLoading}>
|
||||
<SendRoundedIcon />
|
||||
</IconButton>
|
||||
}
|
||||
{...methods.register('query')}
|
||||
/>
|
||||
</form>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChatBoxComp;
|
@ -1,32 +0,0 @@
|
||||
import DarkModeRoundedIcon from '@mui/icons-material/DarkModeRounded';
|
||||
import LightModeRoundedIcon from '@mui/icons-material/LightModeRounded';
|
||||
import { IconButton,useColorScheme } from '@/lib/mui';
|
||||
import React from 'react';
|
||||
|
||||
export default function ColorSchemeToggle() {
|
||||
const { mode, setMode } = useColorScheme();
|
||||
const [mounted, setMounted] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
if (!mounted) {
|
||||
return <IconButton size="sm" variant="outlined" color="primary" />;
|
||||
}
|
||||
return (
|
||||
<IconButton
|
||||
id="toggle-mode"
|
||||
size="sm"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
if (mode === 'light') {
|
||||
setMode('dark');
|
||||
} else {
|
||||
setMode('light');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{mode === 'light' ? <DarkModeRoundedIcon /> : <LightModeRoundedIcon />}
|
||||
</IconButton>
|
||||
);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { Box, Typography } from '@/lib/mui';
|
||||
import Image from 'next/image';
|
||||
import ColorSchemeToggle from './colorSchemeToggle';
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
gap: 2,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gridColumn: '1 / -1',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'divider',
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 1100,
|
||||
background: 'var(--joy-palette-background-body)'
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center justify-center gap-3'>
|
||||
<Image
|
||||
src="/databerry-logo-icon.png"
|
||||
width="200"
|
||||
height="200"
|
||||
className='w-12'
|
||||
alt="Databerry"
|
||||
/>
|
||||
<Typography component="h1" fontWeight="xl">
|
||||
DB-GPT
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<ColorSchemeToggle />
|
||||
</div>
|
||||
</Box>
|
||||
)
|
||||
};
|
||||
|
||||
export default Header;
|
@ -1,255 +0,0 @@
|
||||
"use client";
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { Modal } from 'antd';
|
||||
import { Box, List, ListItem, ListItemButton, ListItemDecorator, ListItemContent, Typography, Button, useColorScheme, IconButton } from '@/lib/mui';
|
||||
import Article from '@mui/icons-material/Article';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import WbSunnyIcon from '@mui/icons-material/WbSunny';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import SmsOutlinedIcon from '@mui/icons-material/SmsOutlined';import { useDialogueContext } from '@/app/context/dialogue';
|
||||
import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined';
|
||||
import { sendPostRequest } from '@/utils/request';
|
||||
|
||||
const LeftSider = () => {
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const id = searchParams.get('id');
|
||||
const router = useRouter();
|
||||
const { dialogueList, queryDialogueList, refreshDialogList } = useDialogueContext();
|
||||
const { mode, setMode } = useColorScheme();
|
||||
|
||||
const menus = useMemo(() => {
|
||||
return [{
|
||||
label: 'Knowledge Space',
|
||||
route: '/datastores',
|
||||
icon: <Article fontSize="small" />,
|
||||
active: pathname === '/datastores'
|
||||
}];
|
||||
}, [pathname]);
|
||||
|
||||
const handleChangeTheme = () => {
|
||||
if (mode === 'light') {
|
||||
setMode('dark');
|
||||
} else {
|
||||
setMode('light');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await queryDialogueList();
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className='flex h-12 items-center justify-between border-b px-4 dark:border-gray-800 dark:bg-gray-800/70 md:hidden'>
|
||||
<div>
|
||||
<MenuIcon />
|
||||
</div>
|
||||
<span className='truncate px-4'>New Chat</span>
|
||||
<a href='' className='-mr-3 flex h-9 w-9 shrink-0 items-center justify-center'>
|
||||
<AddIcon />
|
||||
</a>
|
||||
</nav>
|
||||
<nav className="grid max-h-screen h-full max-md:hidden">
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRight: '1px solid',
|
||||
borderColor: 'divider',
|
||||
maxHeight: '100vh',
|
||||
position: 'sticky',
|
||||
left: '0px',
|
||||
top: '0px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
gap: 2,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center gap-3'>
|
||||
<Typography component="h1" fontWeight="xl">
|
||||
DB-GPT
|
||||
</Typography>
|
||||
</div>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
px: 2
|
||||
}}
|
||||
>
|
||||
<Link href={`/`}>
|
||||
<Button
|
||||
color="primary"
|
||||
className='w-full bg-gradient-to-r from-[#31afff] to-[#1677ff] dark:bg-gradient-to-r dark:from-[#6a6a6a] dark:to-[#80868f]'
|
||||
style={{
|
||||
color: '#fff'
|
||||
}}
|
||||
>
|
||||
+ New Chat
|
||||
</Button>
|
||||
</Link>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
display: {
|
||||
xs: 'none',
|
||||
sm: 'initial',
|
||||
},
|
||||
maxHeight: '100%',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<List size="sm" sx={{ '--ListItem-radius': '8px' }}>
|
||||
<ListItem nested>
|
||||
<List
|
||||
size="sm"
|
||||
aria-labelledby="nav-list-browse"
|
||||
sx={{
|
||||
'& .JoyListItemButton-root': { p: '8px' },
|
||||
gap: '4px'
|
||||
}}
|
||||
>
|
||||
{dialogueList?.data?.map((each) => {
|
||||
const isSelect = (pathname === `/chat` || pathname === '/chat/') && id === each.conv_uid;
|
||||
return (
|
||||
<ListItem key={each.conv_uid}>
|
||||
<ListItemButton
|
||||
selected={isSelect}
|
||||
variant={isSelect ? 'soft' : 'plain'}
|
||||
sx={{
|
||||
'&:hover .del-btn': {
|
||||
visibility: 'visible'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemContent>
|
||||
<Link href={`/chat?id=${each.conv_uid}&scene=${each?.chat_mode}`} className="flex items-center justify-between">
|
||||
<Typography fontSize={14} noWrap={true}>
|
||||
<SmsOutlinedIcon style={{ marginRight: '0.5rem' }} />
|
||||
{each?.user_name || each?.user_input || 'undefined'}
|
||||
</Typography>
|
||||
<IconButton
|
||||
color="neutral"
|
||||
variant="plain"
|
||||
size="sm"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
Modal.confirm({
|
||||
title: 'Delete Chat',
|
||||
content: 'Are you sure delete this chat?',
|
||||
width: '276px',
|
||||
centered: true,
|
||||
async onOk() {
|
||||
await sendPostRequest(`/v1/chat/dialogue/delete?con_uid=${each.conv_uid}`);
|
||||
await refreshDialogList();
|
||||
if (pathname === `/chat` && searchParams.get('id') === each.conv_uid) {
|
||||
router.push('/');
|
||||
}
|
||||
}
|
||||
})
|
||||
}}
|
||||
className='del-btn invisible'
|
||||
>
|
||||
<DeleteOutlineOutlinedIcon />
|
||||
</IconButton>
|
||||
</Link>
|
||||
</ListItemContent>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
<div className='flex flex-col justify-between flex-1'>
|
||||
<div></div>
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
pt: 3,
|
||||
pb: 6,
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'divider',
|
||||
display: {
|
||||
xs: 'none',
|
||||
sm: 'initial',
|
||||
},
|
||||
position: 'sticky',
|
||||
bottom: 0,
|
||||
zIndex: 100,
|
||||
background: 'var(--joy-palette-background-body)'
|
||||
}}
|
||||
>
|
||||
<List size="sm" sx={{ '--ListItem-radius': '8px' }}>
|
||||
<ListItem nested>
|
||||
<List
|
||||
size="sm"
|
||||
aria-labelledby="nav-list-browse"
|
||||
sx={{
|
||||
'& .JoyListItemButton-root': { p: '8px' },
|
||||
}}
|
||||
>
|
||||
{menus.map((each) => (
|
||||
<Link key={each.route} href={each.route}>
|
||||
<ListItem>
|
||||
<ListItemButton
|
||||
color="neutral"
|
||||
sx={{ marginBottom: 1, height: '2.5rem' }}
|
||||
selected={each.active}
|
||||
variant={each.active ? 'soft' : 'plain'}
|
||||
>
|
||||
<ListItemDecorator
|
||||
sx={{
|
||||
color: each.active ? 'inherit' : 'neutral.500',
|
||||
}}
|
||||
>
|
||||
{each.icon}
|
||||
</ListItemDecorator>
|
||||
<ListItemContent>{each.label}</ListItemContent>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</Link>
|
||||
))}
|
||||
</List>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemButton
|
||||
sx={{ height: '2.5rem' }}
|
||||
onClick={handleChangeTheme}
|
||||
>
|
||||
<ListItemDecorator>
|
||||
{mode === 'dark' ? (
|
||||
<DarkModeIcon fontSize="small"/>
|
||||
) : (
|
||||
<WbSunnyIcon fontSize="small"/>
|
||||
)}
|
||||
</ListItemDecorator>
|
||||
<ListItemContent>Theme</ListItemContent>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
</div>
|
||||
</Box>
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
export default LeftSider;
|
@ -1,61 +0,0 @@
|
||||
import Router from 'next/router';
|
||||
import NProgress from 'nprogress';
|
||||
|
||||
let timer: any;
|
||||
let state: any;
|
||||
let activeRequests = 0;
|
||||
const delay = 250;
|
||||
|
||||
function load() {
|
||||
if (state === 'loading') {
|
||||
return;
|
||||
}
|
||||
|
||||
state = 'loading';
|
||||
|
||||
timer = setTimeout(function () {
|
||||
NProgress.start();
|
||||
}, delay); // only show progress bar if it takes longer than the delay
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (activeRequests > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = 'stop';
|
||||
|
||||
clearTimeout(timer);
|
||||
NProgress.done();
|
||||
}
|
||||
|
||||
Router.events.on('routeChangeStart', load);
|
||||
Router.events.on('routeChangeComplete', stop);
|
||||
Router.events.on('routeChangeError', stop);
|
||||
|
||||
if (typeof window !== 'undefined' && typeof window?.fetch === 'function') {
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = async function (...args) {
|
||||
if (activeRequests === 0) {
|
||||
load();
|
||||
}
|
||||
|
||||
activeRequests++;
|
||||
|
||||
try {
|
||||
const response = await originalFetch(...args);
|
||||
return response;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
} finally {
|
||||
activeRequests -= 1;
|
||||
if (activeRequests === 0) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default function TopProgressBar() {
|
||||
return null;
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import { extendTheme } from '@mui/joy/styles';
|
||||
import colors from '@mui/joy/colors';
|
||||
|
||||
export const joyTheme = extendTheme({
|
||||
colorSchemes: {
|
||||
light: {
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: {
|
||||
...colors.grey,
|
||||
solidBg: '#e6f4ff',
|
||||
solidColor: '#1677ff',
|
||||
solidHoverBg: '#e6f4ff',
|
||||
},
|
||||
neutral: {
|
||||
plainColor: '#4d4d4d',
|
||||
plainHoverColor: '#131318',
|
||||
plainHoverBg: '#EBEBEF',
|
||||
plainActiveBg: '#D8D8DF',
|
||||
plainDisabledColor: '#B9B9C6'
|
||||
},
|
||||
background: {
|
||||
body: '#fff',
|
||||
surface: '#fff'
|
||||
},
|
||||
text: {
|
||||
primary: '#505050',
|
||||
},
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
palette: {
|
||||
mode: 'light',
|
||||
primary: {
|
||||
...colors.grey,
|
||||
softBg: '#353539',
|
||||
softHoverBg: '#35353978',
|
||||
softDisabledBg: '#353539',
|
||||
solidBg: '#51525beb',
|
||||
solidHoverBg: '#51525beb',
|
||||
},
|
||||
neutral: {
|
||||
plainColor: '#D8D8DF',
|
||||
plainHoverColor: '#F7F7F8',
|
||||
plainHoverBg: '#353539',
|
||||
plainActiveBg: '#434356',
|
||||
plainDisabledColor: '#434356',
|
||||
outlinedBorder: '#353539',
|
||||
outlinedHoverBorder: '#454651'
|
||||
},
|
||||
text: {
|
||||
primary: '#EBEBEF'
|
||||
},
|
||||
background: {
|
||||
body: '#212121',
|
||||
surface: '#51525beb',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
body: 'Josefin Sans, sans-serif',
|
||||
display: 'Josefin Sans, sans-serif',
|
||||
},
|
||||
typography: {
|
||||
display1: {
|
||||
background:
|
||||
'linear-gradient(-30deg, var(--joy-palette-primary-900), var(--joy-palette-primary-400))',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
},
|
||||
},
|
||||
});
|
@ -1,170 +0,0 @@
|
||||
import {
|
||||
EventStreamContentType,
|
||||
fetchEventSource,
|
||||
} from '@microsoft/fetch-event-source';
|
||||
import useStateReducer from './useStateReducer';
|
||||
import { Message } from '@/types';
|
||||
import { useEffect } from 'react';
|
||||
import { useDialogueContext } from '@/app/context/dialogue';
|
||||
|
||||
type Props = {
|
||||
queryAgentURL: string;
|
||||
channel?: "dashboard" | "website" | "slack" | "crisp";
|
||||
queryBody?: any;
|
||||
initHistory?: Message[];
|
||||
};
|
||||
|
||||
const useAgentChat = ({
|
||||
queryAgentURL,
|
||||
channel,
|
||||
queryBody,
|
||||
initHistory
|
||||
}: Props) => {
|
||||
const [state, setState] = useStateReducer({
|
||||
history: (initHistory || []) as { role: 'human' | 'view'; context: string; id?: string }[],
|
||||
});
|
||||
|
||||
const { refreshDialogList } = useDialogueContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (initHistory) setState({ history: initHistory });
|
||||
}, [initHistory]);
|
||||
|
||||
const handleChatSubmit = async (context: string, otherQueryBody?: any) => {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const history = [...state.history, { role: 'human', context }];
|
||||
const nextIndex = history.length;
|
||||
|
||||
setState({
|
||||
history: history as any,
|
||||
});
|
||||
|
||||
let answer = '';
|
||||
let error = '';
|
||||
|
||||
try {
|
||||
const ctrl = new AbortController();
|
||||
let buffer = '';
|
||||
|
||||
await fetchEventSource(`${process.env.API_BASE_URL ? process.env.API_BASE_URL : ''}${"/api" + queryAgentURL}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...otherQueryBody,
|
||||
...queryBody,
|
||||
user_input: context,
|
||||
channel,
|
||||
}),
|
||||
signal: ctrl.signal,
|
||||
|
||||
async onopen(response) {
|
||||
if (history.length <= 1) {
|
||||
refreshDialogList();
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.delete('initMessage');
|
||||
window.history.replaceState(null, null, `?${searchParams.toString()}`);
|
||||
}
|
||||
if (
|
||||
response.ok &&
|
||||
response.headers.get('content-type') === EventStreamContentType
|
||||
) {
|
||||
return; // everything's good
|
||||
} else if (
|
||||
response.status >= 400 &&
|
||||
response.status < 500 &&
|
||||
response.status !== 429
|
||||
) {
|
||||
if (response.status === 402) {
|
||||
//throw new ApiError(ApiErrorType.USAGE_LIMIT);
|
||||
}
|
||||
// client-side errors are usually non-retriable:
|
||||
//throw new FatalError();
|
||||
} else {
|
||||
//throw new RetriableError();
|
||||
}
|
||||
},
|
||||
onclose() {
|
||||
// if the server closes the connection unexpectedly, retry:
|
||||
console.log('onclose');
|
||||
},
|
||||
onerror(err) {
|
||||
throw new Error(err);
|
||||
},
|
||||
onmessage: (event) => {
|
||||
console.log(event, 'e');
|
||||
event.data = event.data.replaceAll('\\n', '\n');
|
||||
|
||||
if (event.data === '[DONE]') {
|
||||
ctrl.abort();
|
||||
} else if (event.data?.startsWith('[ERROR]')) {
|
||||
ctrl.abort();
|
||||
setState({
|
||||
history: [
|
||||
...history,
|
||||
{
|
||||
role: 'view',
|
||||
context: event.data.replace('[ERROR]', ''),
|
||||
} as any,
|
||||
],
|
||||
});
|
||||
} else {
|
||||
const h = [...history];
|
||||
if (event.data) {
|
||||
if (h?.[nextIndex]) {
|
||||
h[nextIndex].context = `${event.data}`;
|
||||
} else {
|
||||
h.push({ role: 'view', context: event.data });
|
||||
}
|
||||
setState({
|
||||
history: h as any,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('---e', err);
|
||||
|
||||
setState({
|
||||
history: [
|
||||
...history,
|
||||
{ role: 'view', context: answer || '请求出错' as string },
|
||||
] as any,
|
||||
});
|
||||
// if (err instanceof ApiError) {
|
||||
// if (err?.message) {
|
||||
// error = err?.message;
|
||||
|
||||
// if (error === ApiErrorType.USAGE_LIMIT) {
|
||||
// answer =
|
||||
// 'Usage limit reached. Please upgrade your plan to get higher usage.';
|
||||
// } else {
|
||||
// answer = `Error: ${error}`;
|
||||
// }
|
||||
// } else {
|
||||
// answer = `Error: ${error}`;
|
||||
// }
|
||||
|
||||
// setState({
|
||||
// history: [
|
||||
// ...history,
|
||||
// { from: 'ai', message: answer as string },
|
||||
// ] as any,
|
||||
// });
|
||||
// }
|
||||
}
|
||||
};
|
||||
return {
|
||||
handleChatSubmit,
|
||||
history: state.history,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAgentChat;
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export const useNewChat = () => {
|
||||
const [message, setMessage] = useState<string | undefined>("hello");
|
||||
|
||||
return {
|
||||
message,
|
||||
setMessage
|
||||
};
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import { useReducer } from 'react';
|
||||
|
||||
const useStateReducer = <T>(initialState: T) => {
|
||||
const methods = useReducer(
|
||||
(state: T, newState: Partial<T>) => ({
|
||||
...state,
|
||||
...newState,
|
||||
}),
|
||||
{
|
||||
...initialState,
|
||||
}
|
||||
);
|
||||
|
||||
return methods;
|
||||
};
|
||||
|
||||
export default useStateReducer;
|
@ -1,28 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import cuid from 'cuid';
|
||||
|
||||
const useVisitorId = () => {
|
||||
const [visitorId, setVisitorId] = React.useState('');
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
let id = localStorage.getItem('visitorId');
|
||||
|
||||
if (!id) {
|
||||
id = cuid();
|
||||
localStorage.setItem('visitorId', id);
|
||||
}
|
||||
|
||||
setVisitorId(id);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
visitorId,
|
||||
};
|
||||
};
|
||||
|
||||
export default useVisitorId;
|
@ -1,3 +0,0 @@
|
||||
"use client";
|
||||
|
||||
export * from '@mui/joy';
|
@ -1,16 +0,0 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'export',
|
||||
experimental: {
|
||||
esmExternals: 'loose'
|
||||
},
|
||||
typescript: {
|
||||
ignoreBuildErrors: true
|
||||
},
|
||||
env: {
|
||||
API_BASE_URL: process.env.API_BASE_URL
|
||||
},
|
||||
trailingSlash: true
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
@ -1,32 +0,0 @@
|
||||
/* Make clicks pass-through */
|
||||
#nprogress {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: var(--joy-palette-primary-500, #096BDE);
|
||||
|
||||
position: fixed;
|
||||
z-index: 10031;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
/* Fancy blur effect */
|
||||
#nprogress .peg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 10px var(--joy-palette-primary-500, #096BDE), 0 0 5px var(--joy-palette-primary-500, #096BDE);
|
||||
opacity: 1;
|
||||
|
||||
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
||||
-ms-transform: rotate(3deg) translate(0px, -4px);
|
||||
transform: rotate(3deg) translate(0px, -4px);
|
||||
}
|
||||
|
14907
datacenter/package-lock.json
generated
14907
datacenter/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,62 +0,0 @@
|
||||
{
|
||||
"name": "datacenter",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"build:prod": "APP_ENV=prod next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"export": "next export",
|
||||
"compile": "next build && next export"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/pro-components": "^2.6.2",
|
||||
"@emotion/cache": "^11.10.5",
|
||||
"@emotion/react": "^11.10.6",
|
||||
"@emotion/styled": "^11.10.6",
|
||||
"@hookform/resolvers": "^3.0.0",
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"@mui/icons-material": "^5.11.16",
|
||||
"@mui/joy": "5.0.0-alpha.72",
|
||||
"@mui/lab": "5.0.0-alpha.124",
|
||||
"@mui/material": "^5.13.6",
|
||||
"@mui/styled-engine-sc": "^5.12.0",
|
||||
"@mui/utils": "^5.11.13",
|
||||
"@prisma/client": "^4.12.0",
|
||||
"@types/node": "20.3.1",
|
||||
"@types/react": "18.2.14",
|
||||
"@types/react-dom": "18.2.6",
|
||||
"ahooks": "^3.7.8",
|
||||
"antd": "^5.6.2",
|
||||
"autoprefixer": "10.4.14",
|
||||
"axios": "^1.3.4",
|
||||
"bizcharts": "^4.1.22",
|
||||
"cuid": "^3.0.0",
|
||||
"eslint": "8.43.0",
|
||||
"eslint-config-next": "13.4.7",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-to-jsx": "^7.2.1",
|
||||
"moment": "^2.29.4",
|
||||
"next": "13.4.7",
|
||||
"next-auth": "^4.20.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"postcss": "8.4.24",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.8",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"styled-components": "^5.3.11",
|
||||
"swr": "^2.1.1",
|
||||
"tailwindcss": "3.3.2",
|
||||
"typescript": "5.1.3",
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/react-syntax-highlighter": "^15.5.7"
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 197 KiB |
@ -1,19 +0,0 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['"Josefin Sans"', ...defaultTheme.fontFamily.sans],
|
||||
}
|
||||
},
|
||||
},
|
||||
darkMode: 'class',
|
||||
plugins: [],
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import { NextApiRequest, NextPage } from 'next/types';
|
||||
import { Session } from 'next-auth';
|
||||
|
||||
export type Message = { role: 'human' | 'view'; context: string; createdAt?: Date };
|
||||
|
||||
export type AppNextApiRequest = NextApiRequest & {
|
||||
session: Session;
|
||||
};
|
||||
|
||||
export interface DialogueItem {
|
||||
chat_mode: string;
|
||||
conv_uid: string;
|
||||
select_param?: string;
|
||||
user_input?: string;
|
||||
user_name?: string;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
export enum ApiErrorType {
|
||||
UNAUTHORIZED = 'UNAUTHORIZED',
|
||||
USAGE_LIMIT = 'USAGE_LIMIT',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
INVALID_REQUEST = 'INVALID_REQUEST',
|
||||
WEBPAGE_IS_SITEMAP = 'WEBPAGE_IS_SITEMAP',
|
||||
EMPTY_DATASOURCE = 'EMPTY_DATASOURCE',
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(message: ApiErrorType, public status?: number) {
|
||||
super(message);
|
||||
|
||||
if (!status) {
|
||||
switch (message) {
|
||||
case ApiErrorType.UNAUTHORIZED:
|
||||
this.status = 403;
|
||||
break;
|
||||
case ApiErrorType.USAGE_LIMIT:
|
||||
this.status = 402;
|
||||
break;
|
||||
case ApiErrorType.NOT_FOUND:
|
||||
this.status = 404;
|
||||
break;
|
||||
case ApiErrorType.INVALID_REQUEST:
|
||||
this.status = 400;
|
||||
break;
|
||||
case ApiErrorType.EMPTY_DATASOURCE:
|
||||
this.status = 400;
|
||||
break;
|
||||
default:
|
||||
this.status = 500;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(this, ApiError.prototype);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: process.env.API_BASE_URL,
|
||||
});
|
||||
|
||||
api.defaults.timeout = 10000;
|
||||
|
||||
api.interceptors.response.use(
|
||||
response => response.data,
|
||||
err => Promise.reject(err)
|
||||
);
|
||||
|
||||
export default api;
|
@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
export function createCtx<A>(): readonly [
|
||||
() => A,
|
||||
React.Provider<A | undefined>,
|
||||
] {
|
||||
const ctx = React.createContext<A | undefined>(undefined);
|
||||
function useCtx() {
|
||||
const c = React.useContext(ctx);
|
||||
if (c === undefined)
|
||||
throw new Error('useCtx must be inside a Provider with a value');
|
||||
return c;
|
||||
}
|
||||
return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import { message } from 'antd';
|
||||
import axios from './ctx-axios';
|
||||
import { isPlainObject } from 'lodash';
|
||||
|
||||
const DEFAULT_HEADERS = {
|
||||
'content-type': 'application/json',
|
||||
};
|
||||
|
||||
// body 字段 trim
|
||||
const sanitizeBody = (obj: Record<string, any>): string => {
|
||||
// simple shallow copy to avoid changing original obj
|
||||
if (!isPlainObject(obj)) return JSON.stringify(obj);
|
||||
const resObj = { ...obj };
|
||||
for (const key in resObj) {
|
||||
const val = resObj[key];
|
||||
if (typeof val === 'string') {
|
||||
resObj[key] = val.trim();
|
||||
}
|
||||
}
|
||||
return JSON.stringify(resObj);
|
||||
};
|
||||
|
||||
export const sendGetRequest = (url: string, qs?: { [key: string]: any }) => {
|
||||
if (qs) {
|
||||
const str = Object.keys(qs)
|
||||
.filter(k => qs[k] !== undefined && qs[k] !== '')
|
||||
.map(k => `${k}=${qs[k]}`)
|
||||
.join('&');
|
||||
if (str) {
|
||||
url += `?${str}`;
|
||||
}
|
||||
}
|
||||
return axios.get("/api" + url, {
|
||||
headers: DEFAULT_HEADERS
|
||||
}).then(res => res).catch(err => {
|
||||
message.error(err);
|
||||
Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
export const sendSpaceGetRequest = (url: string, qs?: { [key: string]: any }) => {
|
||||
if (qs) {
|
||||
const str = Object.keys(qs)
|
||||
.filter(k => qs[k] !== undefined && qs[k] !== '')
|
||||
.map(k => `${k}=${qs[k]}`)
|
||||
.join('&');
|
||||
if (str) {
|
||||
url += `?${str}`;
|
||||
}
|
||||
}
|
||||
return axios.get(url, {
|
||||
headers: DEFAULT_HEADERS
|
||||
}).then(res => res).catch(err => {
|
||||
message.error(err);
|
||||
Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
export const sendPostRequest = (url: string, body?: any) => {
|
||||
const reqBody = sanitizeBody(body);
|
||||
return axios.post("/api" + url, {
|
||||
body: reqBody,
|
||||
headers: DEFAULT_HEADERS
|
||||
}).then(res => res).catch(err => {
|
||||
message.error(err);
|
||||
Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
export const sendSpacePostRequest = (url: string, body?: any) => {
|
||||
const reqBody = sanitizeBody(body);
|
||||
return axios.post(url, body, {
|
||||
headers: DEFAULT_HEADERS
|
||||
}).then(res => res).catch(err => {
|
||||
message.error(err);
|
||||
Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
export const sendSpaceUploadPostRequest = (url: string, body?: any) => {
|
||||
return axios.post(url, body).then(res => res).catch(err => {
|
||||
message.error(err);
|
||||
Promise.reject(err);
|
||||
});
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
|
||||
export const postFetcher = <T>(uri: string, { arg }: { arg: T }) =>
|
||||
axios(uri, {
|
||||
method: 'POST',
|
||||
data: arg,
|
||||
}).then((r) => r.data);
|
||||
|
||||
export const createFetcher =
|
||||
(config: AxiosRequestConfig) =>
|
||||
<T>(url: string, { arg }: { arg: T }) =>
|
||||
axios({
|
||||
url,
|
||||
...config,
|
||||
data: arg,
|
||||
}).then((r) => r.data);
|
||||
|
||||
export const fetcher = (...args: Parameters<typeof axios>) =>
|
||||
axios(...args).then((r) => r.data);
|
BIN
pilot/xx.db
BIN
pilot/xx.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user