Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
00b90f07
authored
Mar 18, 2023
by
Archer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 模型介绍和温度调整。完善聊天页提示
parent
1c364eca
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
621 additions
and
320 deletions
+621
-320
src/api/model.ts
+4
-4
src/api/response/chat.d.ts
+1
-0
src/components/Markdown/index.module.scss
+1
-1
src/components/Markdown/index.tsx
+1
-1
src/components/Slider/index.tsx
+82
-0
src/constants/common.ts
+22
-0
src/constants/model.ts
+56
-16
src/pages/api/chat/chatGpt.ts
+12
-2
src/pages/api/chat/gpt3.ts
+12
-2
src/pages/api/chat/init.ts
+1
-0
src/pages/api/model/create.ts
+17
-6
src/pages/api/model/detail.tsx
+2
-2
src/pages/api/model/getTrainings.ts
+1
-1
src/pages/api/model/putTrainStatus.ts
+2
-2
src/pages/api/model/train.ts
+2
-2
src/pages/api/model/update.ts
+5
-2
src/pages/chat/components/Empty.tsx
+34
-13
src/pages/chat/components/SlideBar.tsx
+83
-55
src/pages/chat/index.tsx
+7
-2
src/pages/model/components/CreateModel.tsx
+5
-5
src/pages/model/components/ModelEditForm.tsx
+97
-91
src/pages/model/components/ModelPhoneList.tsx
+2
-2
src/pages/model/components/ModelTable.tsx
+6
-6
src/pages/model/components/Training.tsx
+2
-2
src/pages/model/detail.tsx
+117
-41
src/pages/model/list.tsx
+2
-2
src/service/models/chat.ts
+15
-12
src/service/models/model.ts
+16
-4
src/store/user.ts
+4
-4
src/types/chat.d.ts
+0
-2
src/types/model.d.ts
+5
-35
src/types/mongoSchema.d.ts
+5
-3
No files found.
src/api/model.ts
View file @
00b90f07
import
{
GET
,
POST
,
DELETE
,
PUT
}
from
'./request'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
{
ModelUpdateParams
}
from
'@/types/model'
;
import
{
TrainingItemType
}
from
'../types/training'
;
export
const
getMyModels
=
()
=>
GET
<
Model
Type
[]
>
(
'/model/list'
);
export
const
getMyModels
=
()
=>
GET
<
Model
Schema
[]
>
(
'/model/list'
);
export
const
postCreateModel
=
(
data
:
{
name
:
string
;
serviceModelName
:
string
})
=>
POST
<
Model
Type
>
(
'/model/create'
,
data
);
POST
<
Model
Schema
>
(
'/model/create'
,
data
);
export
const
delModelById
=
(
id
:
string
)
=>
DELETE
(
`/model/del?modelId=
${
id
}
`
);
export
const
getModelById
=
(
id
:
string
)
=>
GET
<
Model
Type
>
(
`/model/detail?modelId=
${
id
}
`
);
export
const
getModelById
=
(
id
:
string
)
=>
GET
<
Model
Schema
>
(
`/model/detail?modelId=
${
id
}
`
);
export
const
putModelById
=
(
id
:
string
,
data
:
ModelUpdateParams
)
=>
PUT
(
`/model/update?modelId=
${
id
}
`
,
data
);
...
...
src/api/response/chat.d.ts
View file @
00b90f07
...
...
@@ -6,6 +6,7 @@ export type InitChatResponse = {
modelId
:
string
;
name
:
string
;
avatar
:
string
;
intro
:
string
;
secret
:
ModelSchema
.
secret
;
chatModel
:
ModelSchema
.
service
.
ChatModel
;
// 模型名
history
:
ChatItemType
[];
...
...
src/components/Markdown/index.module.scss
View file @
00b90f07
...
...
@@ -172,7 +172,7 @@
}
.markdown
ul
,
.markdown
ol
{
padding-left
:
30px
;
padding-left
:
1em
;
}
.markdown
ul
.no-list
,
.markdown
ol
.no-list
{
...
...
src/components/Markdown/index.tsx
View file @
00b90f07
...
...
@@ -12,7 +12,7 @@ import 'katex/dist/katex.min.css';
import
styles
from
'./index.module.scss'
;
import
{
codeLight
}
from
'./codeLight'
;
const
Markdown
=
({
source
,
isChatting
}:
{
source
:
string
;
isChatting
:
boolean
})
=>
{
const
Markdown
=
({
source
,
isChatting
=
false
}:
{
source
:
string
;
isChatting
?
:
boolean
})
=>
{
const
formatSource
=
useMemo
(()
=>
source
,
[
source
]);
const
{
copyData
}
=
useCopyData
();
...
...
src/components/Slider/index.tsx
0 → 100644
View file @
00b90f07
import
React
,
{
useMemo
}
from
'react'
;
import
{
Slider
,
SliderTrack
,
SliderFilledTrack
,
SliderThumb
,
SliderMark
,
Box
}
from
'@chakra-ui/react'
;
const
MySlider
=
({
markList
,
setVal
,
activeVal
,
max
=
100
,
min
=
0
,
step
=
1
}:
{
markList
:
{
label
:
string
|
number
;
value
:
number
;
}[];
activeVal
?:
number
;
setVal
:
(
index
:
number
)
=>
void
;
max
?:
number
;
min
?:
number
;
step
?:
number
;
})
=>
{
const
startEndPointStyle
=
{
content
:
'""'
,
borderRadius
:
'10px'
,
width
:
'10px'
,
height
:
'10px'
,
backgroundColor
:
'#ffffff'
,
border
:
'2px solid #D7DBE2'
,
position
:
'absolute'
,
zIndex
:
1
,
top
:
0
,
transform
:
'translateY(-3px)'
};
const
value
=
useMemo
(()
=>
{
const
index
=
markList
.
findIndex
((
item
)
=>
item
.
value
===
activeVal
);
return
index
>
-
1
?
index
:
0
;
},
[
activeVal
,
markList
]);
return
(
<
Slider
max=
{
max
}
min=
{
min
}
step=
{
step
}
size=
{
'lg'
}
value=
{
value
}
onChange=
{
setVal
}
>
{
markList
.
map
((
item
,
i
)
=>
(
<
SliderMark
key=
{
item
.
value
}
value=
{
i
}
mt=
{
3
}
fontSize=
{
'sm'
}
transform=
{
'translateX(-50%)'
}
{
...
(
activeVal
===
item
.
value
?
{
color
:
'
blue
.
500',
fontWeight
:
'
bold
'
}
:
{})}
>
<
Box
px=
{
3
}
cursor=
{
'pointer'
}
>
{
item
.
label
}
</
Box
>
</
SliderMark
>
))
}
<
SliderTrack
bg=
{
'#EAEDF3'
}
overflow=
{
'visible'
}
h=
{
'4px'
}
_before=
{
{
...
startEndPointStyle
,
left
:
'-5px'
}
}
_after=
{
{
...
startEndPointStyle
,
right
:
'-5px'
}
}
>
<
SliderFilledTrack
/>
</
SliderTrack
>
<
SliderThumb
border=
{
'2.5px solid'
}
borderColor=
{
'blue.500'
}
></
SliderThumb
>
</
Slider
>
);
};
export
default
MySlider
;
src/constants/common.ts
View file @
00b90f07
...
...
@@ -39,3 +39,25 @@ export const introPage = `
### 其他问题
还有其他问题,可以加我 wx: YNyiqi,拉个交流群大家一起聊聊。
`
;
export
const
chatProblem
=
`
**代理出错**
服务器代理不稳定,可以过一会儿再尝试。
**API key 问题**
请把 openai 的 API key 粘贴到账号里再创建对话。如果是使用分享的对话,不需要填写 API key。
`
;
export
const
versionIntro
=
`
* 分享对话:使用的是分享者的 Api Key 生成一个对话窗口进行分享。
* 分享空白对话:为该模型创建一个空白的聊天分享出去。
* 分享当前对话:会把当前聊天的内容也分享出去,但是要注意不要多个人同时用一个聊天内容。
* 增加模型介绍:可以在模型编辑页添加对模型的介绍,方便提示模型的范围。
* 温度调整:可以在模型编辑页调整模型温度,以便适应不同类型的对话。例如,翻译类的模型可以把温度拉低;创作类的模型可以把温度拉高。
`
;
export
const
shareHint
=
`
你正准备分享对话,请确保分享链接不会滥用,因为它是使用的是你的 API key。
* 分享空白对话:为该模型创建一个空白的聊天分享出去。
* 分享当前对话:会把当前聊天的内容也分享出去,但是要注意不要多个人同时用一个聊天内容。
`
;
src/constants/model.ts
View file @
00b90f07
import
type
{
ServiceName
}
from
'@/types/mongoSchema'
;
import
{
ModelSchema
}
from
'../types/mongoSchema'
;
export
enum
ChatModelNameEnum
{
GPT35
=
'gpt-3.5-turbo'
,
GPT3
=
'text-davinci-003'
}
export
const
OpenAiList
=
[
{
name
:
'chatGPT'
,
model
:
ChatModelNameEnum
.
GPT35
,
trainName
:
'turbo'
,
canTraining
:
false
,
maxToken
:
4060
},
{
name
:
'GPT3'
,
model
:
ChatModelNameEnum
.
GPT3
,
trainName
:
'davinci'
,
canTraining
:
true
,
maxToken
:
4060
}
];
export
type
ModelConstantsData
=
{
name
:
string
;
model
:
`
${
ChatModelNameEnum
}
`
;
trainName
:
string
;
// 空字符串代表不能训练
maxToken
:
number
;
maxTemperature
:
number
;
};
export
const
ModelList
:
Record
<
ServiceName
,
ModelConstantsData
[]
>
=
{
openai
:
[
{
name
:
'chatGPT'
,
model
:
ChatModelNameEnum
.
GPT35
,
trainName
:
'turbo'
,
maxToken
:
4000
,
maxTemperature
:
2
},
{
name
:
'GPT3'
,
model
:
ChatModelNameEnum
.
GPT3
,
trainName
:
'davinci'
,
maxToken
:
4000
,
maxTemperature
:
2
}
]
};
export
enum
TrainingStatusEnum
{
pending
=
'pending'
,
...
...
@@ -51,3 +65,29 @@ export const formatModelStatus = {
text
:
'已关闭'
}
};
export
const
defaultModel
:
ModelSchema
=
{
_id
:
''
,
userId
:
''
,
name
:
''
,
avatar
:
''
,
status
:
ModelStatusEnum
.
pending
,
updateTime
:
Date
.
now
(),
trainingTimes
:
0
,
systemPrompt
:
''
,
intro
:
''
,
temperature
:
5
,
service
:
{
company
:
'openai'
,
trainId
:
''
,
chatModel
:
ChatModelNameEnum
.
GPT35
,
modelName
:
ChatModelNameEnum
.
GPT35
},
security
:
{
domain
:
[
'*'
],
contextMaxLen
:
1
,
contentMaxLen
:
1
,
expiredTime
:
9999
,
maxLoadAmount
:
1
}
};
src/pages/api/chat/chatGpt.ts
View file @
00b90f07
...
...
@@ -8,6 +8,7 @@ import { ChatItemType } from '@/types/chat';
import
{
jsonRes
}
from
'@/service/response'
;
import
type
{
ModelSchema
}
from
'@/types/mongoSchema'
;
import
{
PassThrough
}
from
'stream'
;
import
{
ModelList
}
from
'@/constants/model'
;
/* 发送提示词 */
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
)
{
...
...
@@ -56,6 +57,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
}
// 计算温度
const
modelConstantsData
=
ModelList
[
'openai'
].
find
(
(
item
)
=>
item
.
model
===
model
.
service
.
modelName
);
if
(
!
modelConstantsData
)
{
throw
new
Error
(
'模型异常'
);
}
const
temperature
=
modelConstantsData
.
maxTemperature
*
(
model
.
temperature
/
10
);
// 获取 chatAPI
const
chatAPI
=
getOpenAIApi
(
userApiKey
);
let
startTime
=
Date
.
now
();
...
...
@@ -63,8 +73,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const
chatResponse
=
await
chatAPI
.
createChatCompletion
(
{
model
:
model
.
service
.
chatModel
,
temperature
:
1
,
// max_tokens: model.security.contentMaxL
en,
temperature
:
temperature
,
max_tokens
:
modelConstantsData
.
maxTok
en
,
messages
:
formatPrompts
,
stream
:
true
},
...
...
src/pages/api/chat/gpt3.ts
View file @
00b90f07
...
...
@@ -5,6 +5,7 @@ import { connectToDatabase } from '@/service/mongo';
import
{
getOpenAIApi
,
authChat
}
from
'@/service/utils/chat'
;
import
{
ChatItemType
}
from
'@/types/chat'
;
import
{
httpsAgent
}
from
'@/service/utils/tools'
;
import
{
ModelList
}
from
'@/constants/model'
;
/* 发送提示词 */
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
)
{
...
...
@@ -27,13 +28,22 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
// prompt处理
const
formatPrompt
=
prompt
.
map
((
item
)
=>
`
${
item
.
value
}
\n\n###\n\n`
).
join
(
''
);
// 计算温度
const
modelConstantsData
=
ModelList
[
'openai'
].
find
(
(
item
)
=>
item
.
model
===
model
.
service
.
modelName
);
if
(
!
modelConstantsData
)
{
throw
new
Error
(
'模型异常'
);
}
const
temperature
=
modelConstantsData
.
maxTemperature
*
(
model
.
temperature
/
10
);
// 发送请求
const
response
=
await
chatAPI
.
createCompletion
(
{
model
:
model
.
service
.
modelName
,
prompt
:
formatPrompt
,
temperature
:
0.5
,
max_tokens
:
model
.
security
.
contentMaxL
en
,
temperature
:
temperature
,
max_tokens
:
model
ConstantsData
.
maxTok
en
,
top_p
:
1
,
frequency_penalty
:
0
,
presence_penalty
:
0.6
,
...
...
src/pages/api/chat/init.ts
View file @
00b90f07
...
...
@@ -47,6 +47,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
modelId
:
model
.
_id
,
name
:
model
.
name
,
avatar
:
model
.
avatar
,
intro
:
model
.
intro
,
secret
:
model
.
security
,
chatModel
:
model
.
service
.
chatModel
,
history
:
chat
.
content
...
...
src/pages/api/model/create.ts
View file @
00b90f07
...
...
@@ -3,12 +3,21 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import
{
jsonRes
}
from
'@/service/response'
;
import
{
connectToDatabase
}
from
'@/service/mongo'
;
import
{
authToken
}
from
'@/service/utils/tools'
;
import
{
ModelStatusEnum
,
OpenAiList
}
from
'@/constants/model'
;
import
{
ModelStatusEnum
,
ModelList
,
ChatModelNameEnum
}
from
'@/constants/model'
;
import
type
{
ServiceName
}
from
'@/types/mongoSchema'
;
import
{
Model
}
from
'@/service/models/model'
;
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
<
any
>
)
{
try
{
const
{
name
,
serviceModelName
,
serviceModelCompany
=
'openai'
}
=
req
.
body
;
const
{
name
,
serviceModelName
,
serviceModelCompany
=
'openai'
}
=
req
.
body
as
{
name
:
string
;
serviceModelName
:
`
${
ChatModelNameEnum
}
`
;
serviceModelCompany
:
ServiceName
;
};
const
{
authorization
}
=
req
.
headers
;
if
(
!
authorization
)
{
...
...
@@ -22,10 +31,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
// 凭证校验
const
userId
=
await
authToken
(
authorization
);
const
modelItem
=
OpenAiList
.
find
((
item
)
=>
item
.
model
===
serviceModelName
);
const
modelItem
=
ModelList
[
serviceModelCompany
].
find
(
(
item
)
=>
item
.
model
===
serviceModelName
);
if
(
!
modelItem
)
{
throw
new
Error
(
'模型
错误
'
);
throw
new
Error
(
'模型
不存在
'
);
}
await
connectToDatabase
();
...
...
@@ -43,8 +54,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const
authCount
=
await
Model
.
countDocuments
({
userId
});
if
(
authCount
>=
1
0
)
{
throw
new
Error
(
'上限
1
0 个模型'
);
if
(
authCount
>=
2
0
)
{
throw
new
Error
(
'上限
2
0 个模型'
);
}
// 创建模型
...
...
src/pages/api/model/detail.tsx
View file @
00b90f07
...
...
@@ -3,7 +3,7 @@ import { jsonRes } from '@/service/response';
import
{
connectToDatabase
}
from
'@/service/mongo'
;
import
{
authToken
}
from
'@/service/utils/tools'
;
import
{
Model
}
from
'@/service/models/model'
;
import
{
ModelType
}
from
'@/types/model
'
;
import
type
{
ModelSchema
}
from
'@/types/mongoSchema
'
;
/* 获取我的模型 */
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
<
any
>
)
{
...
...
@@ -26,7 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
await
connectToDatabase
();
// 根据 userId 获取模型信息
const
model
:
ModelType
|
null
=
await
Model
.
findOne
({
const
model
=
await
Model
.
findOne
<
ModelSchema
>
({
userId
,
_id
:
modelId
});
...
...
src/pages/api/model/getTrainings.ts
View file @
00b90f07
...
...
@@ -6,7 +6,7 @@ import formidable from 'formidable';
import
{
authToken
,
getUserOpenaiKey
}
from
'@/service/utils/tools'
;
import
{
join
}
from
'path'
;
import
fs
from
'fs'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
type
{
OpenAIApi
}
from
'openai'
;
import
{
ModelStatusEnum
,
TrainingStatusEnum
}
from
'@/constants/model'
;
import
{
httpsAgent
}
from
'@/service/utils/tools'
;
...
...
src/pages/api/model/putTrainStatus.ts
View file @
00b90f07
...
...
@@ -3,7 +3,7 @@ import { jsonRes } from '@/service/response';
import
{
connectToDatabase
,
Model
,
Training
}
from
'@/service/mongo'
;
import
{
getOpenAIApi
}
from
'@/service/utils/chat'
;
import
{
authToken
,
getUserOpenaiKey
}
from
'@/service/utils/tools'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
{
TrainingItemType
}
from
'@/types/training'
;
import
{
ModelStatusEnum
,
TrainingStatusEnum
}
from
'@/constants/model'
;
import
{
OpenAiTuneStatusEnum
}
from
'@/service/constants/training'
;
...
...
@@ -26,7 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await
connectToDatabase
();
// 获取模型
const
model
:
ModelType
|
null
=
await
Model
.
findById
(
modelId
);
const
model
=
await
Model
.
findById
<
ModelSchema
>
(
modelId
);
if
(
!
model
||
model
.
status
!==
'training'
)
{
throw
new
Error
(
'模型不在训练中'
);
...
...
src/pages/api/model/train.ts
View file @
00b90f07
...
...
@@ -7,7 +7,7 @@ import formidable from 'formidable';
import
{
authToken
,
getUserOpenaiKey
}
from
'@/service/utils/tools'
;
import
{
join
}
from
'path'
;
import
fs
from
'fs'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
type
{
OpenAIApi
}
from
'openai'
;
import
{
ModelStatusEnum
,
TrainingStatusEnum
}
from
'@/constants/model'
;
import
{
httpsAgent
}
from
'@/service/utils/tools'
;
...
...
@@ -38,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await
connectToDatabase
();
// 获取模型的状态
const
model
:
ModelType
|
null
=
await
Model
.
findById
(
modelId
);
const
model
=
await
Model
.
findById
<
ModelSchema
>
(
modelId
);
if
(
!
model
||
model
.
status
!==
'running'
)
{
throw
new
Error
(
'模型正忙'
);
...
...
src/pages/api/model/update.ts
View file @
00b90f07
...
...
@@ -8,7 +8,8 @@ import type { ModelUpdateParams } from '@/types/model';
/* 获取我的模型 */
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
<
any
>
)
{
try
{
const
{
name
,
service
,
security
,
systemPrompt
}
=
req
.
body
as
ModelUpdateParams
;
const
{
name
,
service
,
security
,
systemPrompt
,
intro
,
temperature
}
=
req
.
body
as
ModelUpdateParams
;
const
{
modelId
}
=
req
.
query
as
{
modelId
:
string
};
const
{
authorization
}
=
req
.
headers
;
...
...
@@ -33,8 +34,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
},
{
name
,
service
,
systemPrompt
,
intro
,
temperature
,
service
,
security
}
);
...
...
src/pages/chat/components/Empty.tsx
View file @
00b90f07
import
React
from
'react'
;
import
{
Card
,
Flex
,
Box
}
from
'@chakra-ui/react'
;
import
{
Card
,
Box
,
Mark
}
from
'@chakra-ui/react'
;
import
{
versionIntro
,
chatProblem
}
from
'@/constants/common'
;
import
Markdown
from
'@/components/Markdown'
;
const
Empty
=
()
=>
{
const
Empty
=
({
intro
}:
{
intro
:
string
})
=>
{
const
Header
=
({
children
}:
{
children
:
string
})
=>
(
<
Box
fontSize=
{
'lg'
}
fontWeight=
{
'bold'
}
textAlign=
{
'center'
}
pb=
{
2
}
>
{
children
}
</
Box
>
);
return
(
<
Flex
h=
{
'100%'
}
alignItems=
{
'center'
}
justifyContent=
{
'center'
}
>
<
Card
p=
{
5
}
w=
{
'70%'
}
>
<
Box
fontSize=
{
'xl'
}
fontWeight=
{
'bold'
}
textAlign=
{
'center'
}
pb=
{
2
}
>
Fast Gpt version1.3
</
Box
>
<
Box
minH=
{
'100%'
}
w=
{
'85%'
}
maxW=
{
'600px'
}
m=
{
'auto'
}
py=
{
'5vh'
}
alignItems=
{
'center'
}
justifyContent=
{
'center'
}
>
{
!!
intro
&&
(
<
Card
p=
{
4
}
mb=
{
10
}
>
<
Header
>
模型介绍
</
Header
>
<
Box
>
{
intro
}
</
Box
>
</
Card
>
)
}
<
Card
p=
{
4
}
mb=
{
10
}
>
<
Header
>
常见问题
</
Header
>
<
Markdown
source=
{
chatProblem
}
/>
</
Card
>
{
/* version intro */
}
<
Card
p=
{
4
}
>
<
Header
>
Fast Gpt version1.4
</
Header
>
<
Box
>
更新了聊天的数据结构,
如果出现问题,请手动删除左侧旧的历史记录,并重新从模型页生成对话框进入。
聊天的数据结构发生了比较大的改动。
如果出现问题,请手动删除左侧旧的历史记录,并重新从模型页生成对话框进入。
</
Box
>
<
Box
>
分享聊天使用的是分享者的 Api Key 进行收费,请确认分享安全
</
Box
>
<
br
/>
<
Box
>
分享空白聊天,会分享一个该模型的空白聊天页
</
Box
>
<
br
/>
<
Box
>
分享当前聊天,会把当前聊天的内容分享出去,请注意不会多人同时使用一个对话框
</
Box
>
<
Markdown
source=
{
versionIntro
}
/
>
</
Card
>
</
Fle
x
>
</
Bo
x
>
);
};
...
...
src/pages/chat/components/SlideBar.tsx
View file @
00b90f07
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
{
Box
,
Button
}
from
'@chakra-ui/react'
;
import
{
AddIcon
,
ChatIcon
,
EditIcon
,
DeleteIcon
}
from
'@chakra-ui/icons'
;
import
{
AddIcon
,
ChatIcon
,
DeleteIcon
}
from
'@chakra-ui/icons'
;
import
{
Box
,
Button
,
Accordion
,
AccordionItem
,
AccordionButton
,
...
...
@@ -9,16 +10,26 @@ import {
AccordionIcon
,
Flex
,
Divider
,
IconButton
IconButton
,
Modal
,
ModalOverlay
,
ModalContent
,
ModalHeader
,
ModalFooter
,
ModalBody
,
ModalCloseButton
,
useDisclosure
}
from
'@chakra-ui/react'
;
import
{
useUserStore
}
from
'@/store/user'
;
import
{
useChatStore
}
from
'@/store/chat'
;
import
{
useQuery
}
from
'@tanstack/react-query'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useScreen
}
from
'@/hooks/useScreen'
;
import
{
getToken
}
from
'@/utils/user'
;
import
MyIcon
from
'@/components/Icon'
;
import
{
useCopyData
}
from
'@/utils/tools'
;
import
Markdown
from
'@/components/Markdown'
;
import
{
shareHint
}
from
'@/constants/common'
;
import
{
getChatSiteId
}
from
'@/api/chat'
;
const
SlideBar
=
({
name
,
...
...
@@ -36,10 +47,13 @@ const SlideBar = ({
const
router
=
useRouter
();
const
{
copyData
}
=
useCopyData
();
const
{
myModels
,
getMyModels
}
=
useUserStore
();
const
{
chatHistory
,
removeChatHistoryByWindowId
,
generateChatWindow
,
updateChatHistory
}
=
useChatStore
();
const
{
isSuccess
}
=
useQuery
([
'init'
],
getMyModels
);
const
{
chatHistory
,
removeChatHistoryByWindowId
}
=
useChatStore
();
const
[
hasReady
,
setHasReady
]
=
useState
(
false
);
const
{
isOpen
:
isOpenShare
,
onOpen
:
onOpenShare
,
onClose
:
onCloseShare
}
=
useDisclosure
();
const
{
isSuccess
}
=
useQuery
([
'init'
],
getMyModels
,
{
cacheTime
:
5
*
60
*
1000
});
useEffect
(()
=>
{
setHasReady
(
true
);
...
...
@@ -119,19 +133,8 @@ const SlideBar = ({
{
/* 我的模型 & 历史记录 折叠框*/
}
<
Box
flex=
{
'1 0 0'
}
px=
{
3
}
h=
{
0
}
overflowY=
{
'auto'
}
>
{
isSuccess
?
(
<
Accordion
defaultIndex=
{
[
0
]
}
allowToggle
>
<
AccordionItem
borderTop=
{
0
}
borderBottom=
{
0
}
>
<
AccordionButton
borderRadius=
{
'md'
}
pl=
{
1
}
>
<
Box
as=
"span"
flex=
"1"
textAlign=
"left"
>
历史记录
</
Box
>
<
AccordionIcon
/>
</
AccordionButton
>
<
AccordionPanel
pb=
{
0
}
px=
{
0
}
>
{
hasReady
&&
<
RenderHistory
/>
}
</
AccordionPanel
>
</
AccordionItem
>
<
Accordion
defaultIndex=
{
[
0
]
}
allowToggle
>
{
isSuccess
&&
(
<
AccordionItem
borderTop=
{
0
}
borderBottom=
{
0
}
>
<
AccordionButton
borderRadius=
{
'md'
}
pl=
{
1
}
>
<
Box
as=
"span"
flex=
"1"
textAlign=
"left"
>
...
...
@@ -161,7 +164,7 @@ const SlideBar = ({
:
{})}
onClick=
{
async
()
=>
{
if
(
item
.
name
===
name
)
return
;
router
.
push
(
`/chat?chatId=${await ge
nerateChatWindow
(item._id)}`
);
router
.
push
(
`/chat?chatId=${await ge
tChatSiteId
(item._id)}`
);
onClose
();
}
}
>
...
...
@@ -173,43 +176,25 @@ const SlideBar = ({
))
}
</
AccordionPanel
>
</
AccordionItem
>
</
Accordion
>
)
:
(
<>
<
Box
mb=
{
4
}
textAlign=
{
'center'
}
>
历史记录
</
Box
>
<
RenderHistory
/>
</>
)
}
)
}
<
AccordionItem
borderTop=
{
0
}
borderBottom=
{
0
}
>
<
AccordionButton
borderRadius=
{
'md'
}
pl=
{
1
}
>
<
Box
as=
"span"
flex=
"1"
textAlign=
"left"
>
历史记录
</
Box
>
<
AccordionIcon
/>
</
AccordionButton
>
<
AccordionPanel
pb=
{
0
}
px=
{
0
}
>
{
hasReady
&&
<
RenderHistory
/>
}
</
AccordionPanel
>
</
AccordionItem
>
</
Accordion
>
</
Box
>
<
Divider
my=
{
4
}
/>
<
Box
px=
{
3
}
>
{
/* 分享 */
}
{
getToken
()
&&
(
<
Flex
alignItems=
{
'center'
}
p=
{
2
}
cursor=
{
'pointer'
}
borderRadius=
{
'md'
}
_hover=
{
{
backgroundColor
:
'rgba(255,255,255,0.2)'
}
}
onClick=
{
async
()
=>
{
copyData
(
`${location.origin}/chat?chatId=${await generateChatWindow(modelId)}`
,
'已复制分享链接'
);
}
}
>
<
MyIcon
name=
"share"
fill=
{
'white'
}
w=
{
'16px'
}
h=
{
'16px'
}
mr=
{
4
}
/>
分享空白对话
</
Flex
>
)
}
<
Flex
mt=
{
4
}
alignItems=
{
'center'
}
p=
{
2
}
cursor=
{
'pointer'
}
...
...
@@ -217,14 +202,57 @@ const SlideBar = ({
_hover=
{
{
backgroundColor
:
'rgba(255,255,255,0.2)'
}
}
onClick=
{
async
()
=>
{
copyData
(
`${location.origin}/chat?chatId=${chatId}`
,
'已复制分享链接'
);
onClick=
{
()
=>
{
onOpenShare
();
onClose
();
}
}
>
<
MyIcon
name=
"share"
fill=
{
'white'
}
w=
{
'16px'
}
h=
{
'16px'
}
mr=
{
4
}
/>
分享
当前
对话
分享对话
</
Flex
>
</
Box
>
{
/* 分享提示modal */
}
<
Modal
isOpen=
{
isOpenShare
}
onClose=
{
onCloseShare
}
>
<
ModalOverlay
/>
<
ModalContent
>
<
ModalHeader
>
分享对话
</
ModalHeader
>
<
ModalCloseButton
/>
<
ModalBody
>
<
Markdown
source=
{
shareHint
}
/>
</
ModalBody
>
<
ModalFooter
>
<
Button
colorScheme=
"gray"
variant=
{
'outline'
}
mr=
{
3
}
onClick=
{
onCloseShare
}
>
Close
</
Button
>
{
getToken
()
&&
(
<
Button
variant=
"outline"
mr=
{
3
}
onClick=
{
async
()
=>
{
copyData
(
`${location.origin}/chat?chatId=${await getChatSiteId(modelId)}`
,
'已复制分享链接'
);
onCloseShare
();
}
}
>
分享空白对话
</
Button
>
)
}
<
Button
onClick=
{
()
=>
{
copyData
(
`${location.origin}/chat?chatId=${chatId}`
,
'已复制分享链接'
);
onCloseShare
();
}
}
>
分享当前对话
</
Button
>
</
ModalFooter
>
</
ModalContent
>
</
Modal
>
</
Flex
>
);
};
...
...
src/pages/chat/index.tsx
View file @
00b90f07
...
...
@@ -51,6 +51,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
modelId
:
''
,
name
:
''
,
avatar
:
''
,
intro
:
''
,
secret
:
{},
chatModel
:
''
,
history
:
[],
...
...
@@ -113,7 +114,11 @@ const Chat = ({ chatId }: { chatId: string }) => {
status
:
'finish'
}))
});
scrollToBottom
();
if
(
res
.
history
.
length
>
0
)
{
setTimeout
(()
=>
{
scrollToBottom
();
},
500
);
}
},
onError
(
e
:
any
)
{
toast
({
...
...
@@ -433,7 +438,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
</
Flex
>
</
Box
>
))
}
{
chatData
.
history
.
length
===
0
&&
<
Empty
/>
}
{
chatData
.
history
.
length
===
0
&&
<
Empty
intro=
{
chatData
.
intro
}
/>
}
</
Box
>
{
/* 发送区 */
}
<
Box
...
...
src/pages/model/components/CreateModel.tsx
View file @
00b90f07
...
...
@@ -16,8 +16,8 @@ import {
}
from
'@chakra-ui/react'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
postCreateModel
}
from
'@/api/model'
;
import
{
ModelType
}
from
'@/types/model
'
;
import
{
OpenAi
List
}
from
'@/constants/model'
;
import
type
{
ModelSchema
}
from
'@/types/mongoSchema
'
;
import
{
Model
List
}
from
'@/constants/model'
;
interface
CreateFormType
{
name
:
string
;
...
...
@@ -29,7 +29,7 @@ const CreateModel = ({
onSuccess
}:
{
setCreateModelOpen
:
Dispatch
<
boolean
>
;
onSuccess
:
Dispatch
<
Model
Type
>
;
onSuccess
:
Dispatch
<
Model
Schema
>
;
})
=>
{
const
[
requesting
,
setRequesting
]
=
useState
(
false
);
const
toast
=
useToast
({
...
...
@@ -42,7 +42,7 @@ const CreateModel = ({
formState
:
{
errors
}
}
=
useForm
<
CreateFormType
>
({
defaultValues
:
{
serviceModelName
:
OpenAiList
[
0
].
model
serviceModelName
:
ModelList
[
'openai'
]
[
0
].
model
}
});
...
...
@@ -95,7 +95,7 @@ const CreateModel = ({
required
:
'底层模型不能为空'
})}
>
{
OpenAiList
.
map
((
item
)
=>
(
{
ModelList
[
'openai'
]
.
map
((
item
)
=>
(
<
option
key=
{
item
.
model
}
value=
{
item
.
model
}
>
{
item
.
name
}
</
option
>
...
...
src/pages/model/components/ModelEditForm.tsx
View file @
00b90f07
import
React
,
{
useCallback
,
useEffect
,
useRef
}
from
'react'
;
import
{
Grid
,
Box
,
Card
,
Flex
,
Button
,
FormControl
,
Input
,
Textarea
}
from
'@chakra-ui/react'
;
import
type
{
ModelType
}
from
'@/types/model'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
useToast
}
from
'@/hooks/useToast'
;
import
{
putModelById
}
from
'@/api/model'
;
import
{
useScreen
}
from
'@/hooks/useScreen'
;
import
{
useGlobalStore
}
from
'@/store/global'
;
import
React
,
{
useState
}
from
'react'
;
import
{
Box
,
Card
,
Flex
,
FormControl
,
Input
,
Textarea
,
Slider
,
SliderTrack
,
SliderFilledTrack
,
SliderThumb
,
SliderMark
,
Tooltip
}
from
'@chakra-ui/react'
;
import
{
QuestionOutlineIcon
}
from
'@chakra-ui/icons'
;
import
type
{
ModelSchema
}
from
'@/types/mongoSchema'
;
import
{
UseFormReturn
}
from
'react-hook-form'
;
const
ModelEditForm
=
({
model
}:
{
model
?:
ModelType
})
=>
{
const
isInit
=
useRef
(
false
);
const
{
register
,
handleSubmit
,
reset
,
formState
:
{
errors
}
}
=
useForm
<
ModelType
>
();
const
{
setLoading
}
=
useGlobalStore
();
const
{
toast
}
=
useToast
();
const
{
media
}
=
useScreen
();
const
onclickSave
=
useCallback
(
async
(
data
:
ModelType
)
=>
{
setLoading
(
true
);
try
{
await
putModelById
(
data
.
_id
,
{
name
:
data
.
name
,
systemPrompt
:
data
.
systemPrompt
,
service
:
data
.
service
,
security
:
data
.
security
});
toast
({
title
:
'更新成功'
,
status
:
'success'
});
}
catch
(
err
)
{
console
.
log
(
'error->'
,
err
);
toast
({
title
:
err
as
string
,
status
:
'success'
});
}
setLoading
(
false
);
},
[
setLoading
,
toast
]
);
const
submitError
=
useCallback
(()
=>
{
// deep search message
const
deepSearch
=
(
obj
:
any
):
string
=>
{
if
(
!
obj
)
return
'提交表单错误'
;
if
(
!!
obj
.
message
)
{
return
obj
.
message
;
}
return
deepSearch
(
Object
.
values
(
obj
)[
0
]);
};
toast
({
title
:
deepSearch
(
errors
),
status
:
'error'
,
duration
:
4000
,
isClosable
:
true
});
},
[
errors
,
toast
]);
/* model 只会改变一次 */
useEffect
(()
=>
{
if
(
model
&&
!
isInit
.
current
)
{
reset
(
model
);
isInit
.
current
=
true
;
}
},
[
model
,
reset
]);
const
ModelEditForm
=
({
formHooks
}:
{
formHooks
:
UseFormReturn
<
ModelSchema
>
})
=>
{
const
{
register
,
setValue
,
getValues
}
=
formHooks
;
const
[
refresh
,
setRefresh
]
=
useState
(
false
);
return
(
<
Grid
gridTemplateColumns=
{
media
(
'1fr 1fr'
,
'1fr'
)
}
gridGap=
{
5
}
>
<>
<
Card
p=
{
4
}
>
<
Flex
justifyContent=
{
'space-between'
}
alignItems=
{
'center'
}
>
<
Box
fontWeight=
{
'bold'
}
fontSize=
{
'lg'
}
>
修改模型信息
</
Box
>
<
Button
onClick=
{
handleSubmit
(
onclickSave
,
submitError
)
}
>
保存
</
Button
>
<
Box
fontWeight=
{
'bold'
}
>
基本信息
</
Box
>
</
Flex
>
<
FormControl
mt=
{
5
}
>
<
FormControl
mt=
{
4
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
flex=
{
'0 0 80px'
}
>
展示名称:
</
Box
>
<
Box
flex=
{
'0 0 50px'
}
w=
{
0
}
>
名称:
</
Box
>
<
Input
{
...
register
('
name
',
{
required
:
'展示名称不能为空'
...
...
@@ -88,30 +39,79 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
></
Input
>
</
Flex
>
</
FormControl
>
<
FormControl
mt=
{
5
}
>
<
FormControl
mt=
{
4
}
>
<
Box
mb=
{
1
}
>
介绍:
</
Box
>
<
Textarea
rows=
{
5
}
maxLength=
{
500
}
{
...
register
('
intro
')}
placeholder=
{
'模型的介绍,仅做展示,不影响模型的效果'
}
/>
</
FormControl
>
</
Card
>
<
Card
p=
{
4
}
>
<
Box
fontWeight=
{
'bold'
}
>
模型效果
</
Box
>
<
FormControl
mt=
{
4
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
flex=
{
'0 0 80px'
}
>
对话模型:
</
Box
>
<
Box
>
{
model
?.
service
.
modelName
}
</
Box
>
<
Box
flex=
{
'0 0 80px'
}
w=
{
0
}
>
<
Box
as=
{
'span'
}
mr=
{
2
}
>
温度
</
Box
>
<
Tooltip
label=
{
'温度越高,模型的发散能力越强;温度越低,内容越严谨。'
}
>
<
QuestionOutlineIcon
/>
</
Tooltip
>
</
Box
>
<
Slider
aria
-
label=
"slider-ex-1"
min=
{
1
}
max=
{
10
}
step=
{
1
}
value=
{
getValues
(
'temperature'
)
}
onChange=
{
(
e
)
=>
{
setValue
(
'temperature'
,
e
);
setRefresh
(
!
refresh
);
}
}
>
<
SliderMark
value=
{
getValues
(
'temperature'
)
}
textAlign=
"center"
bg=
"blue.500"
color=
"white"
w=
{
'18px'
}
h=
{
'18px'
}
borderRadius=
{
'100px'
}
fontSize=
{
'xs'
}
transform=
{
'translate(-50%, -200%)'
}
>
{
getValues
(
'temperature'
)
}
</
SliderMark
>
<
SliderTrack
>
<
SliderFilledTrack
/>
</
SliderTrack
>
<
SliderThumb
/>
</
Slider
>
</
Flex
>
</
FormControl
>
<
FormControl
mt=
{
5
}
>
<
Box
mt=
{
4
}
>
<
Box
mb=
{
1
}
>
系统提示词
</
Box
>
<
Textarea
rows=
{
4
}
rows=
{
6
}
maxLength=
{
500
}
{
...
register
('
systemPrompt
')}
placeholder=
{
'模型默认的 prompt 词,
可以通过调整该内容,生成一个限定范围的模型,更方便的去使用。
'
'模型默认的 prompt 词,
通过调整该内容,可以生成一个限定范围的模型。\n\n注意,改功能会影响对话的整体朝向!
'
}
/>
</
FormControl
>
</
Box
>
</
Card
>
<
Card
p=
{
4
}
>
<
Box
fontWeight=
{
'bold'
}
fontSize=
{
'lg'
}
>
安全策略
</
Box
>
<
Box
fontWeight=
{
'bold'
}
>
安全策略
</
Box
>
<
FormControl
mt=
{
2
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
flex=
{
'0 0 120px'
}
>
单句最大长度:
</
Box
>
<
Box
flex=
{
'0 0 120px'
}
w=
{
0
}
>
单句最大长度:
</
Box
>
<
Input
flex=
{
1
}
type=
{
'number'
}
...
...
@@ -132,7 +132,9 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
</
FormControl
>
<
FormControl
mt=
{
5
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
flex=
{
'0 0 120px'
}
>
上下文最大长度:
</
Box
>
<
Box
flex=
{
'0 0 120px'
}
w=
{
0
}
>
上下文最大长度:
</
Box
>
<
Input
flex=
{
1
}
type=
{
'number'
}
...
...
@@ -153,7 +155,9 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
</
FormControl
>
<
FormControl
mt=
{
5
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
flex=
{
'0 0 120px'
}
>
聊天过期时间:
</
Box
>
<
Box
flex=
{
'0 0 120px'
}
w=
{
0
}
>
聊天过期时间:
</
Box
>
<
Input
flex=
{
1
}
type=
{
'number'
}
...
...
@@ -175,7 +179,9 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
</
FormControl
>
<
FormControl
mt=
{
5
}
pb=
{
5
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Box
flex=
{
'0 0 130px'
}
>
聊天最大加载次数:
</
Box
>
<
Box
flex=
{
'0 0 130px'
}
w=
{
0
}
>
聊天最大加载次数:
</
Box
>
<
Box
flex=
{
1
}
>
<
Input
type=
{
'number'
}
...
...
@@ -196,7 +202,7 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
</
Flex
>
</
FormControl
>
</
Card
>
</
Grid
>
</>
);
};
...
...
src/pages/model/components/ModelPhoneList.tsx
View file @
00b90f07
import
React
,
{
useEffect
}
from
'react'
;
import
{
Box
,
Button
,
Flex
,
Tag
}
from
'@chakra-ui/react'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
{
formatModelStatus
}
from
'@/constants/model'
;
import
dayjs
from
'dayjs'
;
import
{
useRouter
}
from
'next/router'
;
...
...
@@ -9,7 +9,7 @@ const ModelPhoneList = ({
models
,
handlePreviewChat
}:
{
models
:
Model
Type
[];
models
:
Model
Schema
[];
handlePreviewChat
:
(
_
:
string
)
=>
void
;
})
=>
{
const
router
=
useRouter
();
...
...
src/pages/model/components/ModelTable.tsx
View file @
00b90f07
...
...
@@ -14,14 +14,14 @@ import {
}
from
'@chakra-ui/react'
;
import
{
formatModelStatus
}
from
'@/constants/model'
;
import
dayjs
from
'dayjs'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
{
useRouter
}
from
'next/router'
;
const
ModelTable
=
({
models
=
[],
handlePreviewChat
}:
{
models
:
Model
Type
[];
models
:
Model
Schema
[];
handlePreviewChat
:
(
_
:
string
)
=>
void
;
})
=>
{
const
router
=
useRouter
();
...
...
@@ -34,13 +34,13 @@ const ModelTable = ({
{
title
:
'最后更新时间'
,
key
:
'updateTime'
,
render
:
(
item
:
Model
Type
)
=>
dayjs
(
item
.
updateTime
).
format
(
'YYYY-MM-DD HH:mm'
)
render
:
(
item
:
Model
Schema
)
=>
dayjs
(
item
.
updateTime
).
format
(
'YYYY-MM-DD HH:mm'
)
},
{
title
:
'状态'
,
key
:
'status'
,
dataIndex
:
'status'
,
render
:
(
item
:
Model
Type
)
=>
(
render
:
(
item
:
Model
Schema
)
=>
(
<
Tag
colorScheme=
{
formatModelStatus
[
item
.
status
].
colorTheme
}
variant=
"solid"
...
...
@@ -54,7 +54,7 @@ const ModelTable = ({
{
title
:
'AI模型'
,
key
:
'service'
,
render
:
(
item
:
Model
Type
)
=>
(
render
:
(
item
:
Model
Schema
)
=>
(
<
Box
wordBreak=
{
'break-all'
}
whiteSpace=
{
'pre-wrap'
}
maxW=
{
'200px'
}
>
{
item
.
service
.
modelName
}
</
Box
>
...
...
@@ -68,7 +68,7 @@ const ModelTable = ({
{
title
:
'操作'
,
key
:
'control'
,
render
:
(
item
:
Model
Type
)
=>
(
render
:
(
item
:
Model
Schema
)
=>
(
<>
<
Button
mr=
{
3
}
onClick=
{
()
=>
handlePreviewChat
(
item
.
_id
)
}
>
对话
...
...
src/pages/model/components/Training.tsx
View file @
00b90f07
import
React
,
{
useEffect
,
useCallback
,
useState
}
from
'react'
;
import
{
Box
,
TableContainer
,
Table
,
Thead
,
Tbody
,
Tr
,
Th
,
Td
}
from
'@chakra-ui/react'
;
import
{
ModelType
}
from
'@/types/model
'
;
import
type
{
ModelSchema
}
from
'@/types/mongoSchema
'
;
import
{
getModelTrainings
}
from
'@/api/model'
;
import
type
{
TrainingItemType
}
from
'@/types/training'
;
const
Training
=
({
model
}:
{
model
:
Model
Type
})
=>
{
const
Training
=
({
model
}:
{
model
:
Model
Schema
})
=>
{
const
columns
:
{
title
:
string
;
key
:
keyof
TrainingItemType
;
...
...
src/pages/model/detail.tsx
View file @
00b90f07
import
React
,
{
useCallback
,
useState
,
use
Effect
,
useRef
,
useMemo
}
from
'react'
;
import
React
,
{
useCallback
,
useState
,
use
Ref
,
useMemo
,
useEffect
}
from
'react'
;
import
{
useRouter
}
from
'next/router'
;
import
{
getModelById
,
delModelById
,
postTrainModel
,
putModelTrainingStatus
}
from
'@/api/model'
;
import
{
getModelById
,
delModelById
,
postTrainModel
,
putModelTrainingStatus
,
putModelById
}
from
'@/api/model'
;
import
{
getChatSiteId
}
from
'@/api/chat'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
{
Card
,
Box
,
Flex
,
Button
,
Tag
,
Grid
}
from
'@chakra-ui/react'
;
import
{
useToast
}
from
'@/hooks/useToast'
;
import
{
useConfirm
}
from
'@/hooks/useConfirm'
;
import
{
formatModelStatus
,
ModelStatusEnum
,
OpenAiList
}
from
'@/constants/model'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
formatModelStatus
,
ModelStatusEnum
,
ModelList
,
defaultModel
}
from
'@/constants/model'
;
import
{
useGlobalStore
}
from
'@/store/global'
;
import
{
useScreen
}
from
'@/hooks/useScreen'
;
import
ModelEditForm
from
'./components/ModelEditForm'
;
import
Icon
from
'@/components/Iconfont'
;
import
{
useQuery
}
from
'@tanstack/react-query'
;
import
dynamic
from
'next/dynamic'
;
const
Training
=
dynamic
(()
=>
import
(
'./components/Training'
));
const
ModelDetail
=
()
=>
{
const
ModelDetail
=
(
{
modelId
}:
{
modelId
:
string
}
)
=>
{
const
{
toast
}
=
useToast
();
const
router
=
useRouter
();
const
{
isPc
,
media
}
=
useScreen
();
...
...
@@ -24,33 +32,35 @@ const ModelDetail = () => {
content
:
'确认删除该模型?'
});
const
SelectFileDom
=
useRef
<
HTMLInputElement
>
(
null
);
const
{
modelId
}
=
router
.
query
as
{
modelId
:
string
};
const
[
model
,
setModel
]
=
useState
<
ModelType
>
();
const
[
model
,
setModel
]
=
useState
<
ModelSchema
>
(
defaultModel
);
const
formHooks
=
useForm
<
ModelSchema
>
({
defaultValues
:
model
});
const
canTrain
=
useMemo
(()
=>
{
const
openai
=
OpenAiList
.
find
((
item
)
=>
item
.
model
===
model
?.
service
.
modelName
);
return
openai
&&
openai
.
canTraining
===
true
;
const
openai
=
ModelList
[
model
.
service
.
company
].
find
(
(
item
)
=>
item
.
model
===
model
?.
service
.
modelName
);
return
openai
&&
openai
.
trainName
;
},
[
model
]);
/* 加载模型数据 */
const
loadModel
=
useCallback
(
async
()
=>
{
if
(
!
modelId
)
return
;
setLoading
(
true
);
try
{
const
res
=
await
getModelById
(
modelId
as
string
);
const
res
=
await
getModelById
(
modelId
);
console
.
log
(
res
);
res
.
security
.
expiredTime
/=
60
*
60
*
1000
;
setModel
(
res
);
formHooks
.
reset
(
res
);
}
catch
(
err
)
{
console
.
log
(
'error->'
,
err
);
}
setLoading
(
false
);
},
[
modelId
,
setLoading
]);
return
null
;
},
[
formHooks
,
modelId
,
setLoading
]);
useEffect
(()
=>
{
loadModel
();
router
.
prefetch
(
'/chat'
);
},
[
loadModel
,
modelId
,
router
]);
useQuery
([
modelId
],
loadModel
);
/* 点击删除 */
const
handleDelModel
=
useCallback
(
async
()
=>
{
...
...
@@ -71,7 +81,6 @@ const ModelDetail = () => {
/* 点前往聊天预览页 */
const
handlePreviewChat
=
useCallback
(
async
()
=>
{
if
(
!
model
)
return
;
setLoading
(
true
);
try
{
const
chatId
=
await
getChatSiteId
(
model
.
_id
);
...
...
@@ -131,6 +140,65 @@ const ModelDetail = () => {
setLoading
(
false
);
},
[
model
,
setLoading
,
loadModel
,
toast
]);
// 提交保存模型修改
const
saveSubmitSuccess
=
useCallback
(
async
(
data
:
ModelSchema
)
=>
{
setLoading
(
true
);
try
{
await
putModelById
(
data
.
_id
,
{
name
:
data
.
name
,
systemPrompt
:
data
.
systemPrompt
,
intro
:
data
.
intro
,
temperature
:
data
.
temperature
,
service
:
data
.
service
,
security
:
data
.
security
});
toast
({
title
:
'更新成功'
,
status
:
'success'
});
}
catch
(
err
)
{
console
.
log
(
'error->'
,
err
);
toast
({
title
:
err
as
string
,
status
:
'success'
});
}
setLoading
(
false
);
},
[
setLoading
,
toast
]
);
// 提交保存表单失败
const
saveSubmitError
=
useCallback
(()
=>
{
// deep search message
const
deepSearch
=
(
obj
:
any
):
string
=>
{
if
(
!
obj
)
return
'提交表单错误'
;
if
(
!!
obj
.
message
)
{
return
obj
.
message
;
}
return
deepSearch
(
Object
.
values
(
obj
)[
0
]);
};
toast
({
title
:
deepSearch
(
formHooks
.
formState
.
errors
),
status
:
'error'
,
duration
:
4000
,
isClosable
:
true
});
},
[
formHooks
.
formState
.
errors
,
toast
]);
useEffect
(()
=>
{
router
.
prefetch
(
'/chat'
);
window
.
onbeforeunload
=
(
e
)
=>
{
e
.
preventDefault
();
e
.
returnValue
=
'内容已修改,确认离开页面吗?'
;
};
return
()
=>
{
window
.
onbeforeunload
=
null
;
};
},
[]);
return
(
<>
{
/* 头部 */
}
...
...
@@ -138,50 +206,48 @@ const ModelDetail = () => {
{
isPc
?
(
<
Flex
alignItems=
{
'center'
}
>
<
Box
fontSize=
{
'xl'
}
fontWeight=
{
'bold'
}
>
{
model
?.
name
||
'模型'
}
配置
{
model
.
name
}
</
Box
>
{
!!
model
&&
(
<
Tag
ml=
{
2
}
variant=
"solid"
colorScheme=
{
formatModelStatus
[
model
.
status
].
colorTheme
}
cursor=
{
model
.
status
===
ModelStatusEnum
.
training
?
'pointer'
:
'default'
}
onClick=
{
handleClickUpdateStatus
}
>
{
formatModelStatus
[
model
.
status
].
text
}
</
Tag
>
)
}
<
Tag
ml=
{
2
}
variant=
"solid"
colorScheme=
{
formatModelStatus
[
model
.
status
].
colorTheme
}
cursor=
{
model
.
status
===
ModelStatusEnum
.
training
?
'pointer'
:
'default'
}
onClick=
{
handleClickUpdateStatus
}
>
{
formatModelStatus
[
model
.
status
].
text
}
</
Tag
>
<
Box
flex=
{
1
}
/>
<
Button
variant=
{
'outline'
}
onClick=
{
handlePreviewChat
}
>
对话体验
</
Button
>
<
Button
ml=
{
4
}
onClick=
{
formHooks
.
handleSubmit
(
saveSubmitSuccess
,
saveSubmitError
)
}
>
保存修改
</
Button
>
</
Flex
>
)
:
(
<>
<
Flex
alignItems=
{
'center'
}
>
<
Box
as=
{
'h3'
}
fontSize=
{
'xl'
}
fontWeight=
{
'bold'
}
flex=
{
1
}
>
{
model
?.
name
||
'模型'
}
配置
{
model
?.
name
}
</
Box
>
{
!!
model
&&
(
<
Tag
ml=
{
2
}
colorScheme=
{
formatModelStatus
[
model
.
status
].
colorTheme
}
>
{
formatModelStatus
[
model
.
status
].
text
}
</
Tag
>
)
}
<
Tag
ml=
{
2
}
colorScheme=
{
formatModelStatus
[
model
.
status
].
colorTheme
}
>
{
formatModelStatus
[
model
.
status
].
text
}
</
Tag
>
</
Flex
>
<
Box
mt=
{
4
}
textAlign=
{
'right'
}
>
<
Button
variant=
{
'outline'
}
onClick=
{
handlePreviewChat
}
>
对话体验
</
Button
>
<
Button
ml=
{
4
}
onClick=
{
formHooks
.
handleSubmit
(
saveSubmitSuccess
,
saveSubmitError
)
}
>
保存修改
</
Button
>
</
Box
>
</>
)
}
</
Card
>
{
/* 基本信息编辑 */
}
<
Box
mt=
{
5
}
>
<
ModelEditForm
model=
{
model
}
/>
</
Box
>
{
/* 其他配置 */
}
<
Grid
mt=
{
5
}
gridTemplateColumns=
{
media
(
'1fr 1fr'
,
'1fr'
)
}
gridGap=
{
5
}
>
<
ModelEditForm
formHooks=
{
formHooks
}
/>
<
Card
p=
{
4
}
>
{
!!
model
&&
<
Training
model=
{
model
}
/>
}
</
Card
>
<
Card
p=
{
4
}
>
<
Box
fontWeight=
{
'bold'
}
fontSize=
{
'lg'
}
>
...
...
@@ -241,6 +307,8 @@ const ModelDetail = () => {
</
Flex
>
</
Card
>
</
Grid
>
{
/* 文件选择 */
}
<
Box
position=
{
'absolute'
}
w=
{
0
}
h=
{
0
}
overflow=
{
'hidden'
}
>
<
input
ref=
{
SelectFileDom
}
type=
"file"
accept=
".jsonl"
onChange=
{
startTraining
}
/>
</
Box
>
...
...
@@ -250,3 +318,11 @@ const ModelDetail = () => {
};
export
default
ModelDetail
;
export
async
function
getServerSideProps
(
context
:
any
)
{
const
modelId
=
context
.
query
?.
modelId
||
''
;
return
{
props
:
{
modelId
}
};
}
src/pages/model/list.tsx
View file @
00b90f07
import
React
,
{
useState
,
useCallback
}
from
'react'
;
import
{
Box
,
Button
,
Flex
,
Card
}
from
'@chakra-ui/react'
;
import
{
getChatSiteId
}
from
'@/api/chat'
;
import
{
ModelType
}
from
'@/types/model
'
;
import
type
{
ModelSchema
}
from
'@/types/mongoSchema
'
;
import
{
useRouter
}
from
'next/router'
;
import
ModelTable
from
'./components/ModelTable'
;
import
ModelPhoneList
from
'./components/ModelPhoneList'
;
...
...
@@ -27,7 +27,7 @@ const ModelList = () => {
/* 创建成功回调 */
const
createModelSuccess
=
useCallback
(
(
data
:
Model
Type
)
=>
{
(
data
:
Model
Schema
)
=>
{
setMyModels
([
data
,
...
myModels
]);
},
[
myModels
,
setMyModels
]
...
...
src/service/models/chat.ts
View file @
00b90f07
...
...
@@ -25,19 +25,22 @@ const ChatSchema = new Schema({
type
:
Number
,
required
:
true
},
content
:
[
{
obj
:
{
type
:
String
,
required
:
true
,
enum
:
[
'Human'
,
'AI'
,
'SYSTEM'
]
},
value
:
{
type
:
String
,
required
:
true
content
:
{
type
:
[
{
obj
:
{
type
:
String
,
required
:
true
,
enum
:
[
'Human'
,
'AI'
,
'SYSTEM'
]
},
value
:
{
type
:
String
,
required
:
true
}
}
}
]
],
default
:
[]
}
});
export
const
Chat
=
models
[
'chat'
]
||
model
(
'chat'
,
ChatSchema
);
src/service/models/model.ts
View file @
00b90f07
import
{
Schema
,
model
,
models
}
from
'mongoose'
;
const
ModelSchema
=
new
Schema
({
userId
:
{
type
:
Schema
.
Types
.
ObjectId
,
ref
:
'user'
,
required
:
true
},
name
:
{
type
:
String
,
required
:
true
...
...
@@ -10,13 +15,14 @@ const ModelSchema = new Schema({
default
:
'/imgs/modelAvatar.png'
},
systemPrompt
:
{
// 系统提示词
type
:
String
,
default
:
''
},
userId
:
{
type
:
Schema
.
Types
.
ObjectId
,
ref
:
'user'
,
required
:
true
intro
:
{
// 模型介绍
type
:
String
,
default
:
''
},
status
:
{
type
:
String
,
...
...
@@ -31,6 +37,12 @@ const ModelSchema = new Schema({
type
:
Number
,
default
:
0
},
temperature
:
{
type
:
Number
,
min
:
1
,
max
:
10
,
default
:
5
},
service
:
{
company
:
{
type
:
String
,
...
...
src/store/user.ts
View file @
00b90f07
...
...
@@ -2,7 +2,7 @@ import { create } from 'zustand';
import
{
devtools
}
from
'zustand/middleware'
;
import
{
immer
}
from
'zustand/middleware/immer'
;
import
type
{
UserType
,
UserUpdateParams
}
from
'@/types/user'
;
import
type
{
Model
Type
}
from
'@/types/model
'
;
import
type
{
Model
Schema
}
from
'@/types/mongoSchema
'
;
import
{
setToken
}
from
'@/utils/user'
;
import
{
getMyModels
}
from
'@/api/model'
;
...
...
@@ -10,9 +10,9 @@ type State = {
userInfo
:
UserType
|
null
;
setUserInfo
:
(
user
:
UserType
,
token
?:
string
)
=>
void
;
updateUserInfo
:
(
user
:
UserUpdateParams
)
=>
void
;
myModels
:
Model
Type
[];
myModels
:
Model
Schema
[];
getMyModels
:
()
=>
void
;
setMyModels
:
(
data
:
Model
Type
[])
=>
void
;
setMyModels
:
(
data
:
Model
Schema
[])
=>
void
;
};
export
const
useUserStore
=
create
<
State
>
()(
...
...
@@ -42,7 +42,7 @@ export const useUserStore = create<State>()(
});
return
res
;
}),
setMyModels
(
data
:
Model
Type
[])
{
setMyModels
(
data
:
Model
Schema
[])
{
set
((
state
)
=>
{
state
.
myModels
=
data
;
});
...
...
src/types/chat.d.ts
View file @
00b90f07
import
type
{
ModelType
}
from
'./model'
;
export
type
ChatItemType
=
{
obj
:
'Human'
|
'AI'
|
'SYSTEM'
;
value
:
string
;
...
...
src/types/model.d.ts
View file @
00b90f07
import
{
ModelStatusEnum
}
from
'@/constants/model'
;
export
interface
ModelType
{
_id
:
string
;
userId
:
string
;
name
:
string
;
avatar
:
string
;
status
:
`
${
ModelStatusEnum
}
`
;
updateTime
:
Date
;
trainingTimes
:
number
;
systemPrompt
:
string
;
service
:
{
company
:
'openai'
;
// 关联的厂商
trainId
:
string
;
// 训练时需要的ID
chatModel
:
string
;
// 聊天时用的模型
modelName
:
string
;
// 关联的模型
};
security
:
{
domain
:
string
[];
contentMaxLen
:
number
;
contextMaxLen
:
number
;
expiredTime
:
number
;
maxLoadAmount
:
number
;
};
}
import
type
{
ModelSchema
}
from
'./mongoSchema'
;
export
interface
ModelUpdateParams
{
name
:
string
;
systemPrompt
:
string
;
service
:
{
company
:
'openai'
;
// 关联的厂商
modelName
:
string
;
// 关联的模型
};
security
:
{
domain
:
string
[];
contentMaxLen
:
number
;
contextMaxLen
:
number
;
expiredTime
:
number
;
maxLoadAmount
:
number
;
};
intro
:
string
;
temperature
:
number
;
service
:
ModelSchema
.
service
;
security
:
ModelSchema
.
security
;
}
src/types/mongoSchema.d.ts
View file @
00b90f07
...
...
@@ -25,15 +25,17 @@ export interface ModelSchema {
name
:
string
;
avatar
:
string
;
systemPrompt
:
string
;
intro
:
string
;
userId
:
string
;
status
:
`
${
ModelStatusEnum
}
`
;
updateTime
:
number
;
trainingTimes
:
number
;
temperature
:
number
;
service
:
{
company
:
ServiceName
;
trainId
:
string
;
chatModel
:
`
${
ChatModelNameEnum
}
`
;
modelName
:
string
;
trainId
:
string
;
// 训练的模型,训练后就是训练的模型id
chatModel
:
string
;
// 聊天时用的模型,训练后就是训练的模型
modelName
:
`
${
ChatModelNameEnum
}
`
;
// 底层模型名称,不会变
};
security
:
{
domain
:
string
[];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment