Compare commits

..

49 Commits
v3.8.5 ... v3.9

Author SHA1 Message Date
archer
6787f19d78 feat: price 2023-06-23 18:05:53 +08:00
archer
64c35eaa3a docs 2023-06-23 17:43:14 +08:00
archer
41ada6ecda perf: keys 2023-06-23 17:12:52 +08:00
archer
ae1f7a888e perf: token count;feat: chunk size 2023-06-23 15:08:30 +08:00
archer
9aace871ff fix: ssr 2023-06-21 18:04:36 +08:00
moonrailgun
39739f9305 chore: fix admin build problem (#101) 2023-06-21 15:40:51 +08:00
archer
ce757d918b fix: ssr 2023-06-21 15:22:07 +08:00
archer
d592d4e99a markdown 2023-06-21 15:22:06 +08:00
moonrailgun
11ce10cd80 feat: add zh translation and change title (#100) 2023-06-21 15:21:24 +08:00
archer
6fb312ccfd link text 2023-06-20 10:41:17 +08:00
archer
3166376173 fix: template 2023-06-20 10:40:49 +08:00
archer
a02a528737 perf: my models 2023-06-19 21:08:32 +08:00
archer
dd4ca27dc7 perf: deploy 2023-06-19 20:00:54 +08:00
archer
f2d37c30a5 feat: baidu statistic 2023-06-19 17:28:25 +08:00
archer
1d236f87ae perf: markdown redraw 2023-06-19 16:50:14 +08:00
archer
3b515c3c2d fix: choices empty 2023-06-19 11:30:26 +08:00
archer
e95f83ec8e docs 2023-06-18 23:52:40 +08:00
archer
03793c66da README 2023-06-18 23:25:16 +08:00
archer
84daf85393 fix: base url 2023-06-18 22:38:55 +08:00
archer
6c62d80a4c fix: refresh page 2023-06-18 22:19:49 +08:00
archer
ff2043c0fb feat: maxToken setting 2023-06-18 21:23:36 +08:00
archer
ee9afa310a feat: openapi v2 chat 2023-06-18 21:06:07 +08:00
archer
2b93ae2d00 fix: time conf 2023-06-17 21:53:04 +08:00
archer
00c93a63cd perf: queue link 2023-06-17 21:27:44 +08:00
archer
61447c60ac feat: new app page 2023-06-17 17:31:38 +08:00
archer
df2fda6176 feat: auth key 2023-06-16 00:26:11 +08:00
archer
bc2504832f fix: nextjs version 2023-06-16 00:03:52 +08:00
archer
33ffd9d7dd loading 2023-06-15 22:36:09 +08:00
archer
80578a08c8 perf: app store 2023-06-15 22:17:54 +08:00
archer
2463e11cb9 feat: date picker 2023-06-15 21:44:31 +08:00
archer
4cbe4ebdc3 perf: image 2023-06-15 20:06:56 +08:00
archer
bb36e637e0 perf: code 2023-06-15 17:32:35 +08:00
archer
6f9e929298 perf: code 2023-06-15 17:32:12 +08:00
archer
bf1592d2c6 feat: admin set share 2023-06-15 00:21:27 +08:00
archer
c6259fca78 perf: export source 2023-06-14 23:14:26 +08:00
archer
cf3eb3b7b5 perf: upload img 2023-06-14 22:45:47 +08:00
archer
7c52cec0ea perf: binary avatar 2023-06-14 22:26:11 +08:00
archer
7c159d8aba fix: markdown 2023-06-14 20:58:11 +08:00
archer
07f8e18c10 fix: gpt35 4k 2023-06-14 20:54:34 +08:00
archer
e4aeee7be3 perf: token count 2023-06-14 20:02:43 +08:00
archer
8036ed6143 perf: qa 2023-06-14 14:33:26 +08:00
archer
85e6a0f38d fix: token limit 2023-06-14 10:01:00 +08:00
archer
dab70378bb feat: gpt35-16k 2023-06-14 09:45:49 +08:00
archer
0a0febd2e6 perf: admin 2023-06-14 00:24:50 +08:00
archer
391332c8dd perf: ssr 2023-06-13 20:07:32 +08:00
archer
89e7c1abca perf: admin 2023-06-13 11:49:26 +08:00
archer
fc3c360985 fix: context menu 2023-06-13 10:52:44 +08:00
archer
006ba3b877 fix: mermaid 2023-06-12 23:17:48 +08:00
archer
5a534aa630 perf: del loading 2023-06-12 22:12:29 +08:00
157 changed files with 6216 additions and 4369 deletions

View File

@@ -1,6 +1,6 @@
# Fast GPT
Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,目前集成了 Gpt35, Gpt4 和 embedding. 可构建自己的知识库。
Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,目前集成了 Gpt35, Gpt4 和 embedding. 可构建自己的知识库。并且 OpenAPI Chat 接口兼容 OpenAI 接口,意味着你只需修改 BaseUrl 和 Authorization 即可在已有项目基础上接入 FastGpt
## 🛸 在线体验
@@ -42,6 +42,12 @@ Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接
- [FastGpt V3.4 更新集合](https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4)
- [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
## Powered by
- [TuShan 5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan)
- [Laf 3 分钟快速接入三方应用](https://github.com/labring/laf)
- [Sealos 快速部署集群应用](https://github.com/labring/sealos)
## 🌟 Star History
[![Star History Chart](https://api.star-history.com/svg?repos=c121914yu/FastGPT&type=Date)](https://star-history.com/#c121914yu/FastGPT&Date)

2
admin/.gitignore vendored
View File

@@ -1 +1 @@
node_modules/
node_modules/

View File

@@ -2,13 +2,14 @@
## 项目原理
使用 tushan 项目做前端,然后构造了一个与 mongodb 做沟通的 API 做后端,可以做到创建、修改和删除用户
使用 [Tushan](https://tushan.msgbyte.com/) 项目做前端,然后构造了一个与 mongodb 做沟通的 API 做后端,可以做到创建、修改和删除用户
## 开发
1. 复制 .env.template 文件,添加环境变量
2. pnpm i
3. pnpm dev
1. `cp .env.template .env.local`: 复制 .env.template 文件,添加环境变量
2. `pnpm i`
3. `pnpm dev`
4. 打开 `http://localhost:5173/` 访问前端页面
## 部署
@@ -25,7 +26,8 @@ MONGODB_NAME=fastgpt
ADMIN_USER=username
ADMIN_PASS=password
ADMIN_SECRET=any
VITE_PUBLIC_SERVER_URL=http://localhost:3001 # 和server.js一致
PARENT_URL=http://localhost:3000
PARENT_ROOT_KEY=rootkey
```
## sealos 部署
@@ -33,7 +35,7 @@ VITE_PUBLIC_SERVER_URL=http://localhost:3001 # 和server.js一致
1. 进入 sealos 官网: https://cloud.sealos.io/
2. 打开 App Launchpad(应用管理) 工具
3. 新建应用
1. 镜像名: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-admin:latest
1. 镜像名: `registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-admin:latest`
2. 容器端口: 3001
3. 环境变量: 参考上面
4. 打开外网访问开关

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tushan</title>
<title>Fast GPT</title>
</head>
<body>
<div id="root"></div>

View File

@@ -25,7 +25,7 @@
"react-admin": "^4.11.0",
"react-dom": "^18.2.0",
"react-i18next": "^12.3.1",
"tushan": "^0.2.22"
"tushan": "^0.2.30"
},
"devDependencies": {
"@types/jsonexport": "^3.0.2",

3542
admin/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,15 @@ useAppRoute(app);
useKbRoute(app);
useSystemRoute(app);
app.get('/*', (req, res) => {
res.sendFile(new URL('dist/index.html', import.meta.url).pathname);
});
app.use((err, req, res, next) => {
res.sendFile(new URL('dist/index.html', import.meta.url).pathname);
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);

View File

@@ -1,4 +1,4 @@
import { User, Model, Kb } from '../schema.js';
import { Model, Kb } from '../schema.js';
import { auth } from './system.js';
export const useAppRoute = (app) => {
@@ -8,18 +8,19 @@ export const useAppRoute = (app) => {
const start = parseInt(req.query._start) || 0;
const end = parseInt(req.query._end) || 20;
const order = req.query._order === 'DESC' ? -1 : 1;
const sort = req.query._sort || '_id';
const userId = req.query.userId || '';
const sort = req.query._sort;
const name = req.query.name || '';
const id = req.query.id || '';
const where = {
...(userId ? { userId: userId } : {}),
name
...(name && { name: { $regex: name, $options: 'i' } }),
...(id && { _id: id })
};
const modelsRaw = await Model.find()
const modelsRaw = await Model.find(where)
.skip(start)
.limit(end - start)
.sort({ [sort]: order });
.sort({ [sort]: order, 'share.isShare': -1, 'share.collection': -1 });
const models = [];
@@ -37,15 +38,19 @@ export const useAppRoute = (app) => {
id: model._id.toString(),
userId: model.userId,
name: model.name,
intro: model.intro,
model: model.chat?.chatModel,
relatedKbs: kbNames, // 将relatedKbs的id转换为相应的Kb名称
searchMode: model.chat?.searchMode,
systemPrompt: model.chat?.systemPrompt || '',
temperature: model.chat?.temperature
temperature: model.chat?.temperature || 0,
'share.topNum': model.share?.topNum || 0,
'share.isShare': model.share?.isShare || false,
'share.collection': model.share?.collection || 0
};
models.push(orderedModel);
}
const totalCount = await Model.countDocuments();
const totalCount = await Model.countDocuments(where);
res.header('Access-Control-Expose-Headers', 'X-Total-Count');
res.header('X-Total-Count', totalCount);
res.json(models);
@@ -54,4 +59,29 @@ export const useAppRoute = (app) => {
res.status(500).json({ error: 'Error fetching models', details: err.message });
}
});
// 修改 app 信息
app.put('/models/:id', auth(), async (req, res) => {
try {
const _id = req.params.id;
let {
share: { isShare, topNum },
intro
} = req.body;
await Model.findByIdAndUpdate(_id, {
$set: {
intro: intro,
'share.topNum': Number(topNum),
'share.isShare': isShare === 'true' || isShare === true
}
});
res.json({});
} catch (err) {
console.log(`Error updating user: ${err}`);
res.status(500).json({ error: 'Error updating user' });
}
});
};

View File

@@ -10,7 +10,21 @@ export const useKbRoute = (app) => {
const order = req.query._order === 'DESC' ? -1 : 1;
const sort = req.query._sort || '_id';
const tag = req.query.tag || '';
const where = { tags: { $elemMatch: { $regex: tag, $options: 'i' } } };
const name = req.query.name || '';
const where = {
...(name
? {
name: { $regex: name, $options: 'i' }
}
: {}),
...(tag
? {
tags: { $elemMatch: { $regex: tag, $options: 'i' } }
}
: {})
};
console.log(where);
const kbsRaw = await Kb.find(where)
.skip(start)

View File

@@ -110,8 +110,7 @@ export const auth = () => {
try {
const authorization = req.headers.authorization;
if (!authorization) {
res.status(401).end('not found authorization in headers');
return;
return next(new Error("unAuthorization"))
}
const token = authorization.slice('Bearer '.length);

View File

@@ -9,6 +9,52 @@ const hashPassword = (psw) => {
};
export const useUserRoute = (app) => {
// 统计近 30 天注册用户数量
app.get('/users/data', auth(), async (req, res) => {
try {
const day = 60;
let startCount = await User.countDocuments({
createTime: { $lt: new Date(Date.now() - day * 24 * 60 * 60 * 1000) }
});
const usersRaw = await User.aggregate([
{ $match: { createTime: { $gte: new Date(Date.now() - day * 24 * 60 * 60 * 1000) } } },
{
$group: {
_id: {
year: { $year: '$createTime' },
month: { $month: '$createTime' },
day: { $dayOfMonth: '$createTime' }
},
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } },
count: 1
}
},
{ $sort: { date: 1 } }
]);
const countResult = usersRaw.map((item) => {
const increaseRate = `${((item.count / startCount) * 100).toFixed(2)}%`;
startCount += item.count;
return {
date: item.date,
count: startCount,
increase: item.count,
increaseRate
};
});
res.json(countResult);
} catch (err) {
console.log(`Error fetching users: ${err}`);
res.status(500).json({ error: 'Error fetching users' });
}
});
// 获取用户列表
app.get('/users', auth(), async (req, res) => {
try {

View File

@@ -61,14 +61,15 @@ const modelSchema = new mongoose.Schema({
name: String,
avatar: String,
status: String,
intro: String,
chat: {
relatedKbs: [mongoose.Schema.Types.ObjectId],
searchMode: String,
systemPrompt: String,
temperature: Number,
chatModel: String
},
share: {
topNum: Number,
isShare: Boolean,
isShareDetail: Boolean,
intro: String,
@@ -85,18 +86,6 @@ const modelSchema = new mongoose.Schema({
});
const SystemSchema = new mongoose.Schema({
openAIKeys: {
type: String,
default: ''
},
openAITrainingKeys: {
type: String,
default: ''
},
gpt4Key: {
type: String,
default: ''
},
vectorMaxProcess: {
type: Number,
default: 10

View File

@@ -4,15 +4,19 @@ import {
ListTable,
Resource,
Tushan,
fetchJSON
fetchJSON,
TushanContextProps,
HTTPClient
} from 'tushan';
import { authProvider } from './auth';
import { userFields, payFields, kbFields, ModelFields, SystemFields } from './fields';
import { Dashboard } from './Dashboard';
import { IconUser, IconApps, IconBook, IconStamp } from 'tushan/icon';
import { i18nZhTranslation } from 'tushan/client/i18n/resources/zh';
const authStorageKey = 'tushan:auth';
const httpClient: typeof fetchJSON = (url, options = {}) => {
const httpClient: HTTPClient = (url, options = {}) => {
try {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
@@ -28,11 +32,22 @@ const httpClient: typeof fetchJSON = (url, options = {}) => {
const dataProvider = jsonServerProvider(import.meta.env.VITE_PUBLIC_SERVER_URL, httpClient);
const i18n: TushanContextProps['i18n'] = {
languages: [
{
key: 'zh',
label: '简体中文',
translation: i18nZhTranslation
}
]
};
function App() {
return (
<Tushan
basename="/"
header={'FastGpt-Admin'}
i18n={i18n}
dataProvider={dataProvider}
authProvider={authProvider}
dashboard={<Dashboard />}
@@ -40,6 +55,7 @@ function App() {
<Resource
name="users"
label="用户信息"
icon={<IconUser />}
list={
<ListTable
filter={[
@@ -52,10 +68,29 @@ function App() {
/>
}
/>
<Resource
name="models"
icon={<IconApps />}
label="应用"
list={
<ListTable
filter={[
createTextField('id', {
label: 'id'
}),
createTextField('name', {
label: 'name'
})
]}
fields={ModelFields}
action={{ detail: true, edit: true }}
/>
}
/>
<Resource
name="pays"
label="支付记录"
icon={<IconStamp />}
list={
<ListTable
filter={[
@@ -71,9 +106,13 @@ function App() {
<Resource
name="kbs"
label="知识库"
icon={<IconBook />}
list={
<ListTable
filter={[
createTextField('name', {
label: 'name'
}),
createTextField('tag', {
label: 'tag'
})
@@ -83,11 +122,7 @@ function App() {
/>
}
/>
<Resource
name="models"
label="应用"
list={<ListTable fields={ModelFields} action={{ detail: true }} />}
/>
<Resource
name="system"
label="系统"

View File

@@ -2,22 +2,36 @@ import { Card, Link, Space, Grid, Divider, Typography } from '@arco-design/web-r
import { IconApps, IconUser, IconUserGroup } from 'tushan/icon';
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
AreaChart,
Area
} from 'tushan/chart';
import dayjs from 'dayjs';
const authStorageKey = 'tushan:auth';
type UsersChartDataType = { count: number; date: string; increase: number; increaseRate: string };
export const Dashboard: React.FC = React.memo(() => {
const [userCount, setUserCount] = useState(0); //用户数量
const [kbCount, setkbCount] = useState(0);
const [modelCount, setmodelCount] = useState(0);
useEffect(() => {
const fetchCounts = async () => {
const baseUrl = import.meta.env.VITE_PUBLIC_SERVER_URL;
const { token } = JSON.parse(window.localStorage.getItem(authStorageKey) ?? '{}');
const [usersData, setUsersData] = useState<UsersChartDataType[]>([]);
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
};
useEffect(() => {
const baseUrl = import.meta.env.VITE_PUBLIC_SERVER_URL;
const { token } = JSON.parse(window.localStorage.getItem(authStorageKey) ?? '{}');
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
};
const fetchCounts = async () => {
const userResponse = await fetch(`${baseUrl}/users?_end=1`, {
headers
});
@@ -31,7 +45,6 @@ export const Dashboard: React.FC = React.memo(() => {
const userTotalCount = userResponse.headers.get('X-Total-Count');
const kbTotalCount = kbResponse.headers.get('X-Total-Count');
const modelTotalCount = modelResponse.headers.get('X-Total-Count');
console.log(userTotalCount);
if (userTotalCount) {
setUserCount(Number(userTotalCount));
@@ -43,8 +56,20 @@ export const Dashboard: React.FC = React.memo(() => {
setmodelCount(Number(modelTotalCount));
}
};
const fetchUserData = async () => {
const userResponse: UsersChartDataType[] = await fetch(`${baseUrl}/users/data`, {
headers
}).then((res) => res.json());
setUsersData(
userResponse.map((item) => ({
...item,
date: dayjs(item.date).format('MM/DD')
}))
);
};
fetchCounts();
fetchUserData();
}, []);
return (
@@ -71,11 +96,12 @@ export const Dashboard: React.FC = React.memo(() => {
<Divider type="vertical" style={{ height: 40 }} />
<Grid.Col flex={1} style={{ paddingLeft: '1rem' }}>
<DataItem icon={<IconApps />} title={'AI模型'} count={modelCount} />
<DataItem icon={<IconApps />} title={'应用'} count={modelCount} />
</Grid.Col>
</Grid.Row>
<Divider />
<UserChart data={usersData} />
</Card>
</Space>
</div>
@@ -84,38 +110,31 @@ export const Dashboard: React.FC = React.memo(() => {
});
Dashboard.displayName = 'Dashboard';
const DashboardItem: React.FC<
React.PropsWithChildren<{
title: string;
href?: string;
}>
> = React.memo((props) => {
const { t } = useTranslation();
const DashboardItem = React.memo(
(props: { title: string; href?: string; children: React.ReactNode }) => {
const { t } = useTranslation();
return (
<Card
title={props.title}
extra={
props.href && (
<Link target="_blank" href={props.href}>
{t('tushan.dashboard.more')}
</Link>
)
}
bordered={false}
style={{ overflow: 'hidden' }}
>
{props.children}
</Card>
);
});
return (
<Card
title={props.title}
extra={
props.href && (
<Link target="_blank" href={props.href}>
{t('tushan.dashboard.more')}
</Link>
)
}
bordered={false}
style={{ overflow: 'hidden' }}
>
{props.children}
</Card>
);
}
);
DashboardItem.displayName = 'DashboardItem';
const DataItem: React.FC<{
icon: React.ReactElement;
title: string;
count: number;
}> = React.memo((props) => {
const DataItem = React.memo((props: { icon: React.ReactElement; title: string; count: number }) => {
return (
<Space>
<div
@@ -141,3 +160,65 @@ const DataItem: React.FC<{
);
});
DataItem.displayName = 'DataItem';
const CustomTooltip = ({ active, payload }: any) => {
const data = payload?.[0]?.payload as UsersChartDataType;
if (active && data) {
return (
<div
style={{
background: 'white',
padding: '5px 8px',
borderRadius: '8px',
boxShadow: '2px 2px 5px rgba(0,0,0,0.2)'
}}
>
<p className="label">
count: <strong>{data.count}</strong>
</p>
<p className="label">
increase: <strong>{data.increase}</strong>
</p>
<p className="label">
increaseRate: <strong>{data.increaseRate}</strong>
</p>
</div>
);
}
return null;
};
const UserChart = ({ data }: { data: UsersChartDataType[] }) => {
return (
<ResponsiveContainer width="100%" height={320}>
<AreaChart
width={730}
height={250}
data={data}
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#8884d8" stopOpacity={0.8} />
<stop offset="95%" stopColor="#8884d8" stopOpacity={0} />
</linearGradient>
<linearGradient id="colorPv" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#82ca9d" stopOpacity={0.8} />
<stop offset="95%" stopColor="#82ca9d" stopOpacity={0} />
</linearGradient>
</defs>
<XAxis dataKey="date" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip content={<CustomTooltip />} />
<Area
type="monotone"
dataKey="count"
stroke="#82ca9d"
fillOpacity={1}
fill="url(#colorPv)"
/>
</AreaChart>
</ResponsiveContainer>
);
};

View File

@@ -2,7 +2,7 @@ import { createTextField, createNumberField } from 'tushan';
export const userFields = [
createTextField('id', { label: 'ID' }),
createTextField('username', { label: '用户名' }),
createTextField('username', { label: '用户名', edit: { hidden: true } }),
createNumberField('balance', { label: '余额', list: { sort: true } }),
createTextField('createTime', { label: 'Create Time', list: { sort: true } }),
createTextField('password', { label: '密码', list: { hidden: true } })
@@ -19,30 +19,32 @@ export const payFields = [
export const kbFields = [
createTextField('id', { label: 'ID' }),
createTextField('userId', { label: '所属用户' }),
createTextField('userId', { label: '所属用户', edit: { hidden: true } }),
createTextField('name', { label: '知识库' }),
createTextField('tags', { label: 'Tags' })
];
export const ModelFields = [
createTextField('id', { label: 'ID' }),
createTextField('userId', { label: '所属用户' }),
createTextField('userId', { label: '所属用户', list: { hidden: true }, edit: { hidden: true } }),
createTextField('name', { label: '名字' }),
createTextField('relatedKbs', { label: '引用的知识库' }),
createTextField('searchMode', { label: '搜索模式' }),
createTextField('model', { label: '模型', edit: { hidden: true } }),
createTextField('share.collection', { label: '收藏数', list: { sort: true } }),
createTextField('share.topNum', { label: '置顶等级', list: { sort: true } }),
createTextField('share.isShare', { label: '是否分享(true,false)' }),
createTextField('intro', { label: '介绍', list: { width: 400 } }),
createTextField('relatedKbs', { label: '引用的知识库', list: { hidden: true } }),
createTextField('temperature', { label: '温度' }),
createTextField('systemPrompt', {
label: '提示词',
list: {
width: 400
width: 400,
hidden: true
}
}),
createTextField('temperature', { label: '温度' })
})
];
export const SystemFields = [
createTextField('openAIKeys', { label: 'openAIKeys逗号隔开' }),
createTextField('openAITrainingKeys', { label: 'openAITrainingKeys' }),
createTextField('gpt4Key', { label: 'gpt4Key' }),
createTextField('vectorMaxProcess', { label: '向量最大进程' }),
createTextField('qaMaxProcess', { label: 'qa最大进程' }),
createTextField('pgIvfflatProbe', { label: 'pg 探针数量' }),

View File

@@ -1,5 +1,7 @@
# 运行端口,如果不是 3000 口运行,需要改成其他的。注意:不是改了这个变量就会变成其他端口,而是因为改成其他端口,才用这个变量。
PORT=3000
# database max link
DB_MAX_LINK=15
# 代理
# AXIOS_PROXY_HOST=127.0.0.1
# AXIOS_PROXY_PORT=7890
@@ -19,8 +21,6 @@ ROOT_KEY=fdafasd
# OPENAI_BASE_URL=http://ai.openai.com/v1
# OPENAI_BASE_URL_AUTH=可选安全凭证,会放到 header.auth 里
OPENAIKEY=sk-xxx
OPENAI_TRAINING_KEY=sk-xxx
GPT4KEY=sk-xxx
# db
MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
MONGODB_NAME=fastgpt

View File

@@ -15,7 +15,7 @@
"@chakra-ui/icons": "^2.0.17",
"@chakra-ui/react": "^2.7.0",
"@chakra-ui/system": "^2.5.8",
"@dqbd/tiktoken": "^1.0.6",
"@dqbd/tiktoken": "^1.0.7",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@next/font": "13.1.6",
@@ -24,30 +24,31 @@
"axios": "^1.3.3",
"cookie": "^0.5.0",
"crypto": "^1.0.1",
"date-fns": "^2.30.0",
"dayjs": "^1.11.7",
"eventsource-parser": "^0.1.0",
"formidable": "^2.1.1",
"framer-motion": "^9.0.6",
"graphemer": "^1.4.0",
"hyperdown": "^2.4.29",
"immer": "^9.0.19",
"jsonwebtoken": "^9.0.0",
"lodash": "^4.17.21",
"mammoth": "^1.5.1",
"mermaid": "^8.13.5",
"mermaid": "^10.2.3",
"mongoose": "^6.10.0",
"nanoid": "^4.0.1",
"next": "13.1.6",
"nextjs-cors": "^2.1.2",
"nodemailer": "^6.9.1",
"nprogress": "^0.2.0",
"openai": "^3.2.1",
"openai": "^3.3.0",
"papaparse": "^5.4.1",
"pg": "^8.10.0",
"react": "18.2.0",
"react-day-picker": "^8.7.1",
"react-dom": "18.2.0",
"react-hook-form": "^7.43.1",
"react-markdown": "^8.0.5",
"react-markdown": "^8.0.7",
"react-syntax-highlighter": "^15.5.0",
"rehype-katex": "^6.0.2",
"remark-breaks": "^3.0.3",

550
client/pnpm-lock.yaml generated
View File

@@ -24,8 +24,8 @@ dependencies:
specifier: ^2.5.8
version: registry.npmmirror.com/@chakra-ui/system@2.5.8(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(react@18.2.0)
'@dqbd/tiktoken':
specifier: ^1.0.6
version: registry.npmmirror.com/@dqbd/tiktoken@1.0.6
specifier: ^1.0.7
version: registry.npmmirror.com/@dqbd/tiktoken@1.0.7
'@emotion/react':
specifier: ^11.10.6
version: registry.npmmirror.com/@emotion/react@11.10.6(@types/react@18.0.28)(react@18.2.0)
@@ -50,6 +50,9 @@ dependencies:
crypto:
specifier: ^1.0.1
version: registry.npmmirror.com/crypto@1.0.1
date-fns:
specifier: ^2.30.0
version: registry.npmmirror.com/date-fns@2.30.0
dayjs:
specifier: ^1.11.7
version: registry.npmmirror.com/dayjs@1.11.7
@@ -62,9 +65,6 @@ dependencies:
framer-motion:
specifier: ^9.0.6
version: registry.npmmirror.com/framer-motion@9.0.6(react-dom@18.2.0)(react@18.2.0)
graphemer:
specifier: ^1.4.0
version: registry.npmmirror.com/graphemer@1.4.0
hyperdown:
specifier: ^2.4.29
version: registry.npmmirror.com/hyperdown@2.4.29
@@ -81,8 +81,8 @@ dependencies:
specifier: ^1.5.1
version: registry.npmmirror.com/mammoth@1.5.1
mermaid:
specifier: ^8.13.5
version: registry.npmmirror.com/mermaid@8.13.5
specifier: ^10.2.3
version: registry.npmmirror.com/mermaid@10.2.3
mongoose:
specifier: ^6.10.0
version: registry.npmmirror.com/mongoose@6.10.0
@@ -102,8 +102,8 @@ dependencies:
specifier: ^0.2.0
version: registry.npmmirror.com/nprogress@0.2.0
openai:
specifier: ^3.2.1
version: registry.npmmirror.com/openai@3.2.1
specifier: ^3.3.0
version: registry.npmmirror.com/openai@3.3.0
papaparse:
specifier: ^5.4.1
version: registry.npmmirror.com/papaparse@5.4.1
@@ -113,6 +113,9 @@ dependencies:
react:
specifier: 18.2.0
version: registry.npmmirror.com/react@18.2.0
react-day-picker:
specifier: ^8.7.1
version: registry.npmmirror.com/react-day-picker@8.7.1(date-fns@2.30.0)(react@18.2.0)
react-dom:
specifier: 18.2.0
version: registry.npmmirror.com/react-dom@18.2.0(react@18.2.0)
@@ -120,8 +123,8 @@ dependencies:
specifier: ^7.43.1
version: registry.npmmirror.com/react-hook-form@7.43.1(react@18.2.0)
react-markdown:
specifier: ^8.0.5
version: registry.npmmirror.com/react-markdown@8.0.5(@types/react@18.0.28)(react@18.2.0)
specifier: ^8.0.7
version: registry.npmmirror.com/react-markdown@8.0.7(@types/react@18.0.28)(react@18.2.0)
react-syntax-highlighter:
specifier: ^15.5.0
version: registry.npmmirror.com/react-syntax-highlighter@15.5.0(react@18.2.0)
@@ -2920,11 +2923,10 @@ packages:
'@babel/helper-validator-identifier': registry.npmmirror.com/@babel/helper-validator-identifier@7.22.5
to-fast-properties: registry.npmmirror.com/to-fast-properties@2.0.0
registry.npmmirror.com/@braintree/sanitize-url@3.1.0:
resolution: {integrity: sha512-GcIY79elgB+azP74j8vqkiXz8xLFfIzbQJdlwOPisgbKT00tviJQuEghOXSMVxJ00HoYJbGswr4kcllUc4xCcg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-3.1.0.tgz}
registry.npmmirror.com/@braintree/sanitize-url@6.0.2:
resolution: {integrity: sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz}
name: '@braintree/sanitize-url'
version: 3.1.0
deprecated: Potential XSS vulnerability patched in v6.0.0.
version: 6.0.2
dev: false
registry.npmmirror.com/@chakra-ui/accordion@2.2.0(@chakra-ui/system@2.5.8)(framer-motion@9.0.6)(react@18.2.0):
@@ -4264,10 +4266,10 @@ packages:
react: registry.npmmirror.com/react@18.2.0
dev: false
registry.npmmirror.com/@dqbd/tiktoken@1.0.6:
resolution: {integrity: sha512-umSdeZTy/SbPPKVuZKV/XKyFPmXSN145CcM3iHjBbmhlohBJg7vaDp4cPCW+xNlWL6L2U1sp7T2BD+di2sUKdA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@dqbd/tiktoken/-/tiktoken-1.0.6.tgz}
registry.npmmirror.com/@dqbd/tiktoken@1.0.7:
resolution: {integrity: sha512-bhR5k5W+8GLzysjk8zTMVygQZsgvf7W1F0IlL4ZQ5ugjo5rCyiwGM5d8DYriXspytfu98tv59niang3/T+FoDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@dqbd/tiktoken/-/tiktoken-1.0.7.tgz}
name: '@dqbd/tiktoken'
version: 1.0.6
version: 1.0.7
dev: false
registry.npmmirror.com/@emotion/babel-plugin@11.11.0:
@@ -6106,12 +6108,6 @@ packages:
version: 2.0.3
dev: false
registry.npmmirror.com/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz}
name: commander
version: 2.20.3
dev: false
registry.npmmirror.com/commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz}
name: commander
@@ -6194,6 +6190,22 @@ packages:
vary: registry.npmmirror.com/vary@1.1.2
dev: false
registry.npmmirror.com/cose-base@1.0.3:
resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cose-base/-/cose-base-1.0.3.tgz}
name: cose-base
version: 1.0.3
dependencies:
layout-base: registry.npmmirror.com/layout-base@1.0.2
dev: false
registry.npmmirror.com/cose-base@2.2.0:
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cose-base/-/cose-base-2.2.0.tgz}
name: cose-base
version: 2.2.0
dependencies:
layout-base: registry.npmmirror.com/layout-base@2.0.1
dev: false
registry.npmmirror.com/cosmiconfig@7.1.0:
resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz}
name: cosmiconfig
@@ -6275,10 +6287,38 @@ packages:
name: csstype
version: 3.1.2
registry.npmmirror.com/d3-array@1.2.4:
resolution: {integrity: sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-array/-/d3-array-1.2.4.tgz}
name: d3-array
version: 1.2.4
registry.npmmirror.com/cytoscape-cose-bilkent@4.1.0(cytoscape@3.25.0):
resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz}
id: registry.npmmirror.com/cytoscape-cose-bilkent/4.1.0
name: cytoscape-cose-bilkent
version: 4.1.0
peerDependencies:
cytoscape: ^3.2.0
dependencies:
cose-base: registry.npmmirror.com/cose-base@1.0.3
cytoscape: registry.npmmirror.com/cytoscape@3.25.0
dev: false
registry.npmmirror.com/cytoscape-fcose@2.2.0(cytoscape@3.25.0):
resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz}
id: registry.npmmirror.com/cytoscape-fcose/2.2.0
name: cytoscape-fcose
version: 2.2.0
peerDependencies:
cytoscape: ^3.2.0
dependencies:
cose-base: registry.npmmirror.com/cose-base@2.2.0
cytoscape: registry.npmmirror.com/cytoscape@3.25.0
dev: false
registry.npmmirror.com/cytoscape@3.25.0:
resolution: {integrity: sha512-7MW3Iz57mCUo6JQCho6CmPBCbTlJr7LzyEtIkutG255HLVd4XuBg2I9BkTZLI/e4HoaOB/BiAzXuQybQ95+r9Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cytoscape/-/cytoscape-3.25.0.tgz}
name: cytoscape
version: 3.25.0
engines: {node: '>=0.10'}
dependencies:
heap: registry.npmmirror.com/heap@0.2.7
lodash: registry.npmmirror.com/lodash@4.17.21
dev: false
registry.npmmirror.com/d3-array@3.2.4:
@@ -6290,12 +6330,6 @@ packages:
internmap: registry.npmmirror.com/internmap@2.0.3
dev: false
registry.npmmirror.com/d3-axis@1.0.12:
resolution: {integrity: sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-axis/-/d3-axis-1.0.12.tgz}
name: d3-axis
version: 1.0.12
dev: false
registry.npmmirror.com/d3-axis@3.0.0:
resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-axis/-/d3-axis-3.0.0.tgz}
name: d3-axis
@@ -6303,18 +6337,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-brush@1.1.6:
resolution: {integrity: sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-brush/-/d3-brush-1.1.6.tgz}
name: d3-brush
version: 1.1.6
dependencies:
d3-dispatch: registry.npmmirror.com/d3-dispatch@1.0.6
d3-drag: registry.npmmirror.com/d3-drag@1.2.5
d3-interpolate: registry.npmmirror.com/d3-interpolate@1.4.0
d3-selection: registry.npmmirror.com/d3-selection@1.4.2
d3-transition: registry.npmmirror.com/d3-transition@1.3.2
dev: false
registry.npmmirror.com/d3-brush@3.0.0:
resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-brush/-/d3-brush-3.0.0.tgz}
name: d3-brush
@@ -6328,15 +6350,6 @@ packages:
d3-transition: registry.npmmirror.com/d3-transition@3.0.1(d3-selection@3.0.0)
dev: false
registry.npmmirror.com/d3-chord@1.0.6:
resolution: {integrity: sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-chord/-/d3-chord-1.0.6.tgz}
name: d3-chord
version: 1.0.6
dependencies:
d3-array: registry.npmmirror.com/d3-array@1.2.4
d3-path: registry.npmmirror.com/d3-path@1.0.9
dev: false
registry.npmmirror.com/d3-chord@3.0.1:
resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-chord/-/d3-chord-3.0.1.tgz}
name: d3-chord
@@ -6346,18 +6359,6 @@ packages:
d3-path: registry.npmmirror.com/d3-path@3.1.0
dev: false
registry.npmmirror.com/d3-collection@1.0.7:
resolution: {integrity: sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-collection/-/d3-collection-1.0.7.tgz}
name: d3-collection
version: 1.0.7
dev: false
registry.npmmirror.com/d3-color@1.4.1:
resolution: {integrity: sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-color/-/d3-color-1.4.1.tgz}
name: d3-color
version: 1.4.1
dev: false
registry.npmmirror.com/d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz}
name: d3-color
@@ -6365,14 +6366,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-contour@1.3.2:
resolution: {integrity: sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-contour/-/d3-contour-1.3.2.tgz}
name: d3-contour
version: 1.3.2
dependencies:
d3-array: registry.npmmirror.com/d3-array@1.2.4
dev: false
registry.npmmirror.com/d3-contour@4.0.2:
resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-contour/-/d3-contour-4.0.2.tgz}
name: d3-contour
@@ -6391,12 +6384,6 @@ packages:
delaunator: registry.npmmirror.com/delaunator@5.0.0
dev: false
registry.npmmirror.com/d3-dispatch@1.0.6:
resolution: {integrity: sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz}
name: d3-dispatch
version: 1.0.6
dev: false
registry.npmmirror.com/d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz}
name: d3-dispatch
@@ -6404,15 +6391,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-drag@1.2.5:
resolution: {integrity: sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-drag/-/d3-drag-1.2.5.tgz}
name: d3-drag
version: 1.2.5
dependencies:
d3-dispatch: registry.npmmirror.com/d3-dispatch@1.0.6
d3-selection: registry.npmmirror.com/d3-selection@1.4.2
dev: false
registry.npmmirror.com/d3-drag@3.0.0:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz}
name: d3-drag
@@ -6423,17 +6401,6 @@ packages:
d3-selection: registry.npmmirror.com/d3-selection@3.0.0
dev: false
registry.npmmirror.com/d3-dsv@1.2.0:
resolution: {integrity: sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-dsv/-/d3-dsv-1.2.0.tgz}
name: d3-dsv
version: 1.2.0
hasBin: true
dependencies:
commander: registry.npmmirror.com/commander@2.20.3
iconv-lite: registry.npmmirror.com/iconv-lite@0.4.24
rw: registry.npmmirror.com/rw@1.3.3
dev: false
registry.npmmirror.com/d3-dsv@3.0.1:
resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-dsv/-/d3-dsv-3.0.1.tgz}
name: d3-dsv
@@ -6446,12 +6413,6 @@ packages:
rw: registry.npmmirror.com/rw@1.3.3
dev: false
registry.npmmirror.com/d3-ease@1.0.7:
resolution: {integrity: sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-ease/-/d3-ease-1.0.7.tgz}
name: d3-ease
version: 1.0.7
dev: false
registry.npmmirror.com/d3-ease@3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz}
name: d3-ease
@@ -6459,14 +6420,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-fetch@1.2.0:
resolution: {integrity: sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-fetch/-/d3-fetch-1.2.0.tgz}
name: d3-fetch
version: 1.2.0
dependencies:
d3-dsv: registry.npmmirror.com/d3-dsv@1.2.0
dev: false
registry.npmmirror.com/d3-fetch@3.0.1:
resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-fetch/-/d3-fetch-3.0.1.tgz}
name: d3-fetch
@@ -6476,17 +6429,6 @@ packages:
d3-dsv: registry.npmmirror.com/d3-dsv@3.0.1
dev: false
registry.npmmirror.com/d3-force@1.2.1:
resolution: {integrity: sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-force/-/d3-force-1.2.1.tgz}
name: d3-force
version: 1.2.1
dependencies:
d3-collection: registry.npmmirror.com/d3-collection@1.0.7
d3-dispatch: registry.npmmirror.com/d3-dispatch@1.0.6
d3-quadtree: registry.npmmirror.com/d3-quadtree@1.0.7
d3-timer: registry.npmmirror.com/d3-timer@1.0.10
dev: false
registry.npmmirror.com/d3-force@3.0.0:
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz}
name: d3-force
@@ -6498,12 +6440,6 @@ packages:
d3-timer: registry.npmmirror.com/d3-timer@3.0.1
dev: false
registry.npmmirror.com/d3-format@1.4.5:
resolution: {integrity: sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-format/-/d3-format-1.4.5.tgz}
name: d3-format
version: 1.4.5
dev: false
registry.npmmirror.com/d3-format@3.1.0:
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz}
name: d3-format
@@ -6511,14 +6447,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-geo@1.12.1:
resolution: {integrity: sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-geo/-/d3-geo-1.12.1.tgz}
name: d3-geo
version: 1.12.1
dependencies:
d3-array: registry.npmmirror.com/d3-array@1.2.4
dev: false
registry.npmmirror.com/d3-geo@3.1.0:
resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-geo/-/d3-geo-3.1.0.tgz}
name: d3-geo
@@ -6528,12 +6456,6 @@ packages:
d3-array: registry.npmmirror.com/d3-array@3.2.4
dev: false
registry.npmmirror.com/d3-hierarchy@1.1.9:
resolution: {integrity: sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz}
name: d3-hierarchy
version: 1.1.9
dev: false
registry.npmmirror.com/d3-hierarchy@3.1.2:
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz}
name: d3-hierarchy
@@ -6541,14 +6463,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-interpolate@1.4.0:
resolution: {integrity: sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz}
name: d3-interpolate
version: 1.4.0
dependencies:
d3-color: registry.npmmirror.com/d3-color@1.4.1
dev: false
registry.npmmirror.com/d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz}
name: d3-interpolate
@@ -6558,12 +6472,6 @@ packages:
d3-color: registry.npmmirror.com/d3-color@3.1.0
dev: false
registry.npmmirror.com/d3-path@1.0.9:
resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-path/-/d3-path-1.0.9.tgz}
name: d3-path
version: 1.0.9
dev: false
registry.npmmirror.com/d3-path@3.1.0:
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz}
name: d3-path
@@ -6571,12 +6479,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-polygon@1.0.6:
resolution: {integrity: sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-polygon/-/d3-polygon-1.0.6.tgz}
name: d3-polygon
version: 1.0.6
dev: false
registry.npmmirror.com/d3-polygon@3.0.1:
resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-polygon/-/d3-polygon-3.0.1.tgz}
name: d3-polygon
@@ -6584,12 +6486,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-quadtree@1.0.7:
resolution: {integrity: sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz}
name: d3-quadtree
version: 1.0.7
dev: false
registry.npmmirror.com/d3-quadtree@3.0.1:
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz}
name: d3-quadtree
@@ -6597,12 +6493,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-random@1.1.2:
resolution: {integrity: sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-random/-/d3-random-1.1.2.tgz}
name: d3-random
version: 1.1.2
dev: false
registry.npmmirror.com/d3-random@3.0.1:
resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz}
name: d3-random
@@ -6610,15 +6500,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-scale-chromatic@1.5.0:
resolution: {integrity: sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz}
name: d3-scale-chromatic
version: 1.5.0
dependencies:
d3-color: registry.npmmirror.com/d3-color@1.4.1
d3-interpolate: registry.npmmirror.com/d3-interpolate@1.4.0
dev: false
registry.npmmirror.com/d3-scale-chromatic@3.0.0:
resolution: {integrity: sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz}
name: d3-scale-chromatic
@@ -6629,19 +6510,6 @@ packages:
d3-interpolate: registry.npmmirror.com/d3-interpolate@3.0.1
dev: false
registry.npmmirror.com/d3-scale@2.2.2:
resolution: {integrity: sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-scale/-/d3-scale-2.2.2.tgz}
name: d3-scale
version: 2.2.2
dependencies:
d3-array: registry.npmmirror.com/d3-array@1.2.4
d3-collection: registry.npmmirror.com/d3-collection@1.0.7
d3-format: registry.npmmirror.com/d3-format@1.4.5
d3-interpolate: registry.npmmirror.com/d3-interpolate@1.4.0
d3-time: registry.npmmirror.com/d3-time@1.1.0
d3-time-format: registry.npmmirror.com/d3-time-format@2.3.0
dev: false
registry.npmmirror.com/d3-scale@4.0.2:
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz}
name: d3-scale
@@ -6655,12 +6523,6 @@ packages:
d3-time-format: registry.npmmirror.com/d3-time-format@4.1.0
dev: false
registry.npmmirror.com/d3-selection@1.4.2:
resolution: {integrity: sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-selection/-/d3-selection-1.4.2.tgz}
name: d3-selection
version: 1.4.2
dev: false
registry.npmmirror.com/d3-selection@3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz}
name: d3-selection
@@ -6668,14 +6530,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-shape@1.3.7:
resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-shape/-/d3-shape-1.3.7.tgz}
name: d3-shape
version: 1.3.7
dependencies:
d3-path: registry.npmmirror.com/d3-path@1.0.9
dev: false
registry.npmmirror.com/d3-shape@3.2.0:
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz}
name: d3-shape
@@ -6685,14 +6539,6 @@ packages:
d3-path: registry.npmmirror.com/d3-path@3.1.0
dev: false
registry.npmmirror.com/d3-time-format@2.3.0:
resolution: {integrity: sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-time-format/-/d3-time-format-2.3.0.tgz}
name: d3-time-format
version: 2.3.0
dependencies:
d3-time: registry.npmmirror.com/d3-time@1.1.0
dev: false
registry.npmmirror.com/d3-time-format@4.1.0:
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz}
name: d3-time-format
@@ -6702,12 +6548,6 @@ packages:
d3-time: registry.npmmirror.com/d3-time@3.1.0
dev: false
registry.npmmirror.com/d3-time@1.1.0:
resolution: {integrity: sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-time/-/d3-time-1.1.0.tgz}
name: d3-time
version: 1.1.0
dev: false
registry.npmmirror.com/d3-time@3.1.0:
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz}
name: d3-time
@@ -6717,12 +6557,6 @@ packages:
d3-array: registry.npmmirror.com/d3-array@3.2.4
dev: false
registry.npmmirror.com/d3-timer@1.0.10:
resolution: {integrity: sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-timer/-/d3-timer-1.0.10.tgz}
name: d3-timer
version: 1.0.10
dev: false
registry.npmmirror.com/d3-timer@3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz}
name: d3-timer
@@ -6730,19 +6564,6 @@ packages:
engines: {node: '>=12'}
dev: false
registry.npmmirror.com/d3-transition@1.3.2:
resolution: {integrity: sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-transition/-/d3-transition-1.3.2.tgz}
name: d3-transition
version: 1.3.2
dependencies:
d3-color: registry.npmmirror.com/d3-color@1.4.1
d3-dispatch: registry.npmmirror.com/d3-dispatch@1.0.6
d3-ease: registry.npmmirror.com/d3-ease@1.0.7
d3-interpolate: registry.npmmirror.com/d3-interpolate@1.4.0
d3-selection: registry.npmmirror.com/d3-selection@1.4.2
d3-timer: registry.npmmirror.com/d3-timer@1.0.10
dev: false
registry.npmmirror.com/d3-transition@3.0.1(d3-selection@3.0.0):
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz}
id: registry.npmmirror.com/d3-transition/3.0.1
@@ -6760,24 +6581,6 @@ packages:
d3-timer: registry.npmmirror.com/d3-timer@3.0.1
dev: false
registry.npmmirror.com/d3-voronoi@1.1.4:
resolution: {integrity: sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz}
name: d3-voronoi
version: 1.1.4
dev: false
registry.npmmirror.com/d3-zoom@1.8.3:
resolution: {integrity: sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-zoom/-/d3-zoom-1.8.3.tgz}
name: d3-zoom
version: 1.8.3
dependencies:
d3-dispatch: registry.npmmirror.com/d3-dispatch@1.0.6
d3-drag: registry.npmmirror.com/d3-drag@1.2.5
d3-interpolate: registry.npmmirror.com/d3-interpolate@1.4.0
d3-selection: registry.npmmirror.com/d3-selection@1.4.2
d3-transition: registry.npmmirror.com/d3-transition@1.3.2
dev: false
registry.npmmirror.com/d3-zoom@3.0.0:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz}
name: d3-zoom
@@ -6791,44 +6594,6 @@ packages:
d3-transition: registry.npmmirror.com/d3-transition@3.0.1(d3-selection@3.0.0)
dev: false
registry.npmmirror.com/d3@5.16.0:
resolution: {integrity: sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3/-/d3-5.16.0.tgz}
name: d3
version: 5.16.0
dependencies:
d3-array: registry.npmmirror.com/d3-array@1.2.4
d3-axis: registry.npmmirror.com/d3-axis@1.0.12
d3-brush: registry.npmmirror.com/d3-brush@1.1.6
d3-chord: registry.npmmirror.com/d3-chord@1.0.6
d3-collection: registry.npmmirror.com/d3-collection@1.0.7
d3-color: registry.npmmirror.com/d3-color@1.4.1
d3-contour: registry.npmmirror.com/d3-contour@1.3.2
d3-dispatch: registry.npmmirror.com/d3-dispatch@1.0.6
d3-drag: registry.npmmirror.com/d3-drag@1.2.5
d3-dsv: registry.npmmirror.com/d3-dsv@1.2.0
d3-ease: registry.npmmirror.com/d3-ease@1.0.7
d3-fetch: registry.npmmirror.com/d3-fetch@1.2.0
d3-force: registry.npmmirror.com/d3-force@1.2.1
d3-format: registry.npmmirror.com/d3-format@1.4.5
d3-geo: registry.npmmirror.com/d3-geo@1.12.1
d3-hierarchy: registry.npmmirror.com/d3-hierarchy@1.1.9
d3-interpolate: registry.npmmirror.com/d3-interpolate@1.4.0
d3-path: registry.npmmirror.com/d3-path@1.0.9
d3-polygon: registry.npmmirror.com/d3-polygon@1.0.6
d3-quadtree: registry.npmmirror.com/d3-quadtree@1.0.7
d3-random: registry.npmmirror.com/d3-random@1.1.2
d3-scale: registry.npmmirror.com/d3-scale@2.2.2
d3-scale-chromatic: registry.npmmirror.com/d3-scale-chromatic@1.5.0
d3-selection: registry.npmmirror.com/d3-selection@1.4.2
d3-shape: registry.npmmirror.com/d3-shape@1.3.7
d3-time: registry.npmmirror.com/d3-time@1.1.0
d3-time-format: registry.npmmirror.com/d3-time-format@2.3.0
d3-timer: registry.npmmirror.com/d3-timer@1.0.10
d3-transition: registry.npmmirror.com/d3-transition@1.3.2
d3-voronoi: registry.npmmirror.com/d3-voronoi@1.1.4
d3-zoom: registry.npmmirror.com/d3-zoom@1.8.3
dev: false
registry.npmmirror.com/d3@7.8.5:
resolution: {integrity: sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/d3/-/d3-7.8.5.tgz}
name: d3
@@ -6867,24 +6632,13 @@ packages:
d3-zoom: registry.npmmirror.com/d3-zoom@3.0.0
dev: false
registry.npmmirror.com/dagre-d3@0.6.4:
resolution: {integrity: sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dagre-d3/-/dagre-d3-0.6.4.tgz}
name: dagre-d3
version: 0.6.4
registry.npmmirror.com/dagre-d3-es@7.0.10:
resolution: {integrity: sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz}
name: dagre-d3-es
version: 7.0.10
dependencies:
d3: registry.npmmirror.com/d3@5.16.0
dagre: registry.npmmirror.com/dagre@0.8.5
graphlib: registry.npmmirror.com/graphlib@2.1.8
lodash: registry.npmmirror.com/lodash@4.17.21
dev: false
registry.npmmirror.com/dagre@0.8.5:
resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dagre/-/dagre-0.8.5.tgz}
name: dagre
version: 0.8.5
dependencies:
graphlib: registry.npmmirror.com/graphlib@2.1.8
lodash: registry.npmmirror.com/lodash@4.17.21
d3: registry.npmmirror.com/d3@7.8.5
lodash-es: registry.npmmirror.com/lodash-es@4.17.21
dev: false
registry.npmmirror.com/damerau-levenshtein@1.0.8:
@@ -6900,6 +6654,15 @@ packages:
engines: {node: '>= 6'}
dev: false
registry.npmmirror.com/date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz}
name: date-fns
version: 2.30.0
engines: {node: '>=0.11'}
dependencies:
'@babel/runtime': registry.npmmirror.com/@babel/runtime@7.22.5
dev: false
registry.npmmirror.com/dayjs@1.11.7:
resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz}
name: dayjs
@@ -7173,10 +6936,10 @@ packages:
domelementtype: registry.npmmirror.com/domelementtype@2.3.0
dev: true
registry.npmmirror.com/dompurify@2.3.3:
resolution: {integrity: sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dompurify/-/dompurify-2.3.3.tgz}
registry.npmmirror.com/dompurify@3.0.3:
resolution: {integrity: sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/dompurify/-/dompurify-3.0.3.tgz}
name: dompurify
version: 2.3.3
version: 3.0.3
dev: false
registry.npmmirror.com/domutils@2.8.0:
@@ -7216,6 +6979,12 @@ packages:
name: electron-to-chromium
version: 1.4.425
registry.npmmirror.com/elkjs@0.8.2:
resolution: {integrity: sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz}
name: elkjs
version: 0.8.2
dev: false
registry.npmmirror.com/emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz}
name: emoji-regex
@@ -8233,6 +8002,7 @@ packages:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz}
name: graceful-fs
version: 4.2.11
requiresBuild: true
registry.npmmirror.com/grapheme-splitter@1.0.4:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz}
@@ -8240,20 +8010,6 @@ packages:
version: 1.0.4
dev: true
registry.npmmirror.com/graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz}
name: graphemer
version: 1.4.0
dev: false
registry.npmmirror.com/graphlib@2.1.8:
resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/graphlib/-/graphlib-2.1.8.tgz}
name: graphlib
version: 2.1.8
dependencies:
lodash: registry.npmmirror.com/lodash@4.17.21
dev: false
registry.npmmirror.com/has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/has-bigints/-/has-bigints-1.0.2.tgz}
name: has-bigints
@@ -8388,6 +8144,12 @@ packages:
space-separated-tokens: registry.npmmirror.com/space-separated-tokens@2.0.2
dev: false
registry.npmmirror.com/heap@0.2.7:
resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz}
name: heap
version: 0.2.7
dev: false
registry.npmmirror.com/hexoid@1.0.0:
resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/hexoid/-/hexoid-1.0.0.tgz}
name: hexoid
@@ -9109,10 +8871,10 @@ packages:
commander: registry.npmmirror.com/commander@8.3.0
dev: false
registry.npmmirror.com/khroma@1.4.1:
resolution: {integrity: sha512-+GmxKvmiRuCcUYDgR7g5Ngo0JEDeOsGdNONdU2zsiBQaK4z19Y2NvXqfEDE0ZiIrg45GTZyAnPLVsLZZACYm3Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/khroma/-/khroma-1.4.1.tgz}
registry.npmmirror.com/khroma@2.0.0:
resolution: {integrity: sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/khroma/-/khroma-2.0.0.tgz}
name: khroma
version: 1.4.1
version: 2.0.0
dev: false
registry.npmmirror.com/kitx@2.1.0:
@@ -9144,6 +8906,18 @@ packages:
language-subtag-registry: registry.npmmirror.com/language-subtag-registry@0.3.22
dev: true
registry.npmmirror.com/layout-base@1.0.2:
resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/layout-base/-/layout-base-1.0.2.tgz}
name: layout-base
version: 1.0.2
dev: false
registry.npmmirror.com/layout-base@2.0.1:
resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/layout-base/-/layout-base-2.0.1.tgz}
name: layout-base
version: 2.0.1
dev: false
registry.npmmirror.com/levn@0.3.0:
resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/levn/-/levn-0.3.0.tgz}
name: levn
@@ -9186,6 +8960,12 @@ packages:
p-locate: registry.npmmirror.com/p-locate@5.0.0
dev: true
registry.npmmirror.com/lodash-es@4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz}
name: lodash-es
version: 4.17.21
dev: false
registry.npmmirror.com/lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz}
name: lodash.debounce
@@ -9483,20 +9263,30 @@ packages:
engines: {node: '>= 8'}
dev: true
registry.npmmirror.com/mermaid@8.13.5:
resolution: {integrity: sha512-xLINkCQqZZfqDaLpQVy9BOsws8jT6sLBE2ympDEg4G2uvUu1n61j/h3OFDaA2N4dpZyN7q2pAYkDQ4yywruivA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mermaid/-/mermaid-8.13.5.tgz}
registry.npmmirror.com/mermaid@10.2.3:
resolution: {integrity: sha512-cMVE5s9PlQvOwfORkyVpr5beMsLdInrycAosdr+tpZ0WFjG4RJ/bUHST7aTgHNJbujHkdBRAm+N50P3puQOfPw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mermaid/-/mermaid-10.2.3.tgz}
name: mermaid
version: 8.13.5
version: 10.2.3
dependencies:
'@braintree/sanitize-url': registry.npmmirror.com/@braintree/sanitize-url@3.1.0
'@braintree/sanitize-url': registry.npmmirror.com/@braintree/sanitize-url@6.0.2
cytoscape: registry.npmmirror.com/cytoscape@3.25.0
cytoscape-cose-bilkent: registry.npmmirror.com/cytoscape-cose-bilkent@4.1.0(cytoscape@3.25.0)
cytoscape-fcose: registry.npmmirror.com/cytoscape-fcose@2.2.0(cytoscape@3.25.0)
d3: registry.npmmirror.com/d3@7.8.5
dagre: registry.npmmirror.com/dagre@0.8.5
dagre-d3: registry.npmmirror.com/dagre-d3@0.6.4
dompurify: registry.npmmirror.com/dompurify@2.3.3
graphlib: registry.npmmirror.com/graphlib@2.1.8
khroma: registry.npmmirror.com/khroma@1.4.1
moment-mini: registry.npmmirror.com/moment-mini@2.29.4
dagre-d3-es: registry.npmmirror.com/dagre-d3-es@7.0.10
dayjs: registry.npmmirror.com/dayjs@1.11.7
dompurify: registry.npmmirror.com/dompurify@3.0.3
elkjs: registry.npmmirror.com/elkjs@0.8.2
khroma: registry.npmmirror.com/khroma@2.0.0
lodash-es: registry.npmmirror.com/lodash-es@4.17.21
mdast-util-from-markdown: registry.npmmirror.com/mdast-util-from-markdown@1.3.1
non-layered-tidy-tree-layout: registry.npmmirror.com/non-layered-tidy-tree-layout@2.0.2
stylis: registry.npmmirror.com/stylis@4.2.0
ts-dedent: registry.npmmirror.com/ts-dedent@2.2.0
uuid: registry.npmmirror.com/uuid@9.0.0
web-worker: registry.npmmirror.com/web-worker@1.2.0
transitivePeerDependencies:
- supports-color
dev: false
registry.npmmirror.com/micromark-core-commonmark@1.1.0:
@@ -9886,12 +9676,6 @@ packages:
minimist: registry.npmmirror.com/minimist@1.2.8
dev: false
registry.npmmirror.com/moment-mini@2.29.4:
resolution: {integrity: sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/moment-mini/-/moment-mini-2.29.4.tgz}
name: moment-mini
version: 2.29.4
dev: false
registry.npmmirror.com/mongodb-connection-string-url@2.6.0:
resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz}
name: mongodb-connection-string-url
@@ -10087,6 +9871,12 @@ packages:
engines: {node: '>=6.0.0'}
dev: false
registry.npmmirror.com/non-layered-tidy-tree-layout@2.0.2:
resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz}
name: non-layered-tidy-tree-layout
version: 2.0.2
dev: false
registry.npmmirror.com/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz}
name: normalize-path
@@ -10245,10 +10035,10 @@ packages:
is-wsl: registry.npmmirror.com/is-wsl@2.2.0
dev: true
registry.npmmirror.com/openai@3.2.1:
resolution: {integrity: sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/openai/-/openai-3.2.1.tgz}
registry.npmmirror.com/openai@3.3.0:
resolution: {integrity: sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/openai/-/openai-3.3.0.tgz}
name: openai
version: 3.2.1
version: 3.3.0
dependencies:
axios: registry.npmmirror.com/axios@0.26.1
form-data: registry.npmmirror.com/form-data@4.0.0
@@ -10727,6 +10517,19 @@ packages:
react: registry.npmmirror.com/react@18.2.0
dev: false
registry.npmmirror.com/react-day-picker@8.7.1(date-fns@2.30.0)(react@18.2.0):
resolution: {integrity: sha512-Gv426AW8b151CZfh3aP5RUGztLwHB/EyJgWZ5iMgtzbFBkjHfG6Y66CIQFMWGLnYjsQ9DYSJRmJ5S0Pg5HWKjA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/react-day-picker/-/react-day-picker-8.7.1.tgz}
id: registry.npmmirror.com/react-day-picker/8.7.1
name: react-day-picker
version: 8.7.1
peerDependencies:
date-fns: ^2.28.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
date-fns: registry.npmmirror.com/date-fns@2.30.0
react: registry.npmmirror.com/react@18.2.0
dev: false
registry.npmmirror.com/react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz}
id: registry.npmmirror.com/react-dom/18.2.0
@@ -10791,11 +10594,11 @@ packages:
version: 18.2.0
dev: false
registry.npmmirror.com/react-markdown@8.0.5(@types/react@18.0.28)(react@18.2.0):
resolution: {integrity: sha512-jGJolWWmOWAvzf+xMdB9zwStViODyyFQhNB/bwCerbBKmrTmgmA599CGiOlP58OId1IMoIRsA8UdI1Lod4zb5A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/react-markdown/-/react-markdown-8.0.5.tgz}
id: registry.npmmirror.com/react-markdown/8.0.5
registry.npmmirror.com/react-markdown@8.0.7(@types/react@18.0.28)(react@18.2.0):
resolution: {integrity: sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/react-markdown/-/react-markdown-8.0.7.tgz}
id: registry.npmmirror.com/react-markdown/8.0.7
name: react-markdown
version: 8.0.5
version: 8.0.7
peerDependencies:
'@types/react': '>=16'
react: '>=16'
@@ -11413,6 +11216,7 @@ packages:
name: source-map
version: 0.6.1
engines: {node: '>=0.10.0'}
requiresBuild: true
registry.npmmirror.com/space-separated-tokens@1.1.5:
resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz}
@@ -11769,6 +11573,13 @@ packages:
version: 2.1.0
dev: false
registry.npmmirror.com/ts-dedent@2.2.0:
resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.2.0.tgz}
name: ts-dedent
version: 2.2.0
engines: {node: '>=6.10'}
dev: false
registry.npmmirror.com/tsconfig-paths@3.14.2:
resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz}
name: tsconfig-paths
@@ -12148,6 +11959,13 @@ packages:
dev: false
optional: true
registry.npmmirror.com/uuid@9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/uuid/-/uuid-9.0.0.tgz}
name: uuid
version: 9.0.0
hasBin: true
dev: false
registry.npmmirror.com/uvu@0.5.6:
resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/uvu/-/uvu-0.5.6.tgz}
name: uvu
@@ -12214,6 +12032,12 @@ packages:
version: 2.0.1
dev: false
registry.npmmirror.com/web-worker@1.2.0:
resolution: {integrity: sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/web-worker/-/web-worker-1.2.0.tgz}
name: web-worker
version: 1.2.0
dev: false
registry.npmmirror.com/webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz}
name: webidl-conversions

View File

@@ -1,26 +1,18 @@
### 常见问题
**Git 地址**
[项目地址,完全开源,随便用。](https://github.com/c121914yu/FastGPT)
**问题文档**
[先看文档,再提问](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
**删除和复制**
电脑端:聊天内容右侧有复制和删除的图标。
移动端:点击对话头像,可以选择复制或删除该条内容。
**Git 地址**: [项目地址,完全开源,随便用。](https://github.com/c121914yu/FastGPT)
**问题文档**: [先看文档,再提问](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
**价格表**
如果使用了自己的 Api Key网页上 openai 模型聊天不会计费。可以在账号页,看到详细账单。
| 计费项 | 价格: 元/ 1K tokens包含上下文|
| --- | --- |
| 知识库 - 索引 | 0.001 |
| chatgpt - 对话 | 0.025 |
| gpt4 - 对话 | 0.5 |
| 文件拆分 | 0.025 |
| chatgpt - 对话 | 0.015 |
| chatgpt16K - 对话 | 0.015 |
| gpt4 - 对话 | 0.1 |
| 文件拆分 | 0.015 |
**其他问题**
请 WX 联系: YNyiqi
| 交流群 | 小助手 |
| ----------------------- | -------------------- |
| ![](https://otnvvf-imgs.oss.laf.run/wxqun300.jpg) | ![](https://otnvvf-imgs.oss.laf.run/wx300.jpg) |

View File

@@ -19,9 +19,10 @@ FastGpt 项目完全开源,可随意私有化部署,去除平台风险忧虑
| 计费项 | 价格: 元/ 1K tokens包含上下文|
| --- | --- |
| 知识库 - 索引 | 0.001 |
| chatgpt - 对话 | 0.025 |
| gpt4 - 对话 | 0.5 |
| 文件拆分 | 0.025 |
| chatgpt - 对话 | 0.015 |
| chatgpt16K - 对话 | 0.015 |
| gpt4 - 对话 | 0.1 |
| 文件拆分 | 0.015 |
### 交流群/问题反馈

View File

@@ -1,5 +1,7 @@
### Fast GPT V3.8.4
### Fast GPT V3.9
1. 新增 - mermaid 导图兼容,可以在应用市场 'mermaid 导图' 进行体验
2. 优化 - 部分 UI 和账号页
2. 优化 - 知识库搜索速度
1. 限时优惠活动,更低价的 tokens
2. 新增 - 直接分段训练,可调节段落大小
3. 优化 - tokens 计算性能。
4. 优化 - key 池管理,结合 one-api 项目,实现更方便的 key 池管理,具体参考[docker 部署 FastGpt](https://github.com/c121914yu/FastGPT/blob/main/docs/deploy/docker.md)
5. 新增 - V2 版 OpenAPI可以在任意第三方套壳 ChatGpt 项目中直接使用 FastGpt 的应用,注意!是直接,不需要改任何代码。具体参考[API 文档中《在第三方应用中使用 FastGpt》](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -0,0 +1,8 @@
var _hmt = _hmt || [];
(function () {
const hm = document.createElement('script');
hm.src = 'https://hm.baidu.com/hm.js?a5357e9dab086658bac0b6faf148882e';
const s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(hm, s);
})();

View File

@@ -1,67 +1,110 @@
import { GUIDE_PROMPT_HEADER, NEW_CHATID_HEADER, QUOTE_LEN_HEADER } from '@/constants/chat';
import { Props, ChatResponseType } from '@/pages/api/openapi/v1/chat/completions';
import { sseResponseEventEnum } from '@/constants/chat';
import { getErrText } from '@/utils/tools';
interface StreamFetchProps {
url: string;
data: any;
data: Props;
onMessage: (text: string) => void;
abortSignal: AbortController;
}
export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchProps) =>
new Promise<{
responseText: string;
newChatId: string;
systemPrompt: string;
quoteLen: number;
}>(async (resolve, reject) => {
export const streamFetch = ({ data, onMessage, abortSignal }: StreamFetchProps) =>
new Promise<ChatResponseType & { responseText: string }>(async (resolve, reject) => {
try {
const res = await fetch(url, {
const response = await window.fetch('/api/openapi/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
signal: abortSignal.signal
signal: abortSignal.signal,
body: JSON.stringify({
...data,
stream: true
})
});
const reader = res.body?.getReader();
if (!reader) return;
const decoder = new TextDecoder();
if (response.status !== 200) {
const err = await response.json();
return reject(err);
}
const newChatId = decodeURIComponent(res.headers.get(NEW_CHATID_HEADER) || '');
const systemPrompt = decodeURIComponent(res.headers.get(GUIDE_PROMPT_HEADER) || '').trim();
const quoteLen = res.headers.get(QUOTE_LEN_HEADER)
? Number(res.headers.get(QUOTE_LEN_HEADER))
: 0;
if (!response?.body) {
throw new Error('Request Error');
}
const reader = response.body?.getReader();
const decoder = new TextDecoder('utf-8');
// response data
let responseText = '';
let newChatId = '';
let quoteLen = 0;
const read = async () => {
try {
const { done, value } = await reader?.read();
const { done, value } = await reader.read();
if (done) {
if (res.status === 200) {
resolve({ responseText, newChatId, quoteLen, systemPrompt });
if (response.status === 200) {
return resolve({
responseText,
newChatId,
quoteLen
});
} else {
const parseError = JSON.parse(responseText);
reject(parseError?.message || '请求异常');
return reject('响应过程出现异常~');
}
return;
}
const text = decoder.decode(value);
responseText += text;
onMessage(text);
const chunk = decoder.decode(value);
const chunkLines = chunk.split('\n\n').filter((item) => item);
const chunkResponse = chunkLines.map((item) => {
const splitEvent = item.split('\n');
if (splitEvent.length === 2) {
return {
event: splitEvent[0].replace('event: ', ''),
data: splitEvent[1].replace('data: ', '')
};
}
return {
event: '',
data: splitEvent[0].replace('data: ', '')
};
});
chunkResponse.forEach((item) => {
// parse json data
const data = (() => {
try {
return JSON.parse(item.data);
} catch (error) {
return item.data;
}
})();
if (item.event === sseResponseEventEnum.answer && data !== '[DONE]') {
const answer: string = data?.choices?.[0].delta.content || '';
onMessage(answer);
responseText += answer;
} else if (item.event === sseResponseEventEnum.chatResponse) {
const chatResponse = data as ChatResponseType;
newChatId = chatResponse.newChatId;
quoteLen = chatResponse.quoteLen || 0;
}
});
read();
} catch (err: any) {
if (err?.message === 'The user aborted a request.') {
return resolve({ responseText, newChatId, quoteLen, systemPrompt });
return resolve({
responseText,
newChatId,
quoteLen
});
}
reject(typeof err === 'string' ? err : err?.message || '请求异常');
reject(getErrText(err, '请求异常'));
}
};
read();
} catch (err: any) {
console.log(err, '====');
reject(typeof err === 'string' ? err : err?.message || '请求异常');
console.log(err);
reject(getErrText(err, '请求异常'));
}
});

View File

@@ -42,7 +42,7 @@ export const getKbDataList = (data: GetKbDataListProps) =>
* 获取导出数据(不分页)
*/
export const getExportDataList = (kbId: string) =>
GET<[string, string][]>(
GET<[string, string, string][]>(
`/plugins/kb/data/exportModelData`,
{ kbId },
{

View File

@@ -4,6 +4,7 @@ import type { ChatItemType } from '@/types/chat';
export interface InitChatResponse {
chatId: string;
modelId: string;
systemPrompt?: string;
model: {
name: string;
avatar: string;

View File

@@ -5,3 +5,5 @@ import type { InitDateResponse } from '@/pages/api/system/getInitData';
export const getInitData = () => GET<InitDateResponse>('/system/getInitData');
export const getSystemModelList = () => GET<ChatModelItemType[]>('/system/getModels');
export const uploadImg = (base64Img: string) => POST<string>('/system/uploadImage', { base64Img });

View File

@@ -66,7 +66,7 @@ export const loginOut = () => GET('/user/loginout');
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data);
export const getUserBills = (data: RequestPaging) =>
GET<PagingData<UserBillType>>(`/user/getBill?${Obj2Query(data)}`);
POST<PagingData<UserBillType>>(`/user/getBill`, data);
export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`);

View File

@@ -0,0 +1,160 @@
import React, { useState } from 'react';
import {
Box,
Button,
Modal,
ModalOverlay,
ModalContent,
Flex,
ModalFooter,
ModalBody,
ModalCloseButton,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
TableContainer,
IconButton
} from '@chakra-ui/react';
import { getOpenApiKeys, createAOpenApiKey, delOpenApiById } from '@/api/openapi';
import { useQuery, useMutation } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading';
import dayjs from 'dayjs';
import { AddIcon, DeleteIcon } from '@chakra-ui/icons';
import { getErrText, useCopyData } from '@/utils/tools';
import { useToast } from '@/hooks/useToast';
import MyIcon from '../Icon';
const APIKeyModal = ({ onClose }: { onClose: () => void }) => {
const { Loading } = useLoading();
const { toast } = useToast();
const {
data: apiKeys = [],
isLoading: isGetting,
refetch
} = useQuery(['getOpenApiKeys'], getOpenApiKeys);
const [apiKey, setApiKey] = useState('');
const { copyData } = useCopyData();
const { mutate: onclickCreateApiKey, isLoading: isCreating } = useMutation({
mutationFn: () => createAOpenApiKey(),
onSuccess(res) {
setApiKey(res);
refetch();
},
onError(err) {
toast({
status: 'warning',
title: getErrText(err)
});
}
});
const { mutate: onclickRemove, isLoading: isDeleting } = useMutation({
mutationFn: async (id: string) => delOpenApiById(id),
onSuccess() {
refetch();
}
});
return (
<Modal isOpen onClose={onClose}>
<ModalOverlay />
<ModalContent w={'600px'} maxW={'90vw'} position={'relative'}>
<Box py={3} px={5}>
<Box fontWeight={'bold'} fontSize={'2xl'}>
API
</Box>
<Box fontSize={'sm'} color={'myGray.600'}>
API 使~
</Box>
</Box>
<ModalCloseButton />
<ModalBody minH={'300px'} maxH={['70vh', '500px']} overflow={'overlay'}>
<TableContainer mt={2} position={'relative'}>
<Table>
<Thead>
<Tr>
<Th>Api Key</Th>
<Th></Th>
<Th>使</Th>
<Th />
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
{apiKeys.map(({ id, apiKey, createTime, lastUsedTime }) => (
<Tr key={id}>
<Td>{apiKey}</Td>
<Td>{dayjs(createTime).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Td>
{lastUsedTime
? dayjs(lastUsedTime).format('YYYY/MM/DD HH:mm:ss')
: '没有使用过'}
</Td>
<Td>
<IconButton
icon={<DeleteIcon />}
size={'xs'}
aria-label={'delete'}
variant={'base'}
colorScheme={'gray'}
onClick={() => onclickRemove(id)}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</ModalBody>
<ModalFooter>
<Button
variant="base"
leftIcon={<AddIcon color={'myGray.600'} fontSize={'sm'} />}
onClick={() => onclickCreateApiKey()}
>
</Button>
</ModalFooter>
<Loading loading={isGetting || isCreating || isDeleting} fixed={false} />
</ModalContent>
<Modal isOpen={!!apiKey} onClose={() => setApiKey('')}>
<ModalOverlay />
<ModalContent w={'400px'} maxW={'90vw'}>
<Box py={3} px={5}>
<Box fontWeight={'bold'} fontSize={'2xl'}>
API
</Box>
<Box fontSize={'sm'} color={'myGray.600'}>
~
</Box>
</Box>
<ModalCloseButton />
<ModalBody>
<Flex
bg={'myGray.100'}
px={3}
py={2}
cursor={'pointer'}
onClick={() => copyData(apiKey)}
>
<Box flex={1}>{apiKey}</Box>
<MyIcon name={'copy'} w={'16px'}></MyIcon>
</Flex>
</ModalBody>
<ModalFooter>
<Button variant="base" onClick={() => setApiKey('')}>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Modal>
);
};
export default APIKeyModal;

View File

@@ -0,0 +1,4 @@
.datePicker {
--rdp-background-color: #d6e8ff;
--rdp-accent-color: #0000ff;
}

View File

@@ -0,0 +1,121 @@
import React, { useState, useMemo, useRef } from 'react';
import { Box, Card, Flex, useTheme, useOutsideClick, Button } from '@chakra-ui/react';
import { addDays, format } from 'date-fns';
import { type DateRange, DayPicker } from 'react-day-picker';
import MyIcon from '../Icon';
import 'react-day-picker/dist/style.css';
import styles from './index.module.scss';
import zhCN from 'date-fns/locale/zh-CN';
const DateRangePicker = ({
onChange,
onSuccess,
position = 'bottom',
defaultDate = {
from: addDays(new Date(), -30),
to: new Date()
}
}: {
onChange?: (date: DateRange) => void;
onSuccess?: (date: DateRange) => void;
position?: 'bottom' | 'top';
defaultDate?: DateRange;
}) => {
const theme = useTheme();
const OutRangeRef = useRef(null);
const [range, setRange] = useState<DateRange | undefined>(defaultDate);
const [showSelected, setShowSelected] = useState(false);
const formatSelected = useMemo(() => {
if (range?.from && range.to) {
return `${format(range.from, 'y-MM-dd')} ~ ${format(range.to, 'y-MM-dd')}`;
}
return `${format(new Date(), 'y-MM-dd')} ~ ${format(new Date(), 'y-MM-dd')}`;
}, [range]);
useOutsideClick({
ref: OutRangeRef,
handler: () => {
setShowSelected(false);
}
});
return (
<Box position={'relative'} ref={OutRangeRef}>
<Flex
border={theme.borders.base}
px={3}
py={1}
borderRadius={'sm'}
cursor={'pointer'}
bg={'myWhite.600'}
fontSize={'sm'}
onClick={() => setShowSelected(true)}
>
<Box>{formatSelected}</Box>
<MyIcon ml={2} name={'date'} w={'16px'} color={'myGray.600'} />
</Flex>
{showSelected && (
<Card
position={'absolute'}
zIndex={1}
{...(position === 'top'
? {
bottom: '40px'
}
: {})}
>
<DayPicker
locale={zhCN}
id="test"
mode="range"
className={styles.datePicker}
defaultMonth={defaultDate.to}
selected={range}
disabled={[
{ from: new Date(2022, 3, 1), to: addDays(new Date(), -90) },
{ from: addDays(new Date(), 1), to: new Date(2099, 1, 1) }
]}
onSelect={(date) => {
if (date?.from === undefined) {
date = {
from: range?.from,
to: range?.from
};
}
if (date?.to === undefined) {
date.to = date.from;
}
setRange(date);
onChange && onChange(date);
}}
footer={
<Flex justifyContent={'flex-end'}>
<Button
variant={'outline'}
size={'sm'}
mr={2}
onClick={() => setShowSelected(false)}
>
</Button>
<Button
size={'sm'}
onClick={() => {
onSuccess && onSuccess(range || defaultDate);
setShowSelected(false);
}}
>
</Button>
</Flex>
}
/>
</Card>
)}
</Box>
);
};
export default DateRangePicker;
export type DateRangeType = DateRange;

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686969412308" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3481" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M517.864056 487.834624c-56.774051-54.213739-58.850339-144.187937-4.6366-200.960964 54.212716-56.773028 144.187937-58.849316 200.960964-4.6366 56.775074 54.213739 58.850339 144.186913 4.6366 200.960964C664.613328 539.972075 574.639131 542.048363 517.864056 487.834624zM687.194626 452.994118c37.533848-39.308261 36.09508-101.596909-3.210112-139.128711-39.304168-37.531801-101.593839-36.094056-139.127687 3.211135-37.532825 39.307238-36.093033 101.593839 3.212158 139.125641C587.374176 493.736031 649.660778 492.302379 687.194626 452.994118zM479.104287 670.917406l-101.495602 106.289792c26.206872 25.024953 27.167756 66.540486 2.14178 92.749404-25.028023 26.209942-66.543555 27.16571-92.750427 2.140757l-58.361199 53.027727c0 0-68.750827 11.100826-100.379175-19.101033-31.630395-30.205952-37.865399-112.721271-37.865399-112.721271l246.37427-258.302951c-63.173808-117.608581-47.24707-267.162736 49.939389-368.939747 36.517705-38.242999 80.346933-65.156976 127.165238-81.040734l1.084705 46.269813c-35.443233 14.07967-68.566632 35.596729-96.618525 64.973804-80.271208 84.064604-96.099708 205.865671-49.433876 305.083393l23.075555 39.163975L146.090774 798.015106c0 0 0.593518 49.77873 17.242709 65.677838 14.888082 14.216793 61.832254 9.828856 61.832254 9.828856l60.407812-63.260789 31.631418 30.203906c8.741082 8.346085 22.570042 8.030907 30.91715-0.711198 8.347109-8.742105 8.026814-22.571065-0.713244-30.91715l-31.632441-30.207999 156.456355-163.846672 39.009456 22.481014c101.259218 42.039465 222.201731 20.61041 302.474986-63.453171 104.251366-109.178585 100.260471-282.211477-8.91709-386.464889-33.591049-32.075533-73.260537-53.829999-115.093295-65.49262l-1.030469-45.153386c53.197596 12.471033 103.945397 38.547944 146.323577 79.015611 126.645398 120.931257 131.277906 321.649698 10.344602 448.296119C748.158093 705.787588 599.500355 728.598106 479.104287 670.917406z" p-id="3482"></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686832863390" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4120" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M782.84 188.75h-43.15v-60.46c0-16.57-13.43-30-30-30s-30 13.43-30 30v60.46H371.88v-60.46c0-16.57-13.43-30-30-30s-30 13.43-30 30v60.46H250.5c-66.17 0-120 53.83-120 120v494.47c0 66.17 53.83 120 120 120h532.33c66.17 0 120-53.83 120-120V308.75c0.01-66.17-53.82-120-119.99-120z m-532.34 60h61.37v133.63c0 16.57 13.43 30 30 30s30-13.43 30-30V248.75h307.81v133.63c0 16.57 13.43 30 30 30s30-13.43 30-30V248.75h43.15c33.08 0 60 26.92 60 60V649.5H190.5V308.75c0-33.08 26.92-60 60-60z m532.34 614.47H250.5c-33.08 0-60-26.92-60-60V709.5h652.33v93.72c0.01 33.08-26.91 60-59.99 60z" p-id="4121"></path></svg>

After

Width:  |  Height:  |  Size: 924 B

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683254594671" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1491" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M46.95735957 106.20989621h930.08528086v158.0067668H46.95735957zM46.95735957 353.99323467v608.68515424h930.08528086V353.99323467H46.95735957z m346.5375657 418.35882335L328.85579413 835.19565715l-165.18889183-172.37101684 165.18889183-172.37101686 64.63913114 62.84359914-105.93635373 109.52741772 105.93635373 109.52741771z m127.48273175 62.84359913l-86.18550917-23.34190854 87.98104116-330.37778366 86.1855077 23.34191003L520.97765702 835.19565715z m193.91739489 0l-64.63913114-62.84359913 105.93635372-109.52741771-105.93635372-109.52741772 64.63913114-62.84359914 165.18889182 172.37101686-165.18889182 172.37101684z" p-id="1492"></path></svg>

Before

Width:  |  Height:  |  Size: 976 B

View File

@@ -6,7 +6,6 @@ const map = {
model: require('./icons/model.svg').default,
copy: require('./icons/copy.svg').default,
chatSend: require('./icons/chatSend.svg').default,
develop: require('./icons/develop.svg').default,
user: require('./icons/user.svg').default,
delete: require('./icons/delete.svg').default,
withdraw: require('./icons/withdraw.svg').default,
@@ -33,7 +32,9 @@ const map = {
export: require('./icons/export.svg').default,
text: require('./icons/text.svg').default,
history: require('./icons/history.svg').default,
kbTest: require('./icons/kbTest.svg').default
kbTest: require('./icons/kbTest.svg').default,
date: require('./icons/date.svg').default,
apikey: require('./icons/apikey.svg').default
};
export type IconName = keyof typeof map;

View File

@@ -1,23 +0,0 @@
type TIconfont = {
name: string;
color?: string;
width?: number | string;
height?: number | string;
className?: string;
};
function Iconfont({ name, color = 'inherit', width = 16, height = 16, className = '' }: TIconfont) {
const style = {
fill: color,
width,
height
};
return (
<svg className={`icon ${className}`} aria-hidden="true" style={style}>
<use xlinkHref={`#${name}`}></use>
</svg>
);
}
export default Iconfont;

View File

@@ -44,12 +44,6 @@ const Navbar = ({ unread }: { unread: number }) => {
link: '/model/share',
activeLink: ['/model/share']
},
{
label: '开发',
icon: 'develop',
link: '/openapi',
activeLink: ['/openapi']
},
{
label: '账号',
icon: 'user',

View File

@@ -14,7 +14,7 @@ const Loading = ({ fixed = true }: { fixed?: boolean }) => {
alignItems={'center'}
justifyContent={'center'}
>
<Spinner thickness="4px" speed="0.65s" emptyColor="gray.200" color="myBlue.500" size="xl" />
<Spinner thickness="4px" speed="0.65s" emptyColor="myGray.100" color="myBlue.600" size="xl" />
</Flex>
);
};

View File

@@ -287,8 +287,7 @@ const CodeLight = ({
children,
className,
inline,
match,
...props
match
}: {
children: React.ReactNode & React.ReactNode[];
className?: string;
@@ -315,18 +314,14 @@ const CodeLight = ({
<Box ml={1}></Box>
</Flex>
</Flex>
<SyntaxHighlighter style={codeLight as any} language={match?.[1]} PreTag="pre" {...props}>
<SyntaxHighlighter style={codeLight as any} language={match?.[1]} PreTag="pre">
{String(children)}
</SyntaxHighlighter>
</Box>
);
}
return (
<code className={className} {...props}>
{children}
</code>
);
return <code className={className}>{children}</code>;
};
export default React.memo(CodeLight);

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { Box } from '@chakra-ui/react';
const regex = /((http|https|ftp):\/\/[^\s\u4e00-\u9fa5\u3000-\u303f\uff00-\uffef]+)/gi;
const Link = (props: { href?: string; children?: React.ReactNode[] }) => {
const decText = decodeURIComponent(props.href || '');
const replaceText = decText.replace(regex, (match, p1) => {
const text = decText === props.children?.[0] ? p1 : props.children?.[0];
const isInternal = /^\/#/i.test(p1);
const target = isInternal ? '_self' : '_blank';
return `<a href="${p1}" target=${target}>${text}</a>`;
});
return <Box as={'span'} dangerouslySetInnerHTML={{ __html: replaceText }} />;
};
export default React.memo(Link);

View File

@@ -1,18 +0,0 @@
import React, { memo } from 'react';
import { Box } from '@chakra-ui/react';
const Loading = () => {
return (
<Box
minW={'100px'}
w={'100%'}
h={'80px'}
backgroundImage={'url("/imgs/loading.gif")'}
backgroundSize={'contain'}
backgroundRepeat={'no-repeat'}
backgroundPosition={'center'}
/>
);
};
export default memo(Loading);

View File

@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import { Image, Skeleton } from '@chakra-ui/react';
const MdImage = ({ src }: { src?: string }) => {
const [isLoading, setIsLoading] = useState(true);
const [succeed, setSucceed] = useState(false);
return (
<Skeleton
minH="100px"
isLoaded={!isLoading}
fadeDuration={2}
display={'flex'}
justifyContent={'center'}
my={1}
>
<Image
display={'inline-block'}
borderRadius={'md'}
src={src}
alt={''}
fallbackSrc={'/imgs/errImg.png'}
fallbackStrategy={'onError'}
cursor={succeed ? 'pointer' : 'default'}
loading="eager"
onLoad={() => {
setIsLoading(false);
setSucceed(true);
}}
onError={() => setIsLoading(false)}
onClick={() => {
if (!succeed) return;
window.open(src, '_blank');
}}
/>
</Skeleton>
);
};
export default React.memo(MdImage);

View File

@@ -0,0 +1,25 @@
import React, { memo } from 'react';
import { Box } from '@chakra-ui/react';
const Loading = ({ text }: { text?: string }) => {
return (
<Box>
<Box
minW={'100px'}
w={'100%'}
h={'80px'}
backgroundImage={'url("/imgs/loading.gif")'}
backgroundSize={'contain'}
backgroundRepeat={'no-repeat'}
backgroundPosition={'center'}
/>
{text && (
<Box mt={1} textAlign={'center'} fontSize={'sm'} color={'myGray.600'}>
{text}
</Box>
)}
</Box>
);
};
export default memo(Loading);

View File

@@ -1,15 +1,16 @@
import React, { useEffect, useRef, memo, useCallback, useState } from 'react';
import React, { useEffect, useRef, memo, useCallback, useState, useMemo } from 'react';
import { Box } from '@chakra-ui/react';
// @ts-ignore
import mermaid from 'mermaid';
import MyIcon from '../Icon';
import styles from './index.module.scss';
import MyIcon from '../../Icon';
const mermaidAPI = mermaid.mermaidAPI;
mermaidAPI.initialize({
startOnLoad: false,
startOnLoad: true,
theme: 'base',
flowchart: {
useMaxWidth: false
},
themeVariables: {
fontSize: '14px',
primaryColor: '#d6e8ff',
@@ -21,28 +22,53 @@ mermaidAPI.initialize({
}
});
const punctuationMap: Record<string, string> = {
'': ',',
'': ';',
'。': '.',
'': ':',
'': '!',
'': '?',
'“': '"',
'”': '"',
'': "'",
'': "'",
'【': '[',
'】': ']',
'': '(',
'': ')',
'《': '<',
'》': '>',
'、': ','
};
const MermaidBlock = ({ code }: { code: string }) => {
const dom = useRef<HTMLDivElement>(null);
const ref = useRef<HTMLDivElement>(null);
const [svg, setSvg] = useState('');
useEffect(() => {
try {
const formatCode = code.replace(//g, ':');
mermaidAPI.render(`mermaid-${Date.now()}`, formatCode, (svgCode: string) => {
setSvg(svgCode);
});
} catch (error) {
console.log(error);
}
(async () => {
if (!code) return;
try {
const formatCode = code.replace(
new RegExp(`[${Object.keys(punctuationMap).join('')}]`, 'g'),
(match) => punctuationMap[match]
);
const { svg } = await mermaid.render(`mermaid-${Date.now()}`, formatCode);
setSvg(svg);
} catch (e: any) {
console.log('[Mermaid] ', e?.message);
}
})();
}, [code]);
const onclickExport = useCallback(() => {
const svg = dom.current?.children[0];
const svg = ref.current?.children[0];
if (!svg) return;
const w = svg.clientWidth * 4;
const h = svg.clientHeight * 4;
const rate = svg.clientHeight / svg.clientWidth;
const w = 3000;
const h = rate * w;
const canvas = document.createElement('canvas');
canvas.width = w;
@@ -54,7 +80,7 @@ const MermaidBlock = ({ code }: { code: string }) => {
ctx.fillRect(0, 0, w, h);
const img = new Image();
img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(dom.current.innerHTML)}`;
img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(ref.current.innerHTML)}`;
img.onload = () => {
ctx.drawImage(img, 0, 0, w, h);
@@ -73,17 +99,25 @@ const MermaidBlock = ({ code }: { code: string }) => {
}, []);
return (
<Box position={'relative'}>
<Box
position={'relative'}
_hover={{
'& > .export': {
display: 'block'
}
}}
>
<Box
ref={dom}
as={'p'}
className={styles.mermaid}
overflowX={'auto'}
ref={ref}
minW={'100px'}
minH={'50px'}
py={4}
dangerouslySetInnerHTML={{ __html: svg }}
/>
<MyIcon
className="export"
display={'none'}
name={'export'}
w={'20px'}
position={'absolute'}
@@ -100,4 +134,4 @@ const MermaidBlock = ({ code }: { code: string }) => {
);
};
export default memo(MermaidBlock);
export default MermaidBlock;

View File

@@ -319,7 +319,6 @@
border: medium none;
margin: 0;
padding: 0;
white-space: pre;
}
.markdown .highlight pre,
.markdown pre {
@@ -345,10 +344,6 @@
word-break: break-all;
}
p {
white-space: pre-line;
}
pre {
display: block;
width: 100%;
@@ -419,9 +414,4 @@
.mermaid {
overflow-x: auto;
svg {
height: auto !important;
width: auto;
}
}

View File

@@ -1,58 +1,54 @@
import React, { memo, useMemo, useEffect } from 'react';
import React from 'react';
import ReactMarkdown from 'react-markdown';
import { formatLinkText } from '@/utils/tools';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import remarkBreaks from 'remark-breaks';
import rehypeKatex from 'rehype-katex';
import RemarkGfm from 'remark-gfm';
import RemarkMath from 'remark-math';
import RehypeKatex from 'rehype-katex';
import RemarkBreaks from 'remark-breaks';
import 'katex/dist/katex.min.css';
import styles from './index.module.scss';
import CodeLight from './codeLight';
import Loading from './Loading';
import MermaidCodeBlock from './MermaidCodeBlock';
const Markdown = ({
source,
isChatting = false,
formatLink
}: {
source: string;
formatLink?: boolean;
isChatting?: boolean;
}) => {
const formatSource = useMemo(() => {
return formatLink ? formatLinkText(source) : source;
}, [source, formatLink]);
import Link from './Link';
import CodeLight from './CodeLight';
import MermaidCodeBlock from './img/MermaidCodeBlock';
import MdImage from './img/Image';
function Code({ inline, className, children }: any) {
const match = /language-(\w+)/.exec(className || '');
if (match?.[1] === 'mermaid') {
return <MermaidCodeBlock code={String(children)} />;
}
return (
<CodeLight className={className} inline={inline} match={match}>
{children}
</CodeLight>
);
}
function Image({ src }: { src?: string }) {
return <MdImage src={src} />;
}
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
return (
<ReactMarkdown
className={`markdown ${styles.markdown}
${isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''}
`}
remarkPlugins={[remarkGfm, remarkMath, remarkBreaks]}
rehypePlugins={[rehypeKatex]}
${isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''}
`}
remarkPlugins={[RemarkGfm, RemarkMath, RemarkBreaks]}
rehypePlugins={[RehypeKatex]}
components={{
a: Link,
img: Image,
pre: 'div',
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '');
if (match?.[1] === 'mermaid') {
return isChatting ? <Loading /> : <MermaidCodeBlock code={String(children)} />;
}
return (
<CodeLight className={className} inline={inline} match={match} {...props}>
{children}
</CodeLight>
);
}
code: Code
}}
linkTarget="_blank"
>
{formatSource}
{source}
</ReactMarkdown>
);
};
export default memo(Markdown);
export default Markdown;

View File

@@ -25,6 +25,7 @@ const Radio = ({ list, value, onChange, ...props }: Props) => {
mr: 1,
borderRadius: '16px',
transition: '0.2s',
boxSizing: 'border-box',
...(value === item.value
? {
border: '5px solid',

View File

@@ -1,76 +0,0 @@
import React, { useRef, useEffect, useMemo } from 'react';
import type { BoxProps } from '@chakra-ui/react';
import { Box } from '@chakra-ui/react';
import { throttle } from 'lodash';
import { useLoading } from '@/hooks/useLoading';
interface Props extends BoxProps {
nextPage: () => void;
isLoadAll: boolean;
requesting: boolean;
children: React.ReactNode;
initRequesting?: boolean;
}
const ScrollData = ({
children,
nextPage,
isLoadAll,
requesting,
initRequesting,
...props
}: Props) => {
const { Loading } = useLoading({ defaultLoading: true });
const elementRef = useRef<HTMLDivElement>(null);
const loadText = useMemo(() => {
if (requesting) return '请求中……';
if (isLoadAll) return '已加载全部';
return '点击加载更多';
}, [isLoadAll, requesting]);
useEffect(() => {
if (!elementRef.current) return;
const scrolling = throttle((e: Event) => {
const element = e.target as HTMLDivElement;
if (!element) return;
// 当前滚动位置
const scrollTop = element.scrollTop;
// 可视高度
const clientHeight = element.clientHeight;
// 内容总高度
const scrollHeight = element.scrollHeight;
// 判断是否滚动到底部
if (scrollTop + clientHeight + 100 >= scrollHeight) {
nextPage();
}
}, 100);
elementRef.current.addEventListener('scroll', scrolling);
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
elementRef.current?.removeEventListener('scroll', scrolling);
};
}, [elementRef, nextPage]);
return (
<Box {...props} ref={elementRef} overflowY={'auto'} position={'relative'}>
{children}
<Box
mt={2}
fontSize={'xs'}
color={'blackAlpha.500'}
textAlign={'center'}
cursor={loadText === '点击加载更多' ? 'pointer' : 'default'}
onClick={() => {
if (loadText !== '点击加载更多') return;
nextPage();
}}
>
{loadText}
</Box>
{initRequesting && <Loading fixed={false} />}
</Box>
);
};
export default ScrollData;

View File

@@ -0,0 +1,81 @@
import React from 'react';
import { Menu, MenuButton, MenuList, MenuItem, Button, useDisclosure } from '@chakra-ui/react';
import type { ButtonProps } from '@chakra-ui/react';
import { ChevronDownIcon } from '@chakra-ui/icons';
interface Props extends ButtonProps {
value?: string;
placeholder?: string;
list: {
label: string;
id: string;
}[];
onchange?: (val: string) => void;
}
const MySelect = ({ placeholder, value, width = 'auto', list, onchange, ...props }: Props) => {
const menuItemStyles = {
borderRadius: 'sm',
py: 2,
display: 'flex',
alignItems: 'center',
_hover: {
backgroundColor: 'myWhite.600'
}
};
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Menu autoSelect={false} onOpen={onOpen} onClose={onClose}>
<MenuButton as={'span'}>
<Button
width={width}
px={3}
variant={'base'}
display={'flex'}
alignItems={'center'}
justifyContent={'space-between'}
{...(isOpen
? {
boxShadow: '0px 0px 4px #A8DBFF',
borderColor: 'myBlue.600'
}
: {})}
{...props}
>
{list.find((item) => item.id === value)?.label || placeholder}
<ChevronDownIcon />
</Button>
</MenuButton>
<MenuList
minW={
Array.isArray(width) ? width.map((item) => `${item} !important`) : `${width} !important`
}
p={'6px'}
border={'1px solid #fff'}
boxShadow={'0px 2px 4px rgba(161, 167, 179, 0.25), 0px 0px 1px rgba(121, 141, 159, 0.25);'}
zIndex={99}
>
{list.map((item) => (
<MenuItem
key={item.id}
{...menuItemStyles}
{...(value === item.id
? {
color: 'myBlue.600'
}
: {})}
onClick={() => {
if (onchange && value !== item.id) {
onchange(item.id);
}
}}
>
{item.label}
</MenuItem>
))}
</MenuList>
</Menu>
);
};
export default MySelect;

View File

@@ -9,28 +9,30 @@ import {
} from '@chakra-ui/react';
const MySlider = ({
markList,
markList = [],
setVal,
activeVal,
max = 100,
min = 0,
step = 1
step = 1,
width = '100%'
}: {
markList: {
markList?: {
label: string | number;
value: number;
}[];
activeVal?: number;
activeVal: number;
setVal: (index: number) => void;
max?: number;
min?: number;
step?: number;
width?: string | string[] | number | number[];
}) => {
const startEndPointStyle = {
content: '""',
borderRadius: '10px',
width: '10px',
height: '10px',
borderRadius: '6px',
width: '6px',
height: '6px',
backgroundColor: '#ffffff',
border: '2px solid #D7DBE2',
position: 'absolute',
@@ -44,37 +46,62 @@ const MySlider = ({
}, [activeVal, markList]);
return (
<Slider max={max} min={min} step={step} size={'lg'} value={value} onChange={setVal}>
{markList.map((item, i) => (
<Slider
max={max}
min={min}
step={step}
size={'lg'}
value={activeVal}
width={width}
onChange={setVal}
>
{markList?.map((item, i) => (
<SliderMark
key={item.value}
value={i}
mt={3}
value={item.value}
fontSize={'sm'}
mt={3}
whiteSpace={'nowrap'}
transform={'translateX(-50%)'}
{...(activeVal === item.value ? { color: 'myBlue.500', fontWeight: 'bold' } : {})}
color={'myGray.600'}
>
<Box px={3} cursor={'pointer'}>
{item.label}
</Box>
</SliderMark>
))}
<SliderMark
value={activeVal}
textAlign="center"
bg="myBlue.600"
color="white"
px={1}
minW={'18px'}
w={'auto'}
h={'18px'}
borderRadius={'18px'}
fontSize={'xs'}
transform={'translate(-50%, -170%)'}
boxSizing={'border-box'}
>
{activeVal}
</SliderMark>
<SliderTrack
bg={'#EAEDF3'}
overflow={'visible'}
h={'4px'}
_before={{
...startEndPointStyle,
left: '-5px'
left: '-3px'
}}
_after={{
...startEndPointStyle,
right: '-5px'
right: '-3px'
}}
>
<SliderFilledTrack />
<SliderFilledTrack bg={'myBlue.600'} />
</SliderTrack>
<SliderThumb border={'2.5px solid'} borderColor={'myBlue.500'}></SliderThumb>
<SliderThumb border={'3px solid'} borderColor={'myBlue.600'}></SliderThumb>
</Slider>
);
};

View File

@@ -24,13 +24,13 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
return {
fontSize: 'md',
outP: '4px',
inlineP: 2
inlineP: 1
};
case 'lg':
return {
fontSize: 'lg',
outP: '5px',
inlineP: 3
inlineP: 2
};
}
}, [size]);

File diff suppressed because one or more lines are too long

View File

@@ -8,14 +8,12 @@ export type EmbeddingModelType = 'text-embedding-ada-002';
export enum OpenAiChatEnum {
'GPT35' = 'gpt-3.5-turbo',
'GPT3516k' = 'gpt-3.5-turbo-16k',
'GPT4' = 'gpt-4',
'GPT432k' = 'gpt-4-32k'
}
export enum ClaudeEnum {
'Claude' = 'Claude'
}
export type ChatModelType = `${OpenAiChatEnum}` | `${ClaudeEnum}`;
export type ChatModelType = `${OpenAiChatEnum}`;
export type ChatModelItemType = {
chatModel: ChatModelType;
@@ -29,11 +27,19 @@ export type ChatModelItemType = {
export const ChatModelMap = {
[OpenAiChatEnum.GPT35]: {
chatModel: OpenAiChatEnum.GPT35,
name: 'ChatGpt',
contextMaxToken: 4096,
systemMaxToken: 2700,
name: 'Gpt35-4k',
contextMaxToken: 4000,
systemMaxToken: 2400,
maxTemperature: 1.2,
price: 2.5
price: 1.5
},
[OpenAiChatEnum.GPT3516k]: {
chatModel: OpenAiChatEnum.GPT3516k,
name: 'Gpt35-16k',
contextMaxToken: 16000,
systemMaxToken: 8000,
maxTemperature: 1.2,
price: 1.5
},
[OpenAiChatEnum.GPT4]: {
chatModel: OpenAiChatEnum.GPT4,
@@ -41,7 +47,7 @@ export const ChatModelMap = {
contextMaxToken: 8000,
systemMaxToken: 4000,
maxTemperature: 1.2,
price: 50
price: 10
},
[OpenAiChatEnum.GPT432k]: {
chatModel: OpenAiChatEnum.GPT432k,
@@ -50,14 +56,6 @@ export const ChatModelMap = {
systemMaxToken: 8000,
maxTemperature: 1.2,
price: 90
},
[ClaudeEnum.Claude]: {
chatModel: ClaudeEnum.Claude,
name: 'Claude(免费体验)',
contextMaxToken: 9000,
systemMaxToken: 2700,
maxTemperature: 1,
price: 0
}
};
@@ -71,78 +69,26 @@ export const getChatModelList = async () => {
return list;
};
export enum ModelStatusEnum {
running = 'running',
training = 'training',
pending = 'pending',
closed = 'closed'
}
export const formatModelStatus = {
[ModelStatusEnum.running]: {
colorTheme: 'green',
text: '运行中'
},
[ModelStatusEnum.training]: {
colorTheme: 'blue',
text: '训练中'
},
[ModelStatusEnum.pending]: {
colorTheme: 'gray',
text: '加载中'
},
[ModelStatusEnum.closed]: {
colorTheme: 'red',
text: '已关闭'
}
};
/* 知识库搜索时的配置 */
// 搜索方式
export enum appVectorSearchModeEnum {
hightSimilarity = 'hightSimilarity', // 高相似度+禁止回复
lowSimilarity = 'lowSimilarity', // 低相似度
noContext = 'noContex' // 高相似度+无上下文回复
}
export const ModelVectorSearchModeMap: Record<
`${appVectorSearchModeEnum}`,
{
text: string;
similarity: number;
}
> = {
[appVectorSearchModeEnum.hightSimilarity]: {
text: '高相似度, 无匹配时拒绝回复',
similarity: 0.8
},
[appVectorSearchModeEnum.noContext]: {
text: '高相似度,无匹配时直接回复',
similarity: 0.8
},
[appVectorSearchModeEnum.lowSimilarity]: {
text: '低相似度匹配',
similarity: 0.3
}
};
export const defaultModel: ModelSchema = {
_id: 'modelId',
userId: 'userId',
name: '模型名称',
avatar: '/icon/logo.png',
status: ModelStatusEnum.pending,
intro: '',
updateTime: Date.now(),
chat: {
relatedKbs: [],
searchMode: appVectorSearchModeEnum.hightSimilarity,
searchSimilarity: 0.2,
searchLimit: 5,
searchEmptyText: '',
systemPrompt: '',
temperature: 0,
maxToken: 4000,
chatModel: OpenAiChatEnum.GPT35
},
share: {
isShare: false,
isShareDetail: false,
intro: '',
collection: 0
}
};

View File

@@ -11,15 +11,11 @@ const { definePartsStyle: switchPart, defineMultiStyleConfig: switchMultiStyle }
createMultiStyleConfigHelpers(switchAnatomy.keys);
const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } =
createMultiStyleConfigHelpers(selectAnatomy.keys);
const { definePartsStyle: checkboxPart, defineMultiStyleConfig: checkboxMultiStyle } =
createMultiStyleConfigHelpers(checkboxAnatomy.keys);
// modal 弹窗
const ModalTheme = defineMultiStyleConfig({
baseStyle: definePartsStyle({
dialog: {
width: '90%'
}
dialog: {}
})
});
@@ -41,7 +37,7 @@ const Button = defineStyleConfig({
},
sm: {
fontSize: 'sm',
px: 3,
px: 4,
py: 0,
fontWeight: 'normal',
height: '26px',
@@ -69,6 +65,7 @@ const Button = defineStyleConfig({
backgroundImage:
'linear-gradient(to bottom right, #2152d9 0%,#3370ff 40%, #4e83fd 100%) !important',
color: 'white',
border: 'none',
_hover: {
filter: 'brightness(115%)'
},

View File

@@ -1,21 +1,27 @@
import { useState, useCallback, useMemo, useEffect } from 'react';
import { useRef, useState, useCallback, useLayoutEffect, useMemo, useEffect } from 'react';
import type { PagingData } from '../types/index';
import { IconButton, Flex, Box, Input } from '@chakra-ui/react';
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
import { useMutation } from '@tanstack/react-query';
import { useToast } from './useToast';
import { throttle } from 'lodash';
const thresholdVal = 100;
export const usePagination = <T = any,>({
api,
pageSize = 10,
params = {},
defaultRequest = true
defaultRequest = true,
type = 'button'
}: {
api: (data: any) => any;
pageSize?: number;
params?: Record<string, any>;
defaultRequest?: boolean;
type?: 'button' | 'scroll';
}) => {
const elementRef = useRef<HTMLDivElement>(null);
const { toast } = useToast();
const [pageNum, setPageNum] = useState(1);
const [total, setTotal] = useState(0);
@@ -76,6 +82,20 @@ export const usePagination = <T = any,>({
mutate(+e.target.value);
}
}}
onKeyDown={(e) => {
// @ts-ignore
const val = +e.target.value;
if (val && e.keyCode === 13) {
if (val === pageNum) return;
if (val >= maxPage) {
mutate(maxPage);
} else if (val < 1) {
mutate(1);
} else {
mutate(val);
}
}
}}
/>
<Box mx={2}>/</Box>
{maxPage}
@@ -94,6 +114,60 @@ export const usePagination = <T = any,>({
);
}, [isLoading, maxPage, mutate, pageNum]);
const ScrollData = useCallback(
({ children, ...props }: { children: React.ReactNode }) => {
const loadText = useMemo(() => {
if (isLoading) return '请求中……';
if (total <= data.length) return '已加载全部';
return '点击加载更多';
}, []);
return (
<Box {...props} ref={elementRef} overflow={'overlay'}>
{children}
<Box
mt={2}
fontSize={'xs'}
color={'blackAlpha.500'}
textAlign={'center'}
cursor={loadText === '点击加载更多' ? 'pointer' : 'default'}
onClick={() => {
if (loadText !== '点击加载更多') return;
mutate(pageNum + 1);
}}
>
{loadText}
</Box>
</Box>
);
},
[data.length, isLoading, mutate, pageNum, total]
);
useLayoutEffect(() => {
if (!elementRef.current || type !== 'scroll') return;
const scrolling = throttle((e: Event) => {
const element = e.target as HTMLDivElement;
if (!element) return;
// 当前滚动位置
const scrollTop = element.scrollTop;
// 可视高度
const clientHeight = element.clientHeight;
// 内容总高度
const scrollHeight = element.scrollHeight;
// 判断是否滚动到底部
if (scrollTop + clientHeight + thresholdVal >= scrollHeight) {
mutate(pageNum + 1);
}
}, 100);
elementRef.current.addEventListener('scroll', scrolling);
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
elementRef.current?.removeEventListener('scroll', scrolling);
};
}, [elementRef, mutate, pageNum, type]);
useEffect(() => {
defaultRequest && mutate(1);
}, []);
@@ -105,6 +179,7 @@ export const usePagination = <T = any,>({
data,
isLoading,
Pagination,
ScrollData,
getData: mutate
};
};

View File

@@ -8,9 +8,9 @@ import { theme } from '@/constants/theme';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import NProgress from 'nprogress'; //nprogress module
import Router from 'next/router';
import 'nprogress/nprogress.css';
import '../styles/reset.scss';
import { useGlobalStore } from '@/store/global';
import 'nprogress/nprogress.css';
import '@/styles/reset.scss';
//Binding events.
Router.events.on('routeChangeStart', () => NProgress.start());
@@ -28,10 +28,10 @@ const queryClient = new QueryClient({
}
});
export default function App({ Component, pageProps }: AppProps) {
function App({ Component, pageProps }: AppProps) {
const {
loadInitData,
initData: { googleVerKey }
initData: { googleVerKey, baiduTongji }
} = useGlobalStore();
useEffect(() => {
@@ -49,16 +49,23 @@ export default function App({ Component, pageProps }: AppProps) {
/>
<link rel="icon" href="/favicon.ico" />
</Head>
<Script src="/js/qrcode.min.js" strategy="lazyOnload"></Script>
<Script src="/js/pdf.js" strategy="lazyOnload"></Script>
<Script src="/js/html2pdf.bundle.min.js" strategy="lazyOnload"></Script>
<Script src="/js/particles.js" strategy="lazyOnload"></Script>
<Script src="/js/qrcode.min.js" strategy="afterInteractive"></Script>
<Script src="/js/pdf.js" strategy="afterInteractive"></Script>
<Script src="/js/html2pdf.bundle.min.js" strategy="afterInteractive"></Script>
{baiduTongji && <Script src="/js/baidutongji.js" strategy="afterInteractive"></Script>}
{googleVerKey && (
<Script
src={`https://www.recaptcha.net/recaptcha/api.js?render=${googleVerKey}`}
strategy="lazyOnload"
></Script>
<>
<Script
src={`https://www.recaptcha.net/recaptcha/api.js?render=${googleVerKey}`}
strategy="afterInteractive"
></Script>
<Script
src={`https://www.google.com/recaptcha/api.js?render=${googleVerKey}`}
strategy="afterInteractive"
></Script>
</>
)}
<Script src="/js/particles.js"></Script>
<QueryClientProvider client={queryClient}>
<ChakraProvider theme={theme}>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
@@ -72,6 +79,5 @@ export default function App({ Component, pageProps }: AppProps) {
);
}
// export function reportWebVitals(metric: NextWebVitalsMetric) {
// console.log(metric);
// }
// @ts-ignore
export default App;

View File

@@ -1,12 +1,10 @@
function Error({ errStr }: { errStr: string }) {
return <p>{errStr}</p>;
function Error() {
return (
<p>
safari chrome
</p>
);
}
Error.getInitialProps = ({ res, err }: { res: any; err: any }) => {
console.log(err);
return {
errStr: `部分系统不兼容,导致页面崩溃。如果可以,请联系作者,反馈下具体操作和页面。大部分是 苹果 的 safari 浏览器导致,可以尝试更换 chrome 浏览器。`
};
};
export default Error;

View File

@@ -1,195 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { connectToDatabase } from '@/service/mongo';
import { authChat } from '@/service/utils/auth';
import { modelServiceToolMap } from '@/service/utils/chat';
import { ChatItemType } from '@/types/chat';
import { jsonRes } from '@/service/response';
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
import { pushChatBill } from '@/service/events/pushBill';
import { resStreamResponse } from '@/service/utils/chat';
import { appKbSearch } from '../openapi/kb/appKbSearch';
import { ChatRoleEnum, QUOTE_LEN_HEADER, GUIDE_PROMPT_HEADER } from '@/constants/chat';
import { BillTypeEnum } from '@/constants/user';
import { sensitiveCheck } from '../openapi/text/sensitiveCheck';
import { NEW_CHATID_HEADER } from '@/constants/chat';
import { saveChat } from './saveChat';
import { Types } from 'mongoose';
/* 发送提示词 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
res.on('close', () => {
res.end();
});
res.on('error', () => {
console.log('error: ', 'request error');
res.end();
});
try {
const { chatId, prompt, modelId } = req.body as {
prompt: [ChatItemType, ChatItemType];
modelId: string;
chatId?: string;
};
if (!modelId || !prompt) {
throw new Error('缺少参数');
}
await connectToDatabase();
let startTime = Date.now();
const { model, showModelDetail, content, userOpenAiKey, systemAuthKey, userId } =
await authChat({
modelId,
chatId,
req
});
const modelConstantsData = ChatModelMap[model.chat.chatModel];
// 读取对话内容
const prompts = [...content, prompt[0]];
const {
code = 200,
systemPrompts = [],
quote = [],
guidePrompt = ''
} = await (async () => {
// 使用了知识库搜索
if (model.chat.relatedKbs?.length > 0) {
const { code, searchPrompts, rawSearch, guidePrompt } = await appKbSearch({
model,
userId,
fixedQuote: content[content.length - 1]?.quote || [],
prompt: prompt[0],
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
});
return {
code,
quote: rawSearch,
systemPrompts: searchPrompts,
guidePrompt
};
}
if (model.chat.systemPrompt) {
return {
guidePrompt: model.chat.systemPrompt,
systemPrompts: [
{
obj: ChatRoleEnum.System,
value: model.chat.systemPrompt
}
]
};
}
return {};
})();
// get conversationId. create a newId if it is null
const conversationId = chatId || String(new Types.ObjectId());
!chatId && res.setHeader(NEW_CHATID_HEADER, conversationId);
if (showModelDetail) {
guidePrompt && res.setHeader(GUIDE_PROMPT_HEADER, encodeURIComponent(guidePrompt));
res.setHeader(QUOTE_LEN_HEADER, quote.length);
}
// search result is empty
if (code === 201) {
const response = systemPrompts[0]?.value;
await saveChat({
chatId,
newChatId: conversationId,
modelId,
prompts: [
prompt[0],
{
...prompt[1],
quote: [],
value: response
}
],
userId
});
return res.end(response);
}
prompts.unshift(...systemPrompts);
// content check
await sensitiveCheck({
input: [...systemPrompts, prompt[0]].map((item) => item.value).join('')
});
// 计算温度
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
2
);
// 发出 chat 请求
const { streamResponse, responseMessages } = await modelServiceToolMap[
model.chat.chatModel
].chatCompletion({
apiKey: userOpenAiKey || systemAuthKey,
temperature: +temperature,
messages: prompts,
stream: true,
res,
chatId: conversationId
});
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
if (res.closed) return res.end();
try {
const { totalTokens, finishMessages, responseContent } = await resStreamResponse({
model: model.chat.chatModel,
res,
chatResponse: streamResponse,
prompts: responseMessages
});
// save chat
await saveChat({
chatId,
newChatId: conversationId,
modelId,
prompts: [
prompt[0],
{
...prompt[1],
value: responseContent,
quote: showModelDetail ? quote : [],
systemPrompt: showModelDetail ? guidePrompt : ''
}
],
userId
});
res.end();
// 只有使用平台的 key 才计费
pushChatBill({
isPay: !userOpenAiKey,
chatModel: model.chat.chatModel,
userId,
chatId: conversationId,
textLen: finishMessages.map((item) => item.value).join('').length,
tokens: totalTokens,
type: BillTypeEnum.chat
});
} catch (error) {
res.end();
console.log('error结束', error);
}
} catch (err: any) {
res.status(500);
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -6,7 +6,6 @@ import { authUser } from '@/service/utils/auth';
import { ChatItemType } from '@/types/chat';
import { authModel } from '@/service/utils/auth';
import mongoose from 'mongoose';
import { ModelStatusEnum } from '@/constants/model';
import type { ModelSchema } from '@/types/mongoSchema';
/* 初始化我的聊天框,需要身份验证 */
@@ -21,32 +20,32 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase();
let model: ModelSchema;
// 没有 modelId 时直接获取用户的第一个id
if (!modelId) {
const myModel = await Model.findOne({ userId });
if (!myModel) {
const { _id } = await Model.create({
name: '应用1',
userId,
status: ModelStatusEnum.running
});
model = (await Model.findById(_id)) as ModelSchema;
const model = await (async () => {
if (!modelId) {
const myModel = await Model.findOne({ userId });
if (!myModel) {
const { _id } = await Model.create({
name: '应用1',
userId
});
return (await Model.findById(_id)) as ModelSchema;
} else {
return myModel;
}
} else {
model = myModel;
// 校验使用权限
const authRes = await authModel({
modelId,
userId,
authUser: false,
authOwner: false
});
return authRes.model;
}
modelId = model._id;
} else {
// 校验使用权限
const authRes = await authModel({
modelId,
userId,
authUser: false,
authOwner: false
});
model = authRes.model;
}
})();
modelId = modelId || model._id;
// 历史记录
let history: ChatItemType[] = [];
@@ -88,6 +87,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]);
}
const isOwner = String(model.userId) === userId;
jsonRes<InitChatResponse>(res, {
data: {
chatId: chatId || '',
@@ -95,10 +96,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
model: {
name: model.name,
avatar: model.avatar,
intro: model.share.intro,
canUse: model.share.isShare || String(model.userId) === userId
intro: model.intro,
canUse: model.share.isShare || isOwner
},
chatModel: model.chat.chatModel,
systemPrompt: isOwner ? model.chat.systemPrompt : '',
history
}
});

View File

@@ -4,10 +4,9 @@ import { ChatItemType } from '@/types/chat';
import { connectToDatabase, Chat, Model } from '@/service/mongo';
import { authModel } from '@/service/utils/auth';
import { authUser } from '@/service/utils/auth';
import mongoose from 'mongoose';
import { Types } from 'mongoose';
type Props = {
newChatId?: string;
chatId?: string;
modelId: string;
prompts: [ChatItemType, ChatItemType];
@@ -16,7 +15,7 @@ type Props = {
/* 聊天内容存存储 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { chatId, modelId, prompts, newChatId } = req.body as Props;
const { chatId, modelId, prompts } = req.body as Props;
if (!prompts) {
throw new Error('缺少参数');
@@ -24,16 +23,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { userId } = await authUser({ req, authToken: true });
const nId = await saveChat({
const response = await saveChat({
chatId,
modelId,
prompts,
newChatId,
userId
});
jsonRes(res, {
data: nId
data: response
});
} catch (err) {
jsonRes(res, {
@@ -44,25 +42,31 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
export async function saveChat({
chatId,
newChatId,
chatId,
modelId,
prompts,
userId
}: Props & { userId: string }) {
}: Props & { newChatId?: Types.ObjectId; userId: string }) {
await connectToDatabase();
const { model } = await authModel({ modelId, userId, authOwner: false });
const content = prompts.map((item) => ({
_id: item._id ? new mongoose.Types.ObjectId(item._id) : undefined,
_id: item._id,
obj: item.obj,
value: item.value,
systemPrompt: item.systemPrompt,
systemPrompt: item.systemPrompt || '',
quote: item.quote || []
}));
const [id] = await Promise.all([
...(chatId // update chat
if (String(model.userId) === userId) {
await Model.findByIdAndUpdate(modelId, {
updateTime: new Date()
});
}
const [response] = await Promise.all([
...(chatId
? [
Chat.findByIdAndUpdate(chatId, {
$push: {
@@ -73,17 +77,21 @@ export async function saveChat({
title: content[0].value.slice(0, 20),
latestChat: content[1].value,
updateTime: new Date()
}).then(() => '')
}).then(() => ({
newChatId: ''
}))
]
: [
Chat.create({
_id: newChatId ? new mongoose.Types.ObjectId(newChatId) : undefined,
_id: newChatId,
userId,
modelId,
content,
title: content[0].value.slice(0, 20),
latestChat: content[1].value
}).then((res) => res._id)
}).then((res) => ({
newChatId: String(res._id)
}))
]),
// update model
...(String(model.userId) === userId
@@ -96,6 +104,6 @@ export async function saveChat({
]);
return {
id
...response
};
}

View File

@@ -1,140 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { connectToDatabase } from '@/service/mongo';
import { authShareChat } from '@/service/utils/auth';
import { modelServiceToolMap } from '@/service/utils/chat';
import { ChatItemSimpleType } from '@/types/chat';
import { jsonRes } from '@/service/response';
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
import { pushChatBill, updateShareChatBill } from '@/service/events/pushBill';
import { resStreamResponse } from '@/service/utils/chat';
import { ChatRoleEnum } from '@/constants/chat';
import { BillTypeEnum } from '@/constants/user';
import { sensitiveCheck } from '../../openapi/text/sensitiveCheck';
import { appKbSearch } from '../../openapi/kb/appKbSearch';
/* 发送提示词 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
res.on('error', () => {
console.log('error: ', 'request error');
res.end();
});
try {
const { shareId, password, historyId, prompts } = req.body as {
prompts: ChatItemSimpleType[];
password: string;
shareId: string;
historyId: string;
};
if (!historyId || !prompts) {
throw new Error('分享链接无效');
}
await connectToDatabase();
let startTime = Date.now();
const { model, userOpenAiKey, systemAuthKey, userId } = await authShareChat({
shareId,
password
});
const modelConstantsData = ChatModelMap[model.chat.chatModel];
const { code = 200, systemPrompts = [] } = await (async () => {
// 使用了知识库搜索
if (model.chat.relatedKbs?.length > 0) {
const { code, searchPrompts } = await appKbSearch({
model,
userId,
fixedQuote: [],
prompt: prompts[prompts.length - 1],
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
});
return {
code,
systemPrompts: searchPrompts
};
}
if (model.chat.systemPrompt) {
return {
systemPrompts: [
{
obj: ChatRoleEnum.System,
value: model.chat.systemPrompt
}
]
};
}
return {};
})();
// search result is empty
if (code === 201) {
return res.send(systemPrompts[0]?.value);
}
prompts.unshift(...systemPrompts);
// content check
await sensitiveCheck({
input: [...systemPrompts, prompts[prompts.length - 1]].map((item) => item.value).join('')
});
// 计算温度
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
2
);
// 发出请求
const { streamResponse, responseMessages } = await modelServiceToolMap[
model.chat.chatModel
].chatCompletion({
apiKey: userOpenAiKey || systemAuthKey,
temperature: +temperature,
messages: prompts,
stream: true,
res,
chatId: historyId
});
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
if (res.closed) return res.end();
try {
const { totalTokens, finishMessages } = await resStreamResponse({
model: model.chat.chatModel,
res,
chatResponse: streamResponse,
prompts: responseMessages
});
res.end();
/* bill */
pushChatBill({
isPay: !userOpenAiKey,
chatModel: model.chat.chatModel,
userId,
textLen: finishMessages.map((item) => item.value).join('').length,
tokens: totalTokens,
type: BillTypeEnum.chat
});
updateShareChatBill({
shareId,
tokens: totalTokens
});
} catch (error) {
res.end();
console.log('error结束', error);
}
} catch (err: any) {
res.status(500);
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -50,7 +50,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
model: {
name: model.name,
avatar: model.avatar,
intro: model.share.intro
intro: model.intro
},
chatModel: model.chat.chatModel
}

View File

@@ -3,7 +3,6 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { ModelStatusEnum } from '@/constants/model';
import { Model } from '@/service/models/model';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
@@ -25,15 +24,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const authCount = await Model.countDocuments({
userId
});
if (authCount >= 30) {
throw new Error('上限 30 个应用');
if (authCount >= 50) {
throw new Error('上限 50 个应用');
}
// 创建模型
const response = await Model.create({
name,
userId,
status: ModelStatusEnum.running
userId
});
jsonRes(res, {

View File

@@ -18,14 +18,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
{
userId
},
'_id avatar name chat.systemPrompt'
'_id avatar name intro'
).sort({
updateTime: -1
}),
Collection.find({ userId })
.populate({
path: 'modelId',
select: '_id avatar name chat.systemPrompt',
select: '_id avatar name intro',
match: { 'share.isShare': true }
})
.then((res) => res.filter((item) => item.modelId))
@@ -37,14 +37,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
_id: item._id,
name: item.name,
avatar: item.avatar,
systemPrompt: item.chat.systemPrompt
intro: item.intro
})),
myCollectionModels: myCollections
.map((item: any) => ({
_id: item.modelId?._id,
name: item.modelId?.name,
avatar: item.modelId?.avatar,
systemPrompt: item.modelId?.chat.systemPrompt
intro: item.modelId?.intro
}))
.filter((item) => !myModels.find((model) => String(model._id) === String(item._id))) // 去重
}

View File

@@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
$and: [
{ 'share.isShare': true },
{
$or: [{ name: { $regex: regex } }, { 'share.intro': { $regex: regex } }]
$or: [{ name: { $regex: regex } }, { intro: { $regex: regex } }]
}
]
};
@@ -66,6 +66,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
avatar: { $ifNull: ['$avatar', '/icon/logo.png'] },
name: 1,
userId: 1,
intro: 1,
share: 1,
isCollection: {
$cond: {
@@ -77,7 +78,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}
},
{
$sort: { 'share.collection': -1 }
$sort: { 'share.topNum': -1, 'share.collection': -1 }
},
{
$skip: (pageNum - 1) * pageSize

View File

@@ -9,10 +9,10 @@ import { authModel } from '@/service/utils/auth';
/* 获取我的模型 */
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { name, avatar, chat, share } = req.body as ModelUpdateParams;
const { name, avatar, chat, share, intro } = req.body as ModelUpdateParams;
const { modelId } = req.query as { modelId: string };
if (!name || !chat || !modelId) {
if (!modelId) {
throw new Error('参数错误');
}
@@ -35,10 +35,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
{
name,
avatar,
intro,
chat,
'share.isShare': share.isShare,
'share.isShareDetail': share.isShareDetail,
'share.intro': share.intro
...(share && {
'share.isShare': share.isShare,
'share.isShareDetail': share.isShareDetail
})
}
);

View File

@@ -2,16 +2,13 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { connectToDatabase } from '@/service/mongo';
import { authUser, authModel, getApiKey } from '@/service/utils/auth';
import { modelServiceToolMap, resStreamResponse } from '@/service/utils/chat';
import { ChatItemSimpleType } from '@/types/chat';
import { ChatItemType } from '@/types/chat';
import { jsonRes } from '@/service/response';
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
import { ChatModelMap } from '@/constants/model';
import { pushChatBill } from '@/service/events/pushBill';
import { ChatRoleEnum } from '@/constants/chat';
import { withNextCors } from '@/service/utils/tools';
import { BillTypeEnum } from '@/constants/user';
import { sensitiveCheck } from '../../openapi/text/sensitiveCheck';
import { NEW_CHATID_HEADER } from '@/constants/chat';
import { Types } from 'mongoose';
import { appKbSearch } from '../kb/appKbSearch';
/* 发送提示词 */
@@ -32,7 +29,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
isStream = true
} = req.body as {
chatId?: string;
prompts: ChatItemSimpleType[];
prompts: ChatItemType[];
modelId: string;
isStream: boolean;
};
@@ -66,67 +63,60 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
});
const modelConstantsData = ChatModelMap[model.chat.chatModel];
const prompt = prompts[prompts.length - 1];
let systemPrompts: {
obj: ChatRoleEnum;
value: string;
}[] = [];
const { userSystemPrompt = [], quotePrompt = [] } = await (async () => {
// 使用了知识库搜索
if (model.chat.relatedKbs?.length > 0) {
const { userSystemPrompt, quotePrompt } = await appKbSearch({
model,
userId,
fixedQuote: [],
prompt: prompt,
similarity: model.chat.searchSimilarity,
limit: model.chat.searchLimit
});
// 使用了知识库搜索
if (model.chat.relatedKbs?.length > 0) {
const { code, searchPrompts } = await appKbSearch({
model,
userId,
fixedQuote: [],
prompt: prompts[prompts.length - 1],
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
});
// search result is empty
if (code === 201) {
return isStream
? res.send(searchPrompts[0]?.value)
: jsonRes(res, {
data: searchPrompts[0]?.value,
message: searchPrompts[0]?.value
});
return {
userSystemPrompt: userSystemPrompt ? [userSystemPrompt] : [],
quotePrompt: [quotePrompt]
};
}
if (model.chat.systemPrompt) {
return {
userSystemPrompt: [
{
obj: ChatRoleEnum.System,
value: model.chat.systemPrompt
}
]
};
}
return {};
})();
systemPrompts = searchPrompts;
} else if (model.chat.systemPrompt) {
systemPrompts = [
{
obj: ChatRoleEnum.System,
value: model.chat.systemPrompt
}
];
// search result is empty
if (model.chat.relatedKbs?.length > 0 && !quotePrompt[0]?.value && model.chat.searchEmptyText) {
const response = model.chat.searchEmptyText;
return res.end(response);
}
prompts.unshift(...systemPrompts);
// content check
await sensitiveCheck({
input: [...systemPrompts, prompts[prompts.length - 1]].map((item) => item.value).join('')
});
// 读取对话内容
const completePrompts = [...quotePrompt, ...prompts.slice(0, -1), ...userSystemPrompt, prompt];
// 计算温度
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
2
);
// get conversationId. create a newId if it is null
const conversationId = chatId || String(new Types.ObjectId());
!chatId && res?.setHeader(NEW_CHATID_HEADER, conversationId);
// 发出请求
const { streamResponse, responseMessages, responseText, totalTokens } =
await modelServiceToolMap[model.chat.chatModel].chatCompletion({
apiKey,
temperature: +temperature,
messages: prompts,
messages: completePrompts,
stream: isStream,
res,
chatId: conversationId
res
});
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);

View File

@@ -18,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
({ _id, apiKey, createTime, lastUsedTime }) => {
return {
id: _id,
apiKey: `${apiKey.substring(0, 2)}******${apiKey.substring(apiKey.length - 2)}`,
apiKey: `******${apiKey.substring(apiKey.length - 4)}`,
createTime,
lastUsedTime
};

View File

@@ -3,9 +3,8 @@ import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import { PgClient } from '@/service/pg';
import { withNextCors } from '@/service/utils/tools';
import type { ChatItemSimpleType } from '@/types/chat';
import type { ChatItemType } from '@/types/chat';
import type { ModelSchema } from '@/types/mongoSchema';
import { appVectorSearchModeEnum } from '@/constants/model';
import { authModel } from '@/service/utils/auth';
import { ChatModelMap } from '@/constants/model';
import { ChatRoleEnum } from '@/constants/chat';
@@ -19,18 +18,21 @@ export type QuoteItemType = {
source?: string;
};
type Props = {
prompts: ChatItemSimpleType[];
prompts: ChatItemType[];
similarity: number;
limit: number;
appId: string;
};
type Response = {
code: 200 | 201;
rawSearch: QuoteItemType[];
guidePrompt: string;
searchPrompts: {
userSystemPrompt: {
obj: ChatRoleEnum;
value: string;
}[];
};
quotePrompt: {
obj: ChatRoleEnum;
value: string;
};
};
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
@@ -41,7 +43,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
throw new Error('userId is empty');
}
const { prompts, similarity, appId } = req.body as Props;
const { prompts, similarity, limit, appId } = req.body as Props;
if (!similarity || !Array.isArray(prompts) || !appId) {
throw new Error('params is error');
@@ -58,7 +60,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
userId,
fixedQuote: [],
prompt: prompts[prompts.length - 1],
similarity
similarity,
limit
});
jsonRes<Response>(res, {
@@ -76,23 +79,24 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
export async function appKbSearch({
model,
userId,
fixedQuote,
fixedQuote = [],
prompt,
similarity
similarity = 0.8,
limit = 5
}: {
model: ModelSchema;
userId: string;
fixedQuote: QuoteItemType[];
prompt: ChatItemSimpleType;
fixedQuote?: QuoteItemType[];
prompt: ChatItemType;
similarity: number;
limit: number;
}): Promise<Response> {
const modelConstantsData = ChatModelMap[model.chat.chatModel];
// get vector
const promptVector = await openaiEmbedding({
userId,
input: [prompt.value],
type: 'chat'
input: [prompt.value]
});
// search kb
@@ -103,7 +107,7 @@ export async function appKbSearch({
.map((item) => `'${item}'`)
.join(',')}) AND vector <#> '[${promptVector[0]}]' < -${similarity} order by vector <#> '[${
promptVector[0]
}]' limit 15;
}]' limit ${limit};
COMMIT;`
);
@@ -115,7 +119,7 @@ export async function appKbSearch({
...searchRes.slice(0, 3),
...fixedQuote.slice(0, 2),
...searchRes.slice(3),
...fixedQuote.slice(2, 5)
...fixedQuote.slice(2, Math.floor(fixedQuote.length * 0.4))
].filter((item) => {
if (idSet.has(item.id)) {
return false;
@@ -125,86 +129,44 @@ export async function appKbSearch({
});
// 计算固定提示词的 token 数量
const guidePrompt = model.chat.systemPrompt // user system prompt
const userSystemPrompt = model.chat.systemPrompt // user system prompt
? {
obj: ChatRoleEnum.System,
obj: ChatRoleEnum.Human,
value: model.chat.systemPrompt
}
: model.chat.searchMode === appVectorSearchModeEnum.noContext
? {
obj: ChatRoleEnum.System,
value: `知识库是关于"${model.name}"的内容,根据知识库内容回答问题.`
}
: {
obj: ChatRoleEnum.System,
value: `玩一个问答游戏,规则为:
1.你完全忘记你已有的知识
2.你只回答关于"${model.name}"的问题
3.你只从知识库中选择内容进行回答
4.如果问题不在知识库中,你会回答:"我不知道。"
请务必遵守规则`
obj: ChatRoleEnum.Human,
value: `知识库是关于 ${model.name} 的内容,参考知识库回答问题。与 "${model.name}" 无关内容,直接回复: "我不知道"。`
};
const fixedSystemTokens = modelToolMap[model.chat.chatModel].countTokens({
messages: [guidePrompt]
messages: [userSystemPrompt]
});
// filter part quote by maxToken
const sliceResult = modelToolMap[model.chat.chatModel]
.tokenSlice({
maxToken: modelConstantsData.systemMaxToken - fixedSystemTokens,
messages: filterSearch.map((item) => ({
messages: filterSearch.map((item, i) => ({
obj: ChatRoleEnum.System,
value: `${item.q}\n${item.a}`
value: `${i + 1}: [${item.q}\n${item.a}]`
}))
})
.map((item) => item.value);
.map((item) => item.value)
.join('\n')
.trim();
// slice filterSearch
const rawSearch = filterSearch.slice(0, sliceResult.length);
// system prompt
const systemPrompt = sliceResult.join('\n').trim();
/* 高相似度+不回复 */
if (!systemPrompt && model.chat.searchMode === appVectorSearchModeEnum.hightSimilarity) {
return {
code: 201,
rawSearch: [],
guidePrompt: '',
searchPrompts: [
{
obj: ChatRoleEnum.System,
value: '对不起,你的问题不在知识库中。'
}
]
};
}
/* 高相似度+无上下文,不添加额外知识,仅用系统提示词 */
if (!systemPrompt && model.chat.searchMode === appVectorSearchModeEnum.noContext) {
return {
code: 200,
rawSearch: [],
guidePrompt: model.chat.systemPrompt || '',
searchPrompts: model.chat.systemPrompt
? [
{
obj: ChatRoleEnum.System,
value: model.chat.systemPrompt
}
]
: []
};
}
const quoteText = sliceResult ? `知识库:\n${sliceResult}` : '';
return {
code: 200,
rawSearch,
guidePrompt: guidePrompt.value || '',
searchPrompts: [
{
obj: ChatRoleEnum.System,
value: `知识库:<${systemPrompt}>`
},
guidePrompt
]
userSystemPrompt,
quotePrompt: {
obj: ChatRoleEnum.System,
value: quoteText
}
};
}

View File

@@ -8,6 +8,7 @@ import { TrainingModeEnum } from '@/constants/plugin';
import { startQueue } from '@/service/utils/tools';
import { PgClient } from '@/service/pg';
import { modelToolMap } from '@/utils/plugin';
import { OpenAiChatEnum } from '@/constants/model';
type DateItemType = { a: string; q: string; source?: string };
@@ -23,8 +24,8 @@ export type Response = {
};
const modeMaxToken = {
[TrainingModeEnum.index]: 700,
[TrainingModeEnum.qa]: 3300
[TrainingModeEnum.index]: 6000,
[TrainingModeEnum.qa]: 10000
};
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
@@ -75,14 +76,17 @@ export async function pushDataToKb({
data.forEach((item) => {
const text = item.q + item.a;
// count token
const token = modelToolMap['gpt-3.5-turbo'].countTokens({
messages: [{ obj: 'System', value: item.q }]
});
if (mode === TrainingModeEnum.qa) {
// count token
const token = modelToolMap[OpenAiChatEnum.GPT3516k].countTokens({
messages: [{ obj: 'System', value: item.q }]
});
if (token > modeMaxToken[TrainingModeEnum.qa]) {
return;
}
}
if (mode === TrainingModeEnum.qa && token > modeMaxToken[TrainingModeEnum.qa]) {
console.log('q is too long');
} else if (!set.has(text)) {
if (!set.has(text)) {
filterData.push(item);
set.add(text);
}

View File

@@ -29,8 +29,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
const vector = await openaiEmbedding({
userId,
input: [text],
type: 'training'
input: [text]
});
const response: any = await PgClient.query(

View File

@@ -21,8 +21,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (q) {
return openaiEmbedding({
userId,
input: [q],
type: 'chat'
input: [q]
});
}
return [];

View File

@@ -6,25 +6,24 @@ import { getOpenAIApi } from '@/service/utils/chat/openai';
import { embeddingModel } from '@/constants/model';
import { axiosConfig } from '@/service/utils/tools';
import { pushGenerateVectorBill } from '@/service/events/pushBill';
import { ApiKeyType } from '@/service/utils/auth';
import { OpenAiChatEnum } from '@/constants/model';
type Props = {
input: string[];
type?: ApiKeyType;
};
type Response = number[][];
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { userId } = await authUser({ req });
let { input, type } = req.query as Props;
let { input } = req.query as Props;
if (!Array.isArray(input)) {
throw new Error('缺少参数');
}
jsonRes<Response>(res, {
data: await openaiEmbedding({ userId, input, type, mustPay: true })
data: await openaiEmbedding({ userId, input, mustPay: true })
});
} catch (err) {
console.log(err);
@@ -38,14 +37,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
export async function openaiEmbedding({
userId,
input,
mustPay = false,
type = 'chat'
mustPay = false
}: { userId: string; mustPay?: boolean } & Props) {
const { userOpenAiKey, systemAuthKey } = await getApiKey({
model: 'gpt-3.5-turbo',
model: OpenAiChatEnum.GPT35,
userId,
mustPay,
type
mustPay
});
// 获取 chatAPI
@@ -64,7 +61,7 @@ export async function openaiEmbedding({
}
)
.then((res) => ({
tokenLen: res.data.usage.total_tokens || 0,
tokenLen: res.data?.usage?.total_tokens || 0,
vectors: res.data.data.map((item) => item.embedding)
}));

View File

@@ -4,7 +4,7 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, OpenApi } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890');
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -14,11 +14,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const count = await OpenApi.find({ userId }).countDocuments();
if (count >= 5) {
throw new Error('最多 5 组API Key');
if (count >= 10) {
throw new Error('最多 10 API 秘钥');
}
const apiKey = `${userId}-${nanoid()}`;
const apiKey = `fastgpt-${nanoid()}`;
await OpenApi.create({
userId,

View File

@@ -2,17 +2,18 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import type { ChatItemSimpleType } from '@/types/chat';
import type { ChatItemType } from '@/types/chat';
import { countOpenAIToken } from '@/utils/plugin/openai';
import { OpenAiChatEnum } from '@/constants/model';
type ModelType = 'gpt-3.5-turbo' | 'gpt-4' | 'gpt-4-32k';
type ModelType = `${OpenAiChatEnum}`;
type Props = {
messages: ChatItemSimpleType[];
messages: ChatItemType[];
model: ModelType;
maxLen: number;
};
type Response = ChatItemSimpleType[];
type Response = ChatItemType[];
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -44,11 +45,11 @@ export function gpt_chatItemTokenSlice({
model,
maxToken
}: {
messages: ChatItemSimpleType[];
messages: ChatItemType[];
model: ModelType;
maxToken: number;
}) {
let result: ChatItemSimpleType[] = [];
let result: ChatItemType[] = [];
for (let i = 0; i < messages.length; i++) {
const msgs = [...result, messages[i]];

View File

@@ -33,7 +33,7 @@ export async function sensitiveCheck({ input }: Props) {
}
const response = await axios({
...axiosConfig(getSystemOpenAiKey('chat')),
...axiosConfig(getSystemOpenAiKey()),
method: 'POST',
url: `/moderations`,
data: {

View File

@@ -0,0 +1,312 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { connectToDatabase } from '@/service/mongo';
import { authUser, authModel, getApiKey, authShareChat } from '@/service/utils/auth';
import { modelServiceToolMap, V2_StreamResponse } from '@/service/utils/chat';
import { jsonRes } from '@/service/response';
import { ChatModelMap } from '@/constants/model';
import { pushChatBill, updateShareChatBill } from '@/service/events/pushBill';
import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat';
import { withNextCors } from '@/service/utils/tools';
import { BillTypeEnum } from '@/constants/user';
import { appKbSearch } from '../../../openapi/kb/appKbSearch';
import type { CreateChatCompletionRequest } from 'openai';
import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt';
import { getChatHistory } from './getHistory';
import { saveChat } from '@/pages/api/chat/saveChat';
import { sseResponse } from '@/service/utils/tools';
import { getErrText } from '@/utils/tools';
import { type ChatCompletionRequestMessage } from 'openai';
import { Types } from 'mongoose';
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string };
type FastGptWebChatProps = {
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
appId?: string;
};
type FastGptShareChatProps = {
password?: string;
shareId?: string;
};
export type Props = CreateChatCompletionRequest &
FastGptWebChatProps &
FastGptShareChatProps & {
messages: MessageItemType[];
};
export type ChatResponseType = {
newChatId: string;
quoteLen?: number;
};
/* 发送提示词 */
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
res.on('close', () => {
res.end();
});
res.on('error', () => {
console.log('error: ', 'request error');
res.end();
});
let { chatId, appId, shareId, password = '', stream = false, messages = [] } = req.body as Props;
let step = 0;
try {
if (!messages) {
throw new Error('Prams Error');
}
if (!Array.isArray(messages)) {
throw new Error('messages is not array');
}
await connectToDatabase();
let startTime = Date.now();
/* user auth */
const {
userId,
appId: authAppid,
authType
} = await (shareId
? authShareChat({
shareId,
password
})
: authUser({ req }));
appId = appId ? appId : authAppid;
if (!appId) {
throw new Error('appId is empty');
}
// auth app permission
const { model, showModelDetail } = await authModel({
userId,
modelId: appId,
authOwner: false,
reserveDetail: true
});
const showAppDetail = !shareId && showModelDetail;
/* get api key */
const { systemAuthKey: apiKey, userOpenAiKey } = await getApiKey({
model: model.chat.chatModel,
userId,
mustPay: authType !== 'token'
});
// get history
const { history } = await getChatHistory({ chatId, userId });
const prompts = history.concat(gptMessage2ChatType(messages));
// adapt fastgpt web
if (prompts[prompts.length - 1].obj === 'AI') {
prompts.pop();
}
// user question
const prompt = prompts[prompts.length - 1];
const {
rawSearch = [],
userSystemPrompt = [],
quotePrompt = []
} = await (async () => {
// 使用了知识库搜索
if (model.chat.relatedKbs?.length > 0) {
const { rawSearch, userSystemPrompt, quotePrompt } = await appKbSearch({
model,
userId,
fixedQuote: history[history.length - 1]?.quote,
prompt,
similarity: model.chat.searchSimilarity,
limit: model.chat.searchLimit
});
return {
rawSearch,
userSystemPrompt: userSystemPrompt ? [userSystemPrompt] : [],
quotePrompt: [quotePrompt]
};
}
if (model.chat.systemPrompt) {
return {
userSystemPrompt: [
{
obj: ChatRoleEnum.System,
value: model.chat.systemPrompt
}
]
};
}
return {};
})();
// search result is empty
if (model.chat.relatedKbs?.length > 0 && !quotePrompt[0]?.value && model.chat.searchEmptyText) {
const response = model.chat.searchEmptyText;
if (stream) {
sseResponse({
res,
event: sseResponseEventEnum.answer,
data: textAdaptGptResponse({
text: response,
model: model.chat.chatModel,
finish_reason: 'stop'
})
});
return res.end();
} else {
return res.json({
id: chatId || '',
model: model.chat.chatModel,
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
choices: [
{ message: [{ role: 'assistant', content: response }], finish_reason: 'stop', index: 0 }
]
});
}
}
// api messages. [quote,context,systemPrompt,question]
const completePrompts = [...quotePrompt, ...prompts.slice(0, -1), ...userSystemPrompt, prompt];
// chat temperature
const modelConstantsData = ChatModelMap[model.chat.chatModel];
// FastGpt temperature range: 1~10
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
2
);
// start model api. responseText and totalTokens: valid only if stream = false
const { streamResponse, responseMessages, responseText, totalTokens } =
await modelServiceToolMap[model.chat.chatModel].chatCompletion({
apiKey: userOpenAiKey || apiKey,
temperature: +temperature,
maxToken: model.chat.maxToken,
messages: completePrompts,
stream,
res
});
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
if (res.closed) return res.end();
// create a chatId
const newChatId = chatId === '' ? new Types.ObjectId() : undefined;
// response answer
const {
textLen = 0,
answer = responseText,
tokens = totalTokens
} = await (async () => {
if (stream) {
// 创建响应流
res.setHeader('Content-Type', 'text/event-stream;charset-utf-8');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Transfer-Encoding', 'chunked');
res.setHeader('X-Accel-Buffering', 'no');
res.setHeader('Cache-Control', 'no-cache, no-transform');
step = 1;
try {
// response newChatId and quota
sseResponse({
res,
event: sseResponseEventEnum.chatResponse,
data: JSON.stringify({
newChatId,
quoteLen: rawSearch.length
})
});
// response answer
const { finishMessages, totalTokens, responseContent } = await V2_StreamResponse({
model: model.chat.chatModel,
res,
chatResponse: streamResponse,
prompts: responseMessages
});
return {
answer: responseContent,
textLen: finishMessages.map((item) => item.value).join('').length,
tokens: totalTokens
};
} catch (error) {
console.log('stream response error', error);
return {};
}
} else {
return {
textLen: responseMessages.map((item) => item.value).join('').length
};
}
})();
// save chat history
if (typeof chatId === 'string') {
await saveChat({
newChatId,
chatId,
modelId: appId,
prompts: [
prompt,
{
_id: messages[messages.length - 1]._id,
obj: ChatRoleEnum.AI,
value: answer,
...(showAppDetail
? {
quote: rawSearch,
systemPrompt: userSystemPrompt?.[0]?.value
}
: {})
}
],
userId
});
}
// close response
if (stream) {
res.end();
} else {
res.json({
...(showAppDetail
? {
rawSearch
}
: {}),
newChatId,
id: chatId || '',
model: model.chat.chatModel,
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: tokens },
choices: [
{ message: [{ role: 'assistant', content: answer }], finish_reason: 'stop', index: 0 }
]
});
}
pushChatBill({
isPay: !userOpenAiKey,
chatModel: model.chat.chatModel,
userId,
textLen,
tokens,
type: authType === 'apikey' ? BillTypeEnum.openapiChat : BillTypeEnum.chat
});
shareId &&
updateShareChatBill({
shareId,
tokens
});
} catch (err: any) {
res.status(500);
if (step === 1) {
res.end(getErrText(err, 'Stream response error'));
} else {
jsonRes(res, {
code: 500,
error: err
});
}
}
});

View File

@@ -0,0 +1,66 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import { connectToDatabase, Chat } from '@/service/mongo';
import { Types } from 'mongoose';
import type { ChatItemType } from '@/types/chat';
export type Props = {
chatId?: string;
limit?: number;
};
export type Response = { history: ChatItemType[] };
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { userId } = await authUser({ req });
const { chatId, limit } = req.body as Props;
jsonRes<Response>(res, {
data: await getChatHistory({
chatId,
userId,
limit
})
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}
export async function getChatHistory({
chatId,
userId,
limit = 50
}: Props & { userId: string }): Promise<Response> {
if (!chatId) {
return { history: [] };
}
const history = await Chat.aggregate([
{ $match: { _id: new Types.ObjectId(chatId), userId: new Types.ObjectId(userId) } },
{
$project: {
content: {
$slice: ['$content', -limit] // 返回 content 数组的最后50个元素
}
}
},
{ $unwind: '$content' },
{
$project: {
_id: '$content._id',
obj: '$content.obj',
value: '$content.value',
quote: '$content.quote'
}
}
]);
return { history };
}

View File

@@ -42,16 +42,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
where: [['kb_id', kbId], 'AND', ['user_id', userId]]
});
// 从 pg 中获取所有数据
const pgData = await PgClient.select<{ q: string; a: string }>('modelData', {
const pgData = await PgClient.select<{ q: string; a: string; source: string }>('modelData', {
where: [['kb_id', kbId], 'AND', ['user_id', userId]],
fields: ['q', 'a'],
fields: ['q', 'a', 'source'],
order: [{ field: 'id', mode: 'DESC' }],
limit: count
});
const data: [string, string][] = pgData.rows.map((item) => [
const data: [string, string, string][] = pgData.rows.map((item) => [
item.q.replace(/\n/g, '\\n'),
item.a.replace(/\n/g, '\\n')
item.a.replace(/\n/g, '\\n'),
item.source
]);
// update export time

View File

@@ -5,13 +5,15 @@ import { jsonRes } from '@/service/response';
export type InitDateResponse = {
beianText: string;
googleVerKey: string;
baiduTongji: boolean;
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
jsonRes<InitDateResponse>(res, {
data: {
beianText: process.env.SAFE_BEIAN_TEXT || '',
googleVerKey: process.env.CLIENT_GOOGLE_VER_TOKEN || ''
googleVerKey: process.env.CLIENT_GOOGLE_VER_TOKEN || '',
baiduTongji: process.env.BAIDU_TONGJI === '1'
}
});
}

View File

@@ -7,10 +7,9 @@ import { ChatModelMap, OpenAiChatEnum } from '@/constants/model';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const chatModelList: ChatModelItemType[] = [];
if (global.systemEnv.openAIKeys) {
if (process.env.OPENAIKEY) {
chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT3516k]);
chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT35]);
}
if (global.systemEnv.gpt4Key) {
chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT4]);
}

View File

@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Image } from '@/service/mongo';
// get the models available to the system
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { id } = req.query;
const data = await Image.findById(id);
if (!data) {
throw new Error('no image');
}
res.setHeader('Content-Type', 'image/jpeg');
res.send(data.binary);
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}

View File

@@ -0,0 +1,37 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Image } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
type Props = { base64Img: string };
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { userId } = await authUser({ req, authToken: true });
const { base64Img } = req.body as Props;
const data = await uploadImg({
userId,
base64Img
});
jsonRes(res, { data });
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}
export async function uploadImg({ base64Img, userId }: Props & { userId: string }) {
const base64Data = base64Img.split(',')[1];
const { _id } = await Image.create({
userId,
binary: Buffer.from(base64Data, 'base64')
});
return `/api/system/img/${_id}`;
}

View File

@@ -12,7 +12,7 @@ import { startQueue } from '@/service/utils/tools';
/* 校验支付结果 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
let { payId } = req.query as { payId: string };
const { payId } = req.query as { payId: string };
const { userId } = await authUser({ req, authToken: true });
@@ -34,10 +34,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
throw new Error('找不到用户');
}
// 获取邀请者
let inviter: UserModelSchema | null = null;
if (user.inviterId) {
inviter = await User.findById(user.inviterId);
}
const inviter = await (async () => {
if (user.inviterId) {
return User.findById(user.inviterId, '_id promotion');
}
return null;
})();
const payRes = await getPayResult(payOrder.orderId);
@@ -73,28 +75,35 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
amount: (payOrder.price / PRICE_SCALE) * inviter.promotion.rate * 0.01
});
}
jsonRes(res, {
unlockTask(userId);
return jsonRes(res, {
data: '支付成功'
});
unlockTask(userId);
}
} catch (error) {
await Pay.findByIdAndUpdate(payId, {
status: 'NOTPAY'
});
console.log(error);
// roll back status
try {
await Pay.findByIdAndUpdate(payId, {
status: 'NOTPAY'
});
} catch (error) {}
}
} else if (payRes.trade_state === 'CLOSED' || diffInHours > 24) {
return jsonRes(res, {
code: 500,
data: '更新订单失败,请重试'
});
}
if (payRes.trade_state === 'CLOSED' || diffInHours > 24) {
// 订单已关闭
await Pay.findByIdAndUpdate(payId, {
status: 'CLOSED'
});
jsonRes(res, {
return jsonRes(res, {
data: '订单已过期'
});
} else {
throw new Error(payRes?.trade_state_desc || '订单无效');
}
throw new Error(payRes?.trade_state_desc || '订单无效');
} catch (err) {
// console.log(err);
jsonRes(res, {

View File

@@ -4,23 +4,32 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, Bill } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import { adaptBill } from '@/utils/adapt';
import { addDays } from 'date-fns';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
let { pageNum = 1, pageSize = 10 } = req.query as {
pageNum: string;
pageSize: string;
const {
pageNum = 1,
pageSize = 10,
dateStart = addDays(new Date(), -7),
dateEnd = new Date()
} = req.body as {
pageNum: number;
pageSize: number;
dateStart: Date;
dateEnd: Date;
};
pageNum = +pageNum;
pageSize = +pageSize;
const { userId } = await authUser({ req, authToken: true });
await connectToDatabase();
const where = {
userId
userId,
time: {
$gte: new Date(dateStart).setHours(0, 0, 0, 0),
$lte: new Date(dateEnd).setHours(23, 59, 59, 999)
}
};
// get bill record and total by record

View File

@@ -41,6 +41,7 @@ const PcSliderBar = ({
chatId: string;
};
const ContextMenuRef = useRef(null);
const onclickContext = useRef(false);
const theme = useTheme();
const { isPc } = useGlobalStore();
@@ -68,10 +69,16 @@ const PcSliderBar = ({
// close contextMenu
useOutsideClick({
ref: ContextMenuRef,
handler: () =>
handler: () => {
setTimeout(() => {
setContextMenuData(undefined);
}, 10)
if (contextMenuData && !onclickContext.current) {
setContextMenuData(undefined);
}
}, 10);
setTimeout(() => {
onclickContext.current = false;
}, 10);
}
});
const onclickContextMenu = useCallback(
@@ -80,9 +87,10 @@ const PcSliderBar = ({
if (!isPc) return;
onclickContext.current = true;
setContextMenuData({
left: e.clientX + 15,
top: e.clientY + 10,
left: e.clientX,
top: e.clientY,
history
});
},

View File

@@ -38,9 +38,6 @@ const ModelList = ({ models, modelId }: { models: ModelListItemType[]; modelId:
<Box className="textEllipsis" color={'myGray.1000'}>
{item.name}
</Box>
<Box className="textEllipsis" color={'myGray.400'} fontSize={'sm'}>
{item.systemPrompt || '这个 应用 没有设置提示词~'}
</Box>
</Box>
</Flex>
</Box>

View File

@@ -43,35 +43,6 @@ const QuoteModal = ({
isLoading
} = useQuery(['getHistoryQuote'], () => getHistoryQuote({ historyId, chatId }));
/**
* click edit, get new kbDataItem
*/
const onclickEdit = useCallback(
async (item: QuoteItemType) => {
try {
setIsLoading(true);
const data = (await getKbDataItemById(item.id)) as QuoteItemType;
if (!data) {
throw new Error('该数据已被删除');
}
setEditDataItem({
dataId: data.id,
q: data.q,
a: data.a
});
} catch (err) {
toast({
status: 'warning',
title: getErrText(err)
});
}
setIsLoading(false);
},
[setIsLoading, toast]
);
/**
* update kbData, update mongo status and reload quotes
*/
@@ -98,6 +69,36 @@ const QuoteModal = ({
[chatId, historyId, refetch, setIsLoading, toast]
);
/**
* click edit, get new kbDataItem
*/
const onclickEdit = useCallback(
async (item: QuoteItemType) => {
try {
setIsLoading(true);
const data = (await getKbDataItemById(item.id)) as QuoteItemType;
if (!data) {
updateQuoteStatus(item.id, '已删除');
throw new Error('该数据已被删除');
}
setEditDataItem({
dataId: data.id,
q: data.q,
a: data.a
});
} catch (err) {
toast({
status: 'warning',
title: getErrText(err)
});
}
setIsLoading(false);
},
[setIsLoading, toast, updateQuoteStatus]
);
return (
<>
<Modal isOpen={true} onClose={onClose}>

View File

@@ -39,6 +39,7 @@ const PcSliderBar = ({
const { isPc } = useGlobalStore();
const ContextMenuRef = useRef(null);
const onclickContext = useRef(false);
const [contextMenuData, setContextMenuData] = useState<{
left: number;
@@ -51,10 +52,16 @@ const PcSliderBar = ({
// close contextMenu
useOutsideClick({
ref: ContextMenuRef,
handler: () =>
handler: () => {
setTimeout(() => {
setContextMenuData(undefined);
})
if (contextMenuData && !onclickContext.current) {
setContextMenuData(undefined);
}
}, 10);
setTimeout(() => {
onclickContext.current = false;
}, 10);
}
});
const onclickContextMenu = useCallback(
@@ -62,10 +69,11 @@ const PcSliderBar = ({
e.preventDefault(); // 阻止默认右键菜单
if (!isPc) return;
onclickContext.current = true;
setContextMenuData({
left: e.clientX + 15,
top: e.clientY + 10,
left: e.clientX,
top: e.clientY,
history
});
},

View File

@@ -43,13 +43,13 @@ import { fileDownload } from '@/utils/file';
import { htmlTemplate } from '@/constants/common';
import { useUserStore } from '@/store/user';
import Loading from '@/components/Loading';
import Markdown from '@/components/Markdown';
import SideBar from '@/components/SideBar';
import Avatar from '@/components/Avatar';
import Empty from './components/Empty';
import QuoteModal from './components/QuoteModal';
import { HUMAN_ICON } from '@/constants/chat';
const Markdown = dynamic(async () => await import('@/components/Markdown'));
const PhoneSliderBar = dynamic(() => import('./components/PhoneSliderBar'), {
ssr: false
});
@@ -59,6 +59,7 @@ const History = dynamic(() => import('./components/History'), {
});
import styles from './index.module.scss';
import { adaptChatItem_openAI } from '@/utils/plugin/openai';
const textareaMinH = '22px';
@@ -78,7 +79,6 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
const [showHistoryQuote, setShowHistoryQuote] = useState<string>();
const [showSystemPrompt, setShowSystemPrompt] = useState('');
const [messageContextMenuData, setMessageContextMenuData] = useState<{
// message messageContextMenuData
left: number;
top: number;
message: ChatSiteItemType;
@@ -171,19 +171,15 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
controller.current = abortSignal;
isLeavePage.current = false;
const prompt: ChatItemType[] = prompts.map((item) => ({
_id: item._id,
obj: item.obj,
value: item.value
}));
const messages = adaptChatItem_openAI({ messages: prompts, reserveId: true });
// 流请求,获取数据
const { newChatId, quoteLen, systemPrompt } = await streamFetch({
url: '/api/chat/chat',
const { newChatId, quoteLen } = await streamFetch({
data: {
prompt,
messages,
chatId,
modelId
appId: modelId,
model: ''
},
onMessage: (text: string) => {
setChatData((state) => ({
@@ -223,7 +219,7 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
...item,
status: 'finish',
quoteLen,
systemPrompt
systemPrompt: chatData.systemPrompt
};
})
}));
@@ -238,6 +234,7 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
[
chatId,
modelId,
chatData.systemPrompt,
setChatData,
loadHistory,
loadMyModels,
@@ -329,8 +326,8 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
// 删除一句话
const delChatRecord = useCallback(
async (index: number, historyId: string) => {
if (!messageContextMenuData) return;
async (index: number, historyId?: string) => {
if (!messageContextMenuData || !historyId) return;
setIsLoading(true);
try {
@@ -739,7 +736,6 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
<Markdown
source={item.value}
isChatting={isChatting && index === chatData.history.length - 1}
formatLink
/>
<Flex>
{!!item.systemPrompt && (

View File

@@ -56,6 +56,7 @@ const ShareHistory = dynamic(() => import('./components/ShareHistory'), {
});
import styles from './index.module.scss';
import { adaptChatItem_openAI } from '@/utils/plugin/openai';
const textareaMinH = '22px';
@@ -170,19 +171,15 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
controller.current = abortSignal;
isLeavePage.current = false;
const formatPrompts = prompts.map((item) => ({
obj: item.obj,
value: item.value
}));
const messages = adaptChatItem_openAI({ messages: prompts, reserveId: true });
// 流请求,获取数据
const { responseText } = await streamFetch({
url: '/api/chat/shareChat/chat',
data: {
prompts: formatPrompts.slice(-shareChatData.maxContext - 1, -1),
messages: messages.slice(-shareChatData.maxContext - 1, -1),
password,
shareId,
historyId
model: ''
},
onMessage: (text: string) => {
setShareChatData((state) => ({
@@ -226,7 +223,7 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
setShareChatHistory({
historyId,
shareId,
title: formatPrompts[formatPrompts.length - 2].value,
title: prompts[prompts.length - 2].value,
latestChat: responseText,
chats: responseHistory
});
@@ -235,7 +232,7 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
{
type: 'shareChatFinish',
data: {
question: formatPrompts[0].value,
question: prompts[prompts.length - 2].value,
answer: responseText
}
},
@@ -662,7 +659,6 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
<Markdown
source={item.value}
isChatting={isChatting && index === shareChatData.history.length - 1}
formatLink
/>
</Card>
</Box>

View File

@@ -163,7 +163,7 @@ const Home = () => {
position={'absolute'}
userSelect={'none'}
>
<Image src="/icon/logo.png" w={['70px', '120px']} h={['70px', '120px']} alt={''}></Image>
<Image src="/icon/logo2.png" w={['70px', '120px']} h={['70px', '120px']} alt={''}></Image>
<Box
className={styles.textlg}
fontWeight={'bold'}

View File

@@ -21,7 +21,7 @@ import {
delOneKbDataByDataId,
getTrainingData
} from '@/api/plugins/kb';
import { DeleteIcon } from '@chakra-ui/icons';
import { DeleteIcon, RepeatIcon } from '@chakra-ui/icons';
import { fileDownload } from '@/utils/file';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useToast } from '@/hooks/useToast';
@@ -29,6 +29,7 @@ import Papa from 'papaparse';
import dynamic from 'next/dynamic';
import InputModal, { FormData as InputDataType } from './InputDataModal';
import { debounce } from 'lodash';
import { getErrText } from '@/utils/tools';
const SelectFileModal = dynamic(() => import('./SelectFileModal'));
const SelectCsvModal = dynamic(() => import('./SelectCsvModal'));
@@ -37,6 +38,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
const lastSearch = useRef('');
const [searchText, setSearchText] = useState('');
const { toast } = useToast();
const [isDeleting, setIsDeleting] = useState(false);
const {
data: kbDataList,
@@ -93,7 +95,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
onSuccess(res) {
try {
const text = Papa.unparse({
fields: ['question', 'answer'],
fields: ['question', 'answer', 'source'],
data: res
});
fileDownload({
@@ -144,6 +146,18 @@ const DataCard = ({ kbId }: { kbId: string }) => {
: {total}
</Box>
<Box>
<IconButton
icon={<RepeatIcon />}
aria-label={'refresh'}
variant={'base'}
isLoading={isLoading}
mr={[2, 4]}
size={'sm'}
onClick={() => {
refetchData(pageNum);
getTrainingData({ kbId, init: true });
}}
/>
<Button
variant={'base'}
mr={2}
@@ -219,7 +233,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
pt={3}
userSelect={'none'}
boxShadow={'none'}
_hover={{ boxShadow: 'lg', '& .delete': { display: 'block' } }}
_hover={{ boxShadow: 'lg', '& .delete': { display: 'flex' } }}
border={'1px solid '}
borderColor={'myGray.200'}
onClick={() =>
@@ -249,19 +263,28 @@ const DataCard = ({ kbId }: { kbId: string }) => {
</Box>
<IconButton
className="delete"
display={['block', 'none']}
display={['flex', 'none']}
icon={<DeleteIcon />}
variant={'base'}
colorScheme={'gray'}
aria-label={'delete'}
size={'xs'}
borderRadius={'md'}
lineHeight={1}
_hover={{ color: 'red.600' }}
isLoading={isDeleting}
onClick={async (e) => {
e.stopPropagation();
await delOneKbDataByDataId(item.id);
refetchData(pageNum);
try {
setIsDeleting(true);
await delOneKbDataByDataId(item.id);
refetchData(pageNum);
} catch (error) {
toast({
title: getErrText(error),
status: 'error'
});
}
setIsDeleting(false);
}}
/>
</Flex>

View File

@@ -36,10 +36,11 @@ const Detail = ({ kbId }: { kbId: string }) => {
});
const { reset } = form;
useQuery([kbId, myKbList], () => getKbDetail(kbId), {
useQuery([kbId], () => getKbDetail(kbId), {
onSuccess(res) {
kbId && setLastKbId(kbId);
if (res) {
setCurrentTab(TabEnum.data);
reset(res);
BasicInfo.current?.initInput?.(res.tags);
}

View File

@@ -114,12 +114,14 @@ const Info = (
const file = e[0];
if (!file) return;
try {
const base64 = await compressImg({
const src = await compressImg({
file,
maxW: 100,
maxH: 100
});
setValue('avatar', base64);
setValue('avatar', src);
setRefresh((state) => !state);
} catch (err: any) {
toast({

View File

@@ -1,4 +1,4 @@
import React, { useState, useCallback } from 'react';
import React, { useState, useCallback, useRef } from 'react';
import {
Box,
Flex,
@@ -24,24 +24,10 @@ import { TrainingModeEnum } from '@/constants/plugin';
import { getErrText } from '@/utils/tools';
import { ChatModelMap, OpenAiChatEnum, embeddingPrice } from '@/constants/model';
import { formatPrice } from '@/utils/user';
import MySlider from '@/components/Slider';
const fileExtension = '.txt,.doc,.docx,.pdf,.md';
const modeMap = {
[TrainingModeEnum.qa]: {
maxLen: 2600,
slideLen: 700,
price: ChatModelMap[OpenAiChatEnum.GPT35].price,
isPrompt: true
},
[TrainingModeEnum.index]: {
maxLen: 700,
slideLen: 300,
price: embeddingPrice,
isPrompt: false
}
};
const SelectFileModal = ({
onClose,
onSuccess,
@@ -51,6 +37,16 @@ const SelectFileModal = ({
onSuccess: () => void;
kbId: string;
}) => {
const [modeMap, setModeMap] = useState({
[TrainingModeEnum.qa]: {
maxLen: 8000,
price: ChatModelMap[OpenAiChatEnum.GPT3516k].price
},
[TrainingModeEnum.index]: {
maxLen: 600,
price: embeddingPrice
}
});
const [btnLoading, setBtnLoading] = useState(false);
const { toast } = useToast();
const [prompt, setPrompt] = useState('');
@@ -200,7 +196,7 @@ const SelectFileModal = ({
});
}
setBtnLoading(false);
}, [files, mode, mutate, openConfirm, toast]);
}, [files, mode, modeMap, mutate, openConfirm, toast]);
return (
<Modal isOpen={true} onClose={onClose} isCentered>
@@ -244,19 +240,52 @@ const SelectFileModal = ({
/>
</Flex>
{/* 内容介绍 */}
{modeMap[mode].isPrompt && (
<Flex w={'100%'} px={5} alignItems={'center'} mt={4}>
<Box flex={'0 0 70px'} mr={2}>
</Box>
<Input
placeholder="提示词,例如: Laf的介绍/关于gpt4的论文/一段长文本"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
size={'sm'}
/>
</Flex>
)}
<Flex w={'100%'} px={5} alignItems={'center'} mt={4}>
{mode === TrainingModeEnum.qa && (
<>
<Box flex={'0 0 70px'} mr={2}>
</Box>
<Input
placeholder="提示词,例如: Laf的介绍/关于gpt4的论文/一段长文本"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
size={'sm'}
/>
</>
)}
{/* chunk size */}
{mode === TrainingModeEnum.index && (
<Flex mt={5}>
<Box w={['70px']} flexShrink={0}>
</Box>
<Box flex={1} ml={'10px'}>
<MySlider
markList={[
{ label: '300', value: 300 },
{ label: '1000', value: 1000 }
]}
width={['100%', '260px']}
min={300}
max={1000}
step={50}
activeVal={modeMap[TrainingModeEnum.index].maxLen}
setVal={(val) => {
setModeMap((state) => ({
...state,
[TrainingModeEnum.index]: {
maxLen: val,
price: embeddingPrice
}
}));
}}
/>
</Box>
</Flex>
)}
</Flex>
{/* 文本内容 */}
<Box flex={'1 0 0'} px={5} h={0} w={'100%'} overflowY={'auto'} mt={4}>
{files.slice(0, 100).map((item, i) => (

Some files were not shown because too many files have changed in this diff Show More