mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-22 03:45:52 +00:00
feat: 模型介绍和温度调整。完善聊天页提示
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
import { GET, POST, DELETE, PUT } from './request';
|
import { GET, POST, DELETE, PUT } from './request';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { ModelUpdateParams } from '@/types/model';
|
import { ModelUpdateParams } from '@/types/model';
|
||||||
import { TrainingItemType } from '../types/training';
|
import { TrainingItemType } from '../types/training';
|
||||||
|
|
||||||
export const getMyModels = () => GET<ModelType[]>('/model/list');
|
export const getMyModels = () => GET<ModelSchema[]>('/model/list');
|
||||||
|
|
||||||
export const postCreateModel = (data: { name: string; serviceModelName: string }) =>
|
export const postCreateModel = (data: { name: string; serviceModelName: string }) =>
|
||||||
POST<ModelType>('/model/create', data);
|
POST<ModelSchema>('/model/create', data);
|
||||||
|
|
||||||
export const delModelById = (id: string) => DELETE(`/model/del?modelId=${id}`);
|
export const delModelById = (id: string) => DELETE(`/model/del?modelId=${id}`);
|
||||||
|
|
||||||
export const getModelById = (id: string) => GET<ModelType>(`/model/detail?modelId=${id}`);
|
export const getModelById = (id: string) => GET<ModelSchema>(`/model/detail?modelId=${id}`);
|
||||||
|
|
||||||
export const putModelById = (id: string, data: ModelUpdateParams) =>
|
export const putModelById = (id: string, data: ModelUpdateParams) =>
|
||||||
PUT(`/model/update?modelId=${id}`, data);
|
PUT(`/model/update?modelId=${id}`, data);
|
||||||
|
1
src/api/response/chat.d.ts
vendored
1
src/api/response/chat.d.ts
vendored
@@ -6,6 +6,7 @@ export type InitChatResponse = {
|
|||||||
modelId: string;
|
modelId: string;
|
||||||
name: string;
|
name: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
|
intro: string;
|
||||||
secret: ModelSchema.secret;
|
secret: ModelSchema.secret;
|
||||||
chatModel: ModelSchema.service.ChatModel; // 模型名
|
chatModel: ModelSchema.service.ChatModel; // 模型名
|
||||||
history: ChatItemType[];
|
history: ChatItemType[];
|
||||||
|
@@ -172,7 +172,7 @@
|
|||||||
}
|
}
|
||||||
.markdown ul,
|
.markdown ul,
|
||||||
.markdown ol {
|
.markdown ol {
|
||||||
padding-left: 30px;
|
padding-left: 1em;
|
||||||
}
|
}
|
||||||
.markdown ul.no-list,
|
.markdown ul.no-list,
|
||||||
.markdown ol.no-list {
|
.markdown ol.no-list {
|
||||||
|
@@ -12,7 +12,7 @@ import 'katex/dist/katex.min.css';
|
|||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
import { codeLight } from './codeLight';
|
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 formatSource = useMemo(() => source, [source]);
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
|
|
||||||
|
82
src/components/Slider/index.tsx
Normal file
82
src/components/Slider/index.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
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;
|
@@ -39,3 +39,25 @@ export const introPage = `
|
|||||||
### 其他问题
|
### 其他问题
|
||||||
还有其他问题,可以加我 wx: YNyiqi,拉个交流群大家一起聊聊。
|
还有其他问题,可以加我 wx: YNyiqi,拉个交流群大家一起聊聊。
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const chatProblem = `
|
||||||
|
**代理出错**
|
||||||
|
服务器代理不稳定,可以过一会儿再尝试。
|
||||||
|
|
||||||
|
**API key 问题**
|
||||||
|
请把 openai 的 API key 粘贴到账号里再创建对话。如果是使用分享的对话,不需要填写 API key。
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const versionIntro = `
|
||||||
|
* 分享对话:使用的是分享者的 Api Key 生成一个对话窗口进行分享。
|
||||||
|
* 分享空白对话:为该模型创建一个空白的聊天分享出去。
|
||||||
|
* 分享当前对话:会把当前聊天的内容也分享出去,但是要注意不要多个人同时用一个聊天内容。
|
||||||
|
* 增加模型介绍:可以在模型编辑页添加对模型的介绍,方便提示模型的范围。
|
||||||
|
* 温度调整:可以在模型编辑页调整模型温度,以便适应不同类型的对话。例如,翻译类的模型可以把温度拉低;创作类的模型可以把温度拉高。
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const shareHint = `
|
||||||
|
你正准备分享对话,请确保分享链接不会滥用,因为它是使用的是你的 API key。
|
||||||
|
* 分享空白对话:为该模型创建一个空白的聊天分享出去。
|
||||||
|
* 分享当前对话:会把当前聊天的内容也分享出去,但是要注意不要多个人同时用一个聊天内容。
|
||||||
|
`;
|
||||||
|
@@ -1,23 +1,37 @@
|
|||||||
|
import type { ServiceName } from '@/types/mongoSchema';
|
||||||
|
import { ModelSchema } from '../types/mongoSchema';
|
||||||
|
|
||||||
export enum ChatModelNameEnum {
|
export enum ChatModelNameEnum {
|
||||||
GPT35 = 'gpt-3.5-turbo',
|
GPT35 = 'gpt-3.5-turbo',
|
||||||
GPT3 = 'text-davinci-003'
|
GPT3 = 'text-davinci-003'
|
||||||
}
|
}
|
||||||
export const OpenAiList = [
|
|
||||||
{
|
export type ModelConstantsData = {
|
||||||
name: 'chatGPT',
|
name: string;
|
||||||
model: ChatModelNameEnum.GPT35,
|
model: `${ChatModelNameEnum}`;
|
||||||
trainName: 'turbo',
|
trainName: string; // 空字符串代表不能训练
|
||||||
canTraining: false,
|
maxToken: number;
|
||||||
maxToken: 4060
|
maxTemperature: number;
|
||||||
},
|
};
|
||||||
{
|
|
||||||
name: 'GPT3',
|
export const ModelList: Record<ServiceName, ModelConstantsData[]> = {
|
||||||
model: ChatModelNameEnum.GPT3,
|
openai: [
|
||||||
trainName: 'davinci',
|
{
|
||||||
canTraining: true,
|
name: 'chatGPT',
|
||||||
maxToken: 4060
|
model: ChatModelNameEnum.GPT35,
|
||||||
}
|
trainName: 'turbo',
|
||||||
];
|
maxToken: 4000,
|
||||||
|
maxTemperature: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'GPT3',
|
||||||
|
model: ChatModelNameEnum.GPT3,
|
||||||
|
trainName: 'davinci',
|
||||||
|
maxToken: 4000,
|
||||||
|
maxTemperature: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
export enum TrainingStatusEnum {
|
export enum TrainingStatusEnum {
|
||||||
pending = 'pending',
|
pending = 'pending',
|
||||||
@@ -51,3 +65,29 @@ export const formatModelStatus = {
|
|||||||
text: '已关闭'
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@@ -8,6 +8,7 @@ import { ChatItemType } from '@/types/chat';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { PassThrough } from 'stream';
|
import { PassThrough } from 'stream';
|
||||||
|
import { ModelList } from '@/constants/model';
|
||||||
|
|
||||||
/* 发送提示词 */
|
/* 发送提示词 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
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
|
// 获取 chatAPI
|
||||||
const chatAPI = getOpenAIApi(userApiKey);
|
const chatAPI = getOpenAIApi(userApiKey);
|
||||||
let startTime = Date.now();
|
let startTime = Date.now();
|
||||||
@@ -63,8 +73,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const chatResponse = await chatAPI.createChatCompletion(
|
const chatResponse = await chatAPI.createChatCompletion(
|
||||||
{
|
{
|
||||||
model: model.service.chatModel,
|
model: model.service.chatModel,
|
||||||
temperature: 1,
|
temperature: temperature,
|
||||||
// max_tokens: model.security.contentMaxLen,
|
max_tokens: modelConstantsData.maxToken,
|
||||||
messages: formatPrompts,
|
messages: formatPrompts,
|
||||||
stream: true
|
stream: true
|
||||||
},
|
},
|
||||||
|
@@ -5,6 +5,7 @@ import { connectToDatabase } from '@/service/mongo';
|
|||||||
import { getOpenAIApi, authChat } from '@/service/utils/chat';
|
import { getOpenAIApi, authChat } from '@/service/utils/chat';
|
||||||
import { ChatItemType } from '@/types/chat';
|
import { ChatItemType } from '@/types/chat';
|
||||||
import { httpsAgent } from '@/service/utils/tools';
|
import { httpsAgent } from '@/service/utils/tools';
|
||||||
|
import { ModelList } from '@/constants/model';
|
||||||
|
|
||||||
/* 发送提示词 */
|
/* 发送提示词 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
@@ -27,13 +28,22 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
// prompt处理
|
// prompt处理
|
||||||
const formatPrompt = prompt.map((item) => `${item.value}\n\n###\n\n`).join('');
|
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(
|
const response = await chatAPI.createCompletion(
|
||||||
{
|
{
|
||||||
model: model.service.modelName,
|
model: model.service.modelName,
|
||||||
prompt: formatPrompt,
|
prompt: formatPrompt,
|
||||||
temperature: 0.5,
|
temperature: temperature,
|
||||||
max_tokens: model.security.contentMaxLen,
|
max_tokens: modelConstantsData.maxToken,
|
||||||
top_p: 1,
|
top_p: 1,
|
||||||
frequency_penalty: 0,
|
frequency_penalty: 0,
|
||||||
presence_penalty: 0.6,
|
presence_penalty: 0.6,
|
||||||
|
@@ -47,6 +47,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
modelId: model._id,
|
modelId: model._id,
|
||||||
name: model.name,
|
name: model.name,
|
||||||
avatar: model.avatar,
|
avatar: model.avatar,
|
||||||
|
intro: model.intro,
|
||||||
secret: model.security,
|
secret: model.security,
|
||||||
chatModel: model.service.chatModel,
|
chatModel: model.service.chatModel,
|
||||||
history: chat.content
|
history: chat.content
|
||||||
|
@@ -3,12 +3,21 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authToken } from '@/service/utils/tools';
|
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';
|
import { Model } from '@/service/models/model';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
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;
|
const { authorization } = req.headers;
|
||||||
|
|
||||||
if (!authorization) {
|
if (!authorization) {
|
||||||
@@ -22,10 +31,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
// 凭证校验
|
// 凭证校验
|
||||||
const userId = await authToken(authorization);
|
const userId = await authToken(authorization);
|
||||||
|
|
||||||
const modelItem = OpenAiList.find((item) => item.model === serviceModelName);
|
const modelItem = ModelList[serviceModelCompany].find(
|
||||||
|
(item) => item.model === serviceModelName
|
||||||
|
);
|
||||||
|
|
||||||
if (!modelItem) {
|
if (!modelItem) {
|
||||||
throw new Error('模型错误');
|
throw new Error('模型不存在');
|
||||||
}
|
}
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
@@ -43,8 +54,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
const authCount = await Model.countDocuments({
|
const authCount = await Model.countDocuments({
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
if (authCount >= 10) {
|
if (authCount >= 20) {
|
||||||
throw new Error('上限 10 个模型');
|
throw new Error('上限 20 个模型');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建模型
|
// 创建模型
|
||||||
|
@@ -3,7 +3,7 @@ import { jsonRes } from '@/service/response';
|
|||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authToken } from '@/service/utils/tools';
|
import { authToken } from '@/service/utils/tools';
|
||||||
import { Model } from '@/service/models/model';
|
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>) {
|
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();
|
await connectToDatabase();
|
||||||
|
|
||||||
// 根据 userId 获取模型信息
|
// 根据 userId 获取模型信息
|
||||||
const model: ModelType | null = await Model.findOne({
|
const model = await Model.findOne<ModelSchema>({
|
||||||
userId,
|
userId,
|
||||||
_id: modelId
|
_id: modelId
|
||||||
});
|
});
|
||||||
|
@@ -6,7 +6,7 @@ import formidable from 'formidable';
|
|||||||
import { authToken, getUserOpenaiKey } from '@/service/utils/tools';
|
import { authToken, getUserOpenaiKey } from '@/service/utils/tools';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import type { OpenAIApi } from 'openai';
|
import type { OpenAIApi } from 'openai';
|
||||||
import { ModelStatusEnum, TrainingStatusEnum } from '@/constants/model';
|
import { ModelStatusEnum, TrainingStatusEnum } from '@/constants/model';
|
||||||
import { httpsAgent } from '@/service/utils/tools';
|
import { httpsAgent } from '@/service/utils/tools';
|
||||||
|
@@ -3,7 +3,7 @@ import { jsonRes } from '@/service/response';
|
|||||||
import { connectToDatabase, Model, Training } from '@/service/mongo';
|
import { connectToDatabase, Model, Training } from '@/service/mongo';
|
||||||
import { getOpenAIApi } from '@/service/utils/chat';
|
import { getOpenAIApi } from '@/service/utils/chat';
|
||||||
import { authToken, getUserOpenaiKey } from '@/service/utils/tools';
|
import { authToken, getUserOpenaiKey } from '@/service/utils/tools';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { TrainingItemType } from '@/types/training';
|
import { TrainingItemType } from '@/types/training';
|
||||||
import { ModelStatusEnum, TrainingStatusEnum } from '@/constants/model';
|
import { ModelStatusEnum, TrainingStatusEnum } from '@/constants/model';
|
||||||
import { OpenAiTuneStatusEnum } from '@/service/constants/training';
|
import { OpenAiTuneStatusEnum } from '@/service/constants/training';
|
||||||
@@ -26,7 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
// 获取模型
|
// 获取模型
|
||||||
const model: ModelType | null = await Model.findById(modelId);
|
const model = await Model.findById<ModelSchema>(modelId);
|
||||||
|
|
||||||
if (!model || model.status !== 'training') {
|
if (!model || model.status !== 'training') {
|
||||||
throw new Error('模型不在训练中');
|
throw new Error('模型不在训练中');
|
||||||
|
@@ -7,7 +7,7 @@ import formidable from 'formidable';
|
|||||||
import { authToken, getUserOpenaiKey } from '@/service/utils/tools';
|
import { authToken, getUserOpenaiKey } from '@/service/utils/tools';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import type { OpenAIApi } from 'openai';
|
import type { OpenAIApi } from 'openai';
|
||||||
import { ModelStatusEnum, TrainingStatusEnum } from '@/constants/model';
|
import { ModelStatusEnum, TrainingStatusEnum } from '@/constants/model';
|
||||||
import { httpsAgent } from '@/service/utils/tools';
|
import { httpsAgent } from '@/service/utils/tools';
|
||||||
@@ -38,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
// 获取模型的状态
|
// 获取模型的状态
|
||||||
const model: ModelType | null = await Model.findById(modelId);
|
const model = await Model.findById<ModelSchema>(modelId);
|
||||||
|
|
||||||
if (!model || model.status !== 'running') {
|
if (!model || model.status !== 'running') {
|
||||||
throw new Error('模型正忙');
|
throw new Error('模型正忙');
|
||||||
|
@@ -8,7 +8,8 @@ import type { ModelUpdateParams } from '@/types/model';
|
|||||||
/* 获取我的模型 */
|
/* 获取我的模型 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
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 { modelId } = req.query as { modelId: string };
|
||||||
const { authorization } = req.headers;
|
const { authorization } = req.headers;
|
||||||
|
|
||||||
@@ -33,8 +34,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
service,
|
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
|
intro,
|
||||||
|
temperature,
|
||||||
|
service,
|
||||||
security
|
security
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -1,23 +1,44 @@
|
|||||||
import React from 'react';
|
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 (
|
return (
|
||||||
<Flex h={'100%'} alignItems={'center'} justifyContent={'center'}>
|
<Box
|
||||||
<Card p={5} w={'70%'}>
|
minH={'100%'}
|
||||||
<Box fontSize={'xl'} fontWeight={'bold'} textAlign={'center'} pb={2}>
|
w={'85%'}
|
||||||
Fast Gpt version1.3
|
maxW={'600px'}
|
||||||
</Box>
|
m={'auto'}
|
||||||
<Box>
|
py={'5vh'}
|
||||||
更新了聊天的数据结构,如果出现问题,请手动删除左侧旧的历史记录,并重新从模型页生成对话框进入。
|
alignItems={'center'}
|
||||||
</Box>
|
justifyContent={'center'}
|
||||||
<Box>分享聊天使用的是分享者的 Api Key 进行收费,请确认分享安全</Box>
|
>
|
||||||
<br />
|
{!!intro && (
|
||||||
<Box>分享空白聊天,会分享一个该模型的空白聊天页</Box>
|
<Card p={4} mb={10}>
|
||||||
<br />
|
<Header>模型介绍</Header>
|
||||||
<Box>分享当前聊天,会把当前聊天的内容分享出去,请注意不会多人同时使用一个对话框</Box>
|
<Box>{intro}</Box>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
<Card p={4} mb={10}>
|
||||||
|
<Header>常见问题</Header>
|
||||||
|
<Markdown source={chatProblem} />
|
||||||
</Card>
|
</Card>
|
||||||
</Flex>
|
{/* version intro */}
|
||||||
|
<Card p={4}>
|
||||||
|
<Header>Fast Gpt version1.4</Header>
|
||||||
|
<Box>
|
||||||
|
聊天的数据结构发生了比较大的改动。如果出现问题,请手动删除左侧旧的历史记录,并重新从模型页生成对话框进入。
|
||||||
|
</Box>
|
||||||
|
<br />
|
||||||
|
<Markdown source={versionIntro} />
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Box, Button } from '@chakra-ui/react';
|
import { AddIcon, ChatIcon, DeleteIcon } from '@chakra-ui/icons';
|
||||||
import { AddIcon, ChatIcon, EditIcon, DeleteIcon } from '@chakra-ui/icons';
|
|
||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionButton,
|
AccordionButton,
|
||||||
@@ -9,16 +10,26 @@ import {
|
|||||||
AccordionIcon,
|
AccordionIcon,
|
||||||
Flex,
|
Flex,
|
||||||
Divider,
|
Divider,
|
||||||
IconButton
|
IconButton,
|
||||||
|
Modal,
|
||||||
|
ModalOverlay,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalFooter,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
useDisclosure
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import { useChatStore } from '@/store/chat';
|
import { useChatStore } from '@/store/chat';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useScreen } from '@/hooks/useScreen';
|
|
||||||
import { getToken } from '@/utils/user';
|
import { getToken } from '@/utils/user';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
import { useCopyData } from '@/utils/tools';
|
import { useCopyData } from '@/utils/tools';
|
||||||
|
import Markdown from '@/components/Markdown';
|
||||||
|
import { shareHint } from '@/constants/common';
|
||||||
|
import { getChatSiteId } from '@/api/chat';
|
||||||
|
|
||||||
const SlideBar = ({
|
const SlideBar = ({
|
||||||
name,
|
name,
|
||||||
@@ -36,10 +47,13 @@ const SlideBar = ({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
const { myModels, getMyModels } = useUserStore();
|
const { myModels, getMyModels } = useUserStore();
|
||||||
const { chatHistory, removeChatHistoryByWindowId, generateChatWindow, updateChatHistory } =
|
const { chatHistory, removeChatHistoryByWindowId } = useChatStore();
|
||||||
useChatStore();
|
|
||||||
const { isSuccess } = useQuery(['init'], getMyModels);
|
|
||||||
const [hasReady, setHasReady] = useState(false);
|
const [hasReady, setHasReady] = useState(false);
|
||||||
|
const { isOpen: isOpenShare, onOpen: onOpenShare, onClose: onCloseShare } = useDisclosure();
|
||||||
|
|
||||||
|
const { isSuccess } = useQuery(['init'], getMyModels, {
|
||||||
|
cacheTime: 5 * 60 * 1000
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHasReady(true);
|
setHasReady(true);
|
||||||
@@ -119,19 +133,8 @@ const SlideBar = ({
|
|||||||
|
|
||||||
{/* 我的模型 & 历史记录 折叠框*/}
|
{/* 我的模型 & 历史记录 折叠框*/}
|
||||||
<Box flex={'1 0 0'} px={3} h={0} overflowY={'auto'}>
|
<Box flex={'1 0 0'} px={3} h={0} overflowY={'auto'}>
|
||||||
{isSuccess ? (
|
<Accordion defaultIndex={[0]} allowToggle>
|
||||||
<Accordion defaultIndex={[0]} allowToggle>
|
{isSuccess && (
|
||||||
<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>
|
|
||||||
<AccordionItem borderTop={0} borderBottom={0}>
|
<AccordionItem borderTop={0} borderBottom={0}>
|
||||||
<AccordionButton borderRadius={'md'} pl={1}>
|
<AccordionButton borderRadius={'md'} pl={1}>
|
||||||
<Box as="span" flex="1" textAlign="left">
|
<Box as="span" flex="1" textAlign="left">
|
||||||
@@ -161,7 +164,7 @@ const SlideBar = ({
|
|||||||
: {})}
|
: {})}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (item.name === name) return;
|
if (item.name === name) return;
|
||||||
router.push(`/chat?chatId=${await generateChatWindow(item._id)}`);
|
router.push(`/chat?chatId=${await getChatSiteId(item._id)}`);
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -173,43 +176,25 @@ const SlideBar = ({
|
|||||||
))}
|
))}
|
||||||
</AccordionPanel>
|
</AccordionPanel>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
)}
|
||||||
) : (
|
<AccordionItem borderTop={0} borderBottom={0}>
|
||||||
<>
|
<AccordionButton borderRadius={'md'} pl={1}>
|
||||||
<Box mb={4} textAlign={'center'}>
|
<Box as="span" flex="1" textAlign="left">
|
||||||
历史记录
|
历史记录
|
||||||
</Box>
|
</Box>
|
||||||
<RenderHistory />
|
<AccordionIcon />
|
||||||
</>
|
</AccordionButton>
|
||||||
)}
|
<AccordionPanel pb={0} px={0}>
|
||||||
|
{hasReady && <RenderHistory />}
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Divider my={4} />
|
<Divider my={4} />
|
||||||
|
|
||||||
<Box px={3}>
|
<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
|
<Flex
|
||||||
mt={4}
|
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
p={2}
|
p={2}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
@@ -217,14 +202,57 @@ const SlideBar = ({
|
|||||||
_hover={{
|
_hover={{
|
||||||
backgroundColor: 'rgba(255,255,255,0.2)'
|
backgroundColor: 'rgba(255,255,255,0.2)'
|
||||||
}}
|
}}
|
||||||
onClick={async () => {
|
onClick={() => {
|
||||||
copyData(`${location.origin}/chat?chatId=${chatId}`, '已复制分享链接');
|
onOpenShare();
|
||||||
|
onClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MyIcon name="share" fill={'white'} w={'16px'} h={'16px'} mr={4} />
|
<MyIcon name="share" fill={'white'} w={'16px'} h={'16px'} mr={4} />
|
||||||
分享当前对话
|
分享对话
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</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>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -51,6 +51,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
modelId: '',
|
modelId: '',
|
||||||
name: '',
|
name: '',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
|
intro: '',
|
||||||
secret: {},
|
secret: {},
|
||||||
chatModel: '',
|
chatModel: '',
|
||||||
history: [],
|
history: [],
|
||||||
@@ -113,7 +114,11 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
status: 'finish'
|
status: 'finish'
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
scrollToBottom();
|
if (res.history.length > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollToBottom();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onError(e: any) {
|
onError(e: any) {
|
||||||
toast({
|
toast({
|
||||||
@@ -433,7 +438,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
{chatData.history.length === 0 && <Empty />}
|
{chatData.history.length === 0 && <Empty intro={chatData.intro} />}
|
||||||
</Box>
|
</Box>
|
||||||
{/* 发送区 */}
|
{/* 发送区 */}
|
||||||
<Box
|
<Box
|
||||||
|
@@ -16,8 +16,8 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { postCreateModel } from '@/api/model';
|
import { postCreateModel } from '@/api/model';
|
||||||
import { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { OpenAiList } from '@/constants/model';
|
import { ModelList } from '@/constants/model';
|
||||||
|
|
||||||
interface CreateFormType {
|
interface CreateFormType {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -29,7 +29,7 @@ const CreateModel = ({
|
|||||||
onSuccess
|
onSuccess
|
||||||
}: {
|
}: {
|
||||||
setCreateModelOpen: Dispatch<boolean>;
|
setCreateModelOpen: Dispatch<boolean>;
|
||||||
onSuccess: Dispatch<ModelType>;
|
onSuccess: Dispatch<ModelSchema>;
|
||||||
}) => {
|
}) => {
|
||||||
const [requesting, setRequesting] = useState(false);
|
const [requesting, setRequesting] = useState(false);
|
||||||
const toast = useToast({
|
const toast = useToast({
|
||||||
@@ -42,7 +42,7 @@ const CreateModel = ({
|
|||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<CreateFormType>({
|
} = useForm<CreateFormType>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
serviceModelName: OpenAiList[0].model
|
serviceModelName: ModelList['openai'][0].model
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ const CreateModel = ({
|
|||||||
required: '底层模型不能为空'
|
required: '底层模型不能为空'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{OpenAiList.map((item) => (
|
{ModelList['openai'].map((item) => (
|
||||||
<option key={item.model} value={item.model}>
|
<option key={item.model} value={item.model}>
|
||||||
{item.name}
|
{item.name}
|
||||||
</option>
|
</option>
|
||||||
|
@@ -1,86 +1,37 @@
|
|||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Grid, Box, Card, Flex, Button, FormControl, Input, Textarea } from '@chakra-ui/react';
|
import {
|
||||||
import type { ModelType } from '@/types/model';
|
Box,
|
||||||
import { useForm } from 'react-hook-form';
|
Card,
|
||||||
import { useToast } from '@/hooks/useToast';
|
Flex,
|
||||||
import { putModelById } from '@/api/model';
|
FormControl,
|
||||||
import { useScreen } from '@/hooks/useScreen';
|
Input,
|
||||||
import { useGlobalStore } from '@/store/global';
|
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 ModelEditForm = ({ formHooks }: { formHooks: UseFormReturn<ModelSchema> }) => {
|
||||||
const isInit = useRef(false);
|
const { register, setValue, getValues } = formHooks;
|
||||||
const {
|
const [refresh, setRefresh] = useState(false);
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}>
|
<>
|
||||||
<Card p={4}>
|
<Card p={4}>
|
||||||
<Flex justifyContent={'space-between'} alignItems={'center'}>
|
<Flex justifyContent={'space-between'} alignItems={'center'}>
|
||||||
<Box fontWeight={'bold'} fontSize={'lg'}>
|
<Box fontWeight={'bold'}>基本信息</Box>
|
||||||
修改模型信息
|
|
||||||
</Box>
|
|
||||||
<Button onClick={handleSubmit(onclickSave, submitError)}>保存</Button>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<FormControl mt={5}>
|
<FormControl mt={4}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={'0 0 80px'}>展示名称:</Box>
|
<Box flex={'0 0 50px'} w={0}>
|
||||||
|
名称:
|
||||||
|
</Box>
|
||||||
<Input
|
<Input
|
||||||
{...register('name', {
|
{...register('name', {
|
||||||
required: '展示名称不能为空'
|
required: '展示名称不能为空'
|
||||||
@@ -88,30 +39,79 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
|
|||||||
></Input>
|
></Input>
|
||||||
</Flex>
|
</Flex>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl mt={5}>
|
<FormControl mt={4}>
|
||||||
<Flex alignItems={'center'}>
|
<Box mb={1}>介绍:</Box>
|
||||||
<Box flex={'0 0 80px'}>对话模型:</Box>
|
|
||||||
<Box>{model?.service.modelName}</Box>
|
|
||||||
</Flex>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl mt={5}>
|
|
||||||
<Textarea
|
<Textarea
|
||||||
rows={4}
|
rows={5}
|
||||||
maxLength={500}
|
maxLength={500}
|
||||||
{...register('systemPrompt')}
|
{...register('intro')}
|
||||||
placeholder={
|
placeholder={'模型的介绍,仅做展示,不影响模型的效果'}
|
||||||
'模型默认的 prompt 词,可以通过调整该内容,生成一个限定范围的模型,更方便的去使用。'
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Card>
|
</Card>
|
||||||
<Card p={4}>
|
<Card p={4}>
|
||||||
<Box fontWeight={'bold'} fontSize={'lg'}>
|
<Box fontWeight={'bold'}>模型效果</Box>
|
||||||
安全策略
|
<FormControl mt={4}>
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<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>
|
||||||
|
<Box mt={4}>
|
||||||
|
<Box mb={1}>系统提示词</Box>
|
||||||
|
<Textarea
|
||||||
|
rows={6}
|
||||||
|
maxLength={500}
|
||||||
|
{...register('systemPrompt')}
|
||||||
|
placeholder={
|
||||||
|
'模型默认的 prompt 词,通过调整该内容,可以生成一个限定范围的模型。\n\n注意,改功能会影响对话的整体朝向!'
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Card>
|
||||||
|
<Card p={4}>
|
||||||
|
<Box fontWeight={'bold'}>安全策略</Box>
|
||||||
<FormControl mt={2}>
|
<FormControl mt={2}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={'0 0 120px'}>单句最大长度:</Box>
|
<Box flex={'0 0 120px'} w={0}>
|
||||||
|
单句最大长度:
|
||||||
|
</Box>
|
||||||
<Input
|
<Input
|
||||||
flex={1}
|
flex={1}
|
||||||
type={'number'}
|
type={'number'}
|
||||||
@@ -132,7 +132,9 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl mt={5}>
|
<FormControl mt={5}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={'0 0 120px'}>上下文最大长度:</Box>
|
<Box flex={'0 0 120px'} w={0}>
|
||||||
|
上下文最大长度:
|
||||||
|
</Box>
|
||||||
<Input
|
<Input
|
||||||
flex={1}
|
flex={1}
|
||||||
type={'number'}
|
type={'number'}
|
||||||
@@ -153,7 +155,9 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl mt={5}>
|
<FormControl mt={5}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={'0 0 120px'}>聊天过期时间:</Box>
|
<Box flex={'0 0 120px'} w={0}>
|
||||||
|
聊天过期时间:
|
||||||
|
</Box>
|
||||||
<Input
|
<Input
|
||||||
flex={1}
|
flex={1}
|
||||||
type={'number'}
|
type={'number'}
|
||||||
@@ -175,7 +179,9 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl mt={5} pb={5}>
|
<FormControl mt={5} pb={5}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={'0 0 130px'}>聊天最大加载次数:</Box>
|
<Box flex={'0 0 130px'} w={0}>
|
||||||
|
聊天最大加载次数:
|
||||||
|
</Box>
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
<Input
|
<Input
|
||||||
type={'number'}
|
type={'number'}
|
||||||
@@ -196,7 +202,7 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Box, Button, Flex, Tag } from '@chakra-ui/react';
|
import { Box, Button, Flex, Tag } from '@chakra-ui/react';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { formatModelStatus } from '@/constants/model';
|
import { formatModelStatus } from '@/constants/model';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@@ -9,7 +9,7 @@ const ModelPhoneList = ({
|
|||||||
models,
|
models,
|
||||||
handlePreviewChat
|
handlePreviewChat
|
||||||
}: {
|
}: {
|
||||||
models: ModelType[];
|
models: ModelSchema[];
|
||||||
handlePreviewChat: (_: string) => void;
|
handlePreviewChat: (_: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@@ -14,14 +14,14 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { formatModelStatus } from '@/constants/model';
|
import { formatModelStatus } from '@/constants/model';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
const ModelTable = ({
|
const ModelTable = ({
|
||||||
models = [],
|
models = [],
|
||||||
handlePreviewChat
|
handlePreviewChat
|
||||||
}: {
|
}: {
|
||||||
models: ModelType[];
|
models: ModelSchema[];
|
||||||
handlePreviewChat: (_: string) => void;
|
handlePreviewChat: (_: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -34,13 +34,13 @@ const ModelTable = ({
|
|||||||
{
|
{
|
||||||
title: '最后更新时间',
|
title: '最后更新时间',
|
||||||
key: 'updateTime',
|
key: 'updateTime',
|
||||||
render: (item: ModelType) => dayjs(item.updateTime).format('YYYY-MM-DD HH:mm')
|
render: (item: ModelSchema) => dayjs(item.updateTime).format('YYYY-MM-DD HH:mm')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
render: (item: ModelType) => (
|
render: (item: ModelSchema) => (
|
||||||
<Tag
|
<Tag
|
||||||
colorScheme={formatModelStatus[item.status].colorTheme}
|
colorScheme={formatModelStatus[item.status].colorTheme}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
@@ -54,7 +54,7 @@ const ModelTable = ({
|
|||||||
{
|
{
|
||||||
title: 'AI模型',
|
title: 'AI模型',
|
||||||
key: 'service',
|
key: 'service',
|
||||||
render: (item: ModelType) => (
|
render: (item: ModelSchema) => (
|
||||||
<Box wordBreak={'break-all'} whiteSpace={'pre-wrap'} maxW={'200px'}>
|
<Box wordBreak={'break-all'} whiteSpace={'pre-wrap'} maxW={'200px'}>
|
||||||
{item.service.modelName}
|
{item.service.modelName}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -68,7 +68,7 @@ const ModelTable = ({
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'control',
|
key: 'control',
|
||||||
render: (item: ModelType) => (
|
render: (item: ModelSchema) => (
|
||||||
<>
|
<>
|
||||||
<Button mr={3} onClick={() => handlePreviewChat(item._id)}>
|
<Button mr={3} onClick={() => handlePreviewChat(item._id)}>
|
||||||
对话
|
对话
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useCallback, useState } from 'react';
|
import React, { useEffect, useCallback, useState } from 'react';
|
||||||
import { Box, TableContainer, Table, Thead, Tbody, Tr, Th, Td } from '@chakra-ui/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 { getModelTrainings } from '@/api/model';
|
||||||
import type { TrainingItemType } from '@/types/training';
|
import type { TrainingItemType } from '@/types/training';
|
||||||
|
|
||||||
const Training = ({ model }: { model: ModelType }) => {
|
const Training = ({ model }: { model: ModelSchema }) => {
|
||||||
const columns: {
|
const columns: {
|
||||||
title: string;
|
title: string;
|
||||||
key: keyof TrainingItemType;
|
key: keyof TrainingItemType;
|
||||||
|
@@ -1,21 +1,29 @@
|
|||||||
import React, { useCallback, useState, useEffect, useRef, useMemo } from 'react';
|
import React, { useCallback, useState, useRef, useMemo, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
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 { getChatSiteId } from '@/api/chat';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { Card, Box, Flex, Button, Tag, Grid } from '@chakra-ui/react';
|
import { Card, Box, Flex, Button, Tag, Grid } from '@chakra-ui/react';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useConfirm } from '@/hooks/useConfirm';
|
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 { useGlobalStore } from '@/store/global';
|
||||||
import { useScreen } from '@/hooks/useScreen';
|
import { useScreen } from '@/hooks/useScreen';
|
||||||
import ModelEditForm from './components/ModelEditForm';
|
import ModelEditForm from './components/ModelEditForm';
|
||||||
import Icon from '@/components/Iconfont';
|
import Icon from '@/components/Iconfont';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
const Training = dynamic(() => import('./components/Training'));
|
const Training = dynamic(() => import('./components/Training'));
|
||||||
|
|
||||||
const ModelDetail = () => {
|
const ModelDetail = ({ modelId }: { modelId: string }) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isPc, media } = useScreen();
|
const { isPc, media } = useScreen();
|
||||||
@@ -24,33 +32,35 @@ const ModelDetail = () => {
|
|||||||
content: '确认删除该模型?'
|
content: '确认删除该模型?'
|
||||||
});
|
});
|
||||||
const SelectFileDom = useRef<HTMLInputElement>(null);
|
const SelectFileDom = useRef<HTMLInputElement>(null);
|
||||||
|
const [model, setModel] = useState<ModelSchema>(defaultModel);
|
||||||
const { modelId } = router.query as { modelId: string };
|
const formHooks = useForm<ModelSchema>({
|
||||||
const [model, setModel] = useState<ModelType>();
|
defaultValues: model
|
||||||
|
});
|
||||||
|
|
||||||
const canTrain = useMemo(() => {
|
const canTrain = useMemo(() => {
|
||||||
const openai = OpenAiList.find((item) => item.model === model?.service.modelName);
|
const openai = ModelList[model.service.company].find(
|
||||||
return openai && openai.canTraining === true;
|
(item) => item.model === model?.service.modelName
|
||||||
|
);
|
||||||
|
return openai && openai.trainName;
|
||||||
}, [model]);
|
}, [model]);
|
||||||
|
|
||||||
/* 加载模型数据 */
|
/* 加载模型数据 */
|
||||||
const loadModel = useCallback(async () => {
|
const loadModel = useCallback(async () => {
|
||||||
if (!modelId) return;
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await getModelById(modelId as string);
|
const res = await getModelById(modelId);
|
||||||
|
console.log(res);
|
||||||
res.security.expiredTime /= 60 * 60 * 1000;
|
res.security.expiredTime /= 60 * 60 * 1000;
|
||||||
setModel(res);
|
setModel(res);
|
||||||
|
formHooks.reset(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('error->', err);
|
console.log('error->', err);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [modelId, setLoading]);
|
return null;
|
||||||
|
}, [formHooks, modelId, setLoading]);
|
||||||
|
|
||||||
useEffect(() => {
|
useQuery([modelId], loadModel);
|
||||||
loadModel();
|
|
||||||
router.prefetch('/chat');
|
|
||||||
}, [loadModel, modelId, router]);
|
|
||||||
|
|
||||||
/* 点击删除 */
|
/* 点击删除 */
|
||||||
const handleDelModel = useCallback(async () => {
|
const handleDelModel = useCallback(async () => {
|
||||||
@@ -71,7 +81,6 @@ const ModelDetail = () => {
|
|||||||
|
|
||||||
/* 点前往聊天预览页 */
|
/* 点前往聊天预览页 */
|
||||||
const handlePreviewChat = useCallback(async () => {
|
const handlePreviewChat = useCallback(async () => {
|
||||||
if (!model) return;
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const chatId = await getChatSiteId(model._id);
|
const chatId = await getChatSiteId(model._id);
|
||||||
@@ -131,6 +140,65 @@ const ModelDetail = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [model, setLoading, loadModel, toast]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* 头部 */}
|
{/* 头部 */}
|
||||||
@@ -138,50 +206,48 @@ const ModelDetail = () => {
|
|||||||
{isPc ? (
|
{isPc ? (
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box fontSize={'xl'} fontWeight={'bold'}>
|
<Box fontSize={'xl'} fontWeight={'bold'}>
|
||||||
{model?.name || '模型'} 配置
|
{model.name}
|
||||||
</Box>
|
</Box>
|
||||||
{!!model && (
|
<Tag
|
||||||
<Tag
|
ml={2}
|
||||||
ml={2}
|
variant="solid"
|
||||||
variant="solid"
|
colorScheme={formatModelStatus[model.status].colorTheme}
|
||||||
colorScheme={formatModelStatus[model.status].colorTheme}
|
cursor={model.status === ModelStatusEnum.training ? 'pointer' : 'default'}
|
||||||
cursor={model.status === ModelStatusEnum.training ? 'pointer' : 'default'}
|
onClick={handleClickUpdateStatus}
|
||||||
onClick={handleClickUpdateStatus}
|
>
|
||||||
>
|
{formatModelStatus[model.status].text}
|
||||||
{formatModelStatus[model.status].text}
|
</Tag>
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
<Box flex={1} />
|
<Box flex={1} />
|
||||||
<Button variant={'outline'} onClick={handlePreviewChat}>
|
<Button variant={'outline'} onClick={handlePreviewChat}>
|
||||||
对话体验
|
对话体验
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button ml={4} onClick={formHooks.handleSubmit(saveSubmitSuccess, saveSubmitError)}>
|
||||||
|
保存修改
|
||||||
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box as={'h3'} fontSize={'xl'} fontWeight={'bold'} flex={1}>
|
<Box as={'h3'} fontSize={'xl'} fontWeight={'bold'} flex={1}>
|
||||||
{model?.name || '模型'} 配置
|
{model?.name}
|
||||||
</Box>
|
</Box>
|
||||||
{!!model && (
|
<Tag ml={2} colorScheme={formatModelStatus[model.status].colorTheme}>
|
||||||
<Tag ml={2} colorScheme={formatModelStatus[model.status].colorTheme}>
|
{formatModelStatus[model.status].text}
|
||||||
{formatModelStatus[model.status].text}
|
</Tag>
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box mt={4} textAlign={'right'}>
|
<Box mt={4} textAlign={'right'}>
|
||||||
<Button variant={'outline'} onClick={handlePreviewChat}>
|
<Button variant={'outline'} onClick={handlePreviewChat}>
|
||||||
对话体验
|
对话体验
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button ml={4} onClick={formHooks.handleSubmit(saveSubmitSuccess, saveSubmitError)}>
|
||||||
|
保存修改
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
{/* 基本信息编辑 */}
|
|
||||||
<Box mt={5}>
|
|
||||||
<ModelEditForm model={model} />
|
|
||||||
</Box>
|
|
||||||
{/* 其他配置 */}
|
|
||||||
<Grid mt={5} gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}>
|
<Grid mt={5} gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}>
|
||||||
|
<ModelEditForm formHooks={formHooks} />
|
||||||
<Card p={4}>{!!model && <Training model={model} />}</Card>
|
<Card p={4}>{!!model && <Training model={model} />}</Card>
|
||||||
<Card p={4}>
|
<Card p={4}>
|
||||||
<Box fontWeight={'bold'} fontSize={'lg'}>
|
<Box fontWeight={'bold'} fontSize={'lg'}>
|
||||||
@@ -241,6 +307,8 @@ const ModelDetail = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
{/* 文件选择 */}
|
||||||
<Box position={'absolute'} w={0} h={0} overflow={'hidden'}>
|
<Box position={'absolute'} w={0} h={0} overflow={'hidden'}>
|
||||||
<input ref={SelectFileDom} type="file" accept=".jsonl" onChange={startTraining} />
|
<input ref={SelectFileDom} type="file" accept=".jsonl" onChange={startTraining} />
|
||||||
</Box>
|
</Box>
|
||||||
@@ -250,3 +318,11 @@ const ModelDetail = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default ModelDetail;
|
export default ModelDetail;
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: any) {
|
||||||
|
const modelId = context.query?.modelId || '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: { modelId }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
import { Box, Button, Flex, Card } from '@chakra-ui/react';
|
import { Box, Button, Flex, Card } from '@chakra-ui/react';
|
||||||
import { getChatSiteId } from '@/api/chat';
|
import { getChatSiteId } from '@/api/chat';
|
||||||
import { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import ModelTable from './components/ModelTable';
|
import ModelTable from './components/ModelTable';
|
||||||
import ModelPhoneList from './components/ModelPhoneList';
|
import ModelPhoneList from './components/ModelPhoneList';
|
||||||
@@ -27,7 +27,7 @@ const ModelList = () => {
|
|||||||
|
|
||||||
/* 创建成功回调 */
|
/* 创建成功回调 */
|
||||||
const createModelSuccess = useCallback(
|
const createModelSuccess = useCallback(
|
||||||
(data: ModelType) => {
|
(data: ModelSchema) => {
|
||||||
setMyModels([data, ...myModels]);
|
setMyModels([data, ...myModels]);
|
||||||
},
|
},
|
||||||
[myModels, setMyModels]
|
[myModels, setMyModels]
|
||||||
|
@@ -25,19 +25,22 @@ const ChatSchema = new Schema({
|
|||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
content: [
|
content: {
|
||||||
{
|
type: [
|
||||||
obj: {
|
{
|
||||||
type: String,
|
obj: {
|
||||||
required: true,
|
type: String,
|
||||||
enum: ['Human', 'AI', 'SYSTEM']
|
required: true,
|
||||||
},
|
enum: ['Human', 'AI', 'SYSTEM']
|
||||||
value: {
|
},
|
||||||
type: String,
|
value: {
|
||||||
required: true
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
]
|
default: []
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Chat = models['chat'] || model('chat', ChatSchema);
|
export const Chat = models['chat'] || model('chat', ChatSchema);
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { Schema, model, models } from 'mongoose';
|
import { Schema, model, models } from 'mongoose';
|
||||||
|
|
||||||
const ModelSchema = new Schema({
|
const ModelSchema = new Schema({
|
||||||
|
userId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'user',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
@@ -10,13 +15,14 @@ const ModelSchema = new Schema({
|
|||||||
default: '/imgs/modelAvatar.png'
|
default: '/imgs/modelAvatar.png'
|
||||||
},
|
},
|
||||||
systemPrompt: {
|
systemPrompt: {
|
||||||
|
// 系统提示词
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
userId: {
|
intro: {
|
||||||
type: Schema.Types.ObjectId,
|
// 模型介绍
|
||||||
ref: 'user',
|
type: String,
|
||||||
required: true
|
default: ''
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -31,6 +37,12 @@ const ModelSchema = new Schema({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
|
temperature: {
|
||||||
|
type: Number,
|
||||||
|
min: 1,
|
||||||
|
max: 10,
|
||||||
|
default: 5
|
||||||
|
},
|
||||||
service: {
|
service: {
|
||||||
company: {
|
company: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@@ -2,7 +2,7 @@ import { create } from 'zustand';
|
|||||||
import { devtools } from 'zustand/middleware';
|
import { devtools } from 'zustand/middleware';
|
||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import type { UserType, UserUpdateParams } from '@/types/user';
|
import type { UserType, UserUpdateParams } from '@/types/user';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { setToken } from '@/utils/user';
|
import { setToken } from '@/utils/user';
|
||||||
import { getMyModels } from '@/api/model';
|
import { getMyModels } from '@/api/model';
|
||||||
|
|
||||||
@@ -10,9 +10,9 @@ type State = {
|
|||||||
userInfo: UserType | null;
|
userInfo: UserType | null;
|
||||||
setUserInfo: (user: UserType, token?: string) => void;
|
setUserInfo: (user: UserType, token?: string) => void;
|
||||||
updateUserInfo: (user: UserUpdateParams) => void;
|
updateUserInfo: (user: UserUpdateParams) => void;
|
||||||
myModels: ModelType[];
|
myModels: ModelSchema[];
|
||||||
getMyModels: () => void;
|
getMyModels: () => void;
|
||||||
setMyModels: (data: ModelType[]) => void;
|
setMyModels: (data: ModelSchema[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUserStore = create<State>()(
|
export const useUserStore = create<State>()(
|
||||||
@@ -42,7 +42,7 @@ export const useUserStore = create<State>()(
|
|||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}),
|
}),
|
||||||
setMyModels(data: ModelType[]) {
|
setMyModels(data: ModelSchema[]) {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.myModels = data;
|
state.myModels = data;
|
||||||
});
|
});
|
||||||
|
2
src/types/chat.d.ts
vendored
2
src/types/chat.d.ts
vendored
@@ -1,5 +1,3 @@
|
|||||||
import type { ModelType } from './model';
|
|
||||||
|
|
||||||
export type ChatItemType = {
|
export type ChatItemType = {
|
||||||
obj: 'Human' | 'AI' | 'SYSTEM';
|
obj: 'Human' | 'AI' | 'SYSTEM';
|
||||||
value: string;
|
value: string;
|
||||||
|
40
src/types/model.d.ts
vendored
40
src/types/model.d.ts
vendored
@@ -1,40 +1,10 @@
|
|||||||
import { ModelStatusEnum } from '@/constants/model';
|
import { ModelStatusEnum } from '@/constants/model';
|
||||||
export interface ModelType {
|
import type { ModelSchema } from './mongoSchema';
|
||||||
_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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModelUpdateParams {
|
export interface ModelUpdateParams {
|
||||||
name: string;
|
name: string;
|
||||||
systemPrompt: string;
|
systemPrompt: string;
|
||||||
service: {
|
intro: string;
|
||||||
company: 'openai'; // 关联的厂商
|
temperature: number;
|
||||||
modelName: string; // 关联的模型
|
service: ModelSchema.service;
|
||||||
};
|
security: ModelSchema.security;
|
||||||
security: {
|
|
||||||
domain: string[];
|
|
||||||
contentMaxLen: number;
|
|
||||||
contextMaxLen: number;
|
|
||||||
expiredTime: number;
|
|
||||||
maxLoadAmount: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
8
src/types/mongoSchema.d.ts
vendored
8
src/types/mongoSchema.d.ts
vendored
@@ -25,15 +25,17 @@ export interface ModelSchema {
|
|||||||
name: string;
|
name: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
systemPrompt: string;
|
systemPrompt: string;
|
||||||
|
intro: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
status: `${ModelStatusEnum}`;
|
status: `${ModelStatusEnum}`;
|
||||||
updateTime: number;
|
updateTime: number;
|
||||||
trainingTimes: number;
|
trainingTimes: number;
|
||||||
|
temperature: number;
|
||||||
service: {
|
service: {
|
||||||
company: ServiceName;
|
company: ServiceName;
|
||||||
trainId: string;
|
trainId: string; // 训练的模型,训练后就是训练的模型id
|
||||||
chatModel: `${ChatModelNameEnum}`;
|
chatModel: string; // 聊天时用的模型,训练后就是训练的模型
|
||||||
modelName: string;
|
modelName: `${ChatModelNameEnum}`; // 底层模型名称,不会变
|
||||||
};
|
};
|
||||||
security: {
|
security: {
|
||||||
domain: string[];
|
domain: string[];
|
||||||
|
Reference in New Issue
Block a user