代码清理

This commit is contained in:
yhjun1026 2023-07-21 18:21:39 +08:00
parent ad4fd6c0fe
commit 3f733a302b
42 changed files with 0 additions and 18459 deletions

View File

@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

35
datacenter/.gitignore vendored
View File

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

View File

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

View File

@ -1,13 +0,0 @@
import { Box } from '@/lib/mui';
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<>
{children}
</>
)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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',
},
},
});

View File

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

View File

@ -1,10 +0,0 @@
import { useState } from 'react';
export const useNewChat = () => {
const [message, setMessage] = useState<string | undefined>("hello");
return {
message,
setMessage
};
}

View File

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

View File

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

View File

@ -1,3 +0,0 @@
"use client";
export * from '@mui/joy';

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

View File

@ -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: [],
}

View File

@ -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"]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.