diff --git a/src/constants/common.ts b/src/constants/common.ts index bfc411187..112bda376 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -8,11 +8,12 @@ export const introPage = ` [Git 仓库](https://github.com/c121914yu/FastGPT) -时间比较赶,介绍没来得及完善,先直接上怎么使用: +### 快速开始 1. 使用邮箱注册账号。 2. 进入账号页面,添加关联账号,目前只有 openai 的账号可以添加,直接去 openai 官网,把 API Key 粘贴过来。 -3. 进入模型页,创建一个模型,建议直接用 ChatGPT。 -4. 在模型列表点击【对话】,即可使用 API 进行聊天。 +3. 如果填写了自己的 openai 账号,使用时会直接用你的账号。如果没有填写,需要付费使用平台的账号。 +4. 进入模型页,创建一个模型,建议直接用 ChatGPT。 +5. 在模型列表点击【对话】,即可使用 API 进行聊天。 ### 模型配置 diff --git a/src/constants/model.ts b/src/constants/model.ts index 782e62071..420e3ab61 100644 --- a/src/constants/model.ts +++ b/src/constants/model.ts @@ -16,7 +16,7 @@ export type ModelConstantsData = { price: number; // 多少钱 / 1字,单位: 0.00001元 }; -export const ModelList: ModelConstantsData[] = [ +export const modelList: ModelConstantsData[] = [ { serviceCompany: 'openai', name: 'chatGPT', diff --git a/src/pages/api/chat/chatGpt.ts b/src/pages/api/chat/chatGpt.ts index 6e5a32043..0e89a226e 100644 --- a/src/pages/api/chat/chatGpt.ts +++ b/src/pages/api/chat/chatGpt.ts @@ -8,7 +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'; +import { modelList } from '@/constants/model'; import { pushBill } from '@/service/events/bill'; /* 发送提示词 */ @@ -74,7 +74,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } // 计算温度 - const modelConstantsData = ModelList.find((item) => item.model === model.service.modelName); + const modelConstantsData = modelList.find((item) => item.model === model.service.modelName); if (!modelConstantsData) { throw new Error('模型异常'); } diff --git a/src/pages/api/chat/gpt3.ts b/src/pages/api/chat/gpt3.ts index a8e87a049..bf3c0c7fb 100644 --- a/src/pages/api/chat/gpt3.ts +++ b/src/pages/api/chat/gpt3.ts @@ -5,7 +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'; +import { modelList } from '@/constants/model'; import { pushBill } from '@/service/events/bill'; /* 发送提示词 */ @@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const formatPrompts = prompt.map((item) => `${item.value}\n\n###\n\n`).join(''); // 计算温度 - const modelConstantsData = ModelList.find((item) => item.model === model.service.modelName); + const modelConstantsData = modelList.find((item) => item.model === model.service.modelName); if (!modelConstantsData) { throw new Error('模型异常'); } diff --git a/src/pages/api/model/create.ts b/src/pages/api/model/create.ts index d9ae7f63a..259c46bea 100644 --- a/src/pages/api/model/create.ts +++ b/src/pages/api/model/create.ts @@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { connectToDatabase } from '@/service/mongo'; import { authToken } from '@/service/utils/tools'; -import { ModelStatusEnum, ModelList, ChatModelNameEnum } from '@/constants/model'; +import { ModelStatusEnum, modelList, ChatModelNameEnum } from '@/constants/model'; import { Model } from '@/service/models/model'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< // 凭证校验 const userId = await authToken(authorization); - const modelItem = ModelList.find((item) => item.model === serviceModelName); + const modelItem = modelList.find((item) => item.model === serviceModelName); if (!modelItem) { throw new Error('模型不存在'); diff --git a/src/pages/api/train/generateQA.ts b/src/pages/api/train/generateQA.ts new file mode 100644 index 000000000..363e94452 --- /dev/null +++ b/src/pages/api/train/generateQA.ts @@ -0,0 +1,103 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@/service/response'; +import { connectToDatabase, Data } from '@/service/mongo'; +import { getOpenAIApi } from '@/service/utils/chat'; +import { httpsAgent, getOpenApiKey } from '@/service/utils/tools'; +import type { ChatCompletionRequestMessage, CreateChatCompletionResponse } from 'openai'; +import fs from 'fs'; +import { v4 } from 'uuid'; + +/* 定时删除那些不活跃的内容 */ +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const systemPrompt: ChatCompletionRequestMessage = { + role: 'system', + content: `总结助手。我会向你发送一段长文本,请从中总结出10个以上问题和答案,并转化成中文!请按以下格式返回: +"Q1:" +"A1:" +###### +"Q2:" +"A2:" +###### +` + }; + + try { + const { text } = req.body as { text: string }; + const { authorization } = req.headers; + const replaceText = text.replace(/\n/g, ' '); + await connectToDatabase(); + + const { userId, userApiKey, systemKey } = await getOpenApiKey(authorization); + + const chatAPI = getOpenAIApi(userApiKey || systemKey); + + const docId = v4(); + + const queue: { + requestPrompts: ChatCompletionRequestMessage; + temperature: number; + }[] = [ + { + requestPrompts: { + role: 'user', + content: `跟 AI ChatGPT 聊了 11 个我过去关心的话题\n原创 刘飞Lufy 刘言飞语 2022-12-05 21:40 发表于浙江\n收录于合集\n#行业观察\n33\n个\n#AIGC\n11\n个\n\n\n\n\n这几天沉浸式体验 ChatGPT(OpenAI发布的一款人工智能对话引擎),突发奇想,想知道它对我曾经聊的话题都怎么看。于是我用一些过去公众号发过的文章主题,跟它进行了一轮对话。\n\n以下是我们聊天的内容原始记录,没有做文字篡改,为方便没有用截图,做了文本排版。各位可以直观体会下它的智能程度。\n\n\n(本文内容都是在以上这样的界面里完成的)\n\n\n——\n\n参考:微信搜索的未来\n\n刘飞:\n\n微信搜索现在的使用量大吗?\n\nChatGPT:\n\n微信搜索是一项非常流行的功能,它可以帮助用户快速搜索到想要的信息。由于微信是中国最流行的社交软件,微信搜索的使用量也非常大。根据官方数据,截至2022年,微信的日活跃用户数量已经超过10亿,其中,每天都会有大量用户使用微信搜索功能搜索各种信息。因此,微信搜索的使用量非常大。\n\n刘飞:\n\n跟百度搜索比呢?\n\nChatGPT:\n\n微信搜索和百度搜索都是帮助用户快速搜索到想要的信息的工具。但是,两者有着明显的不同。\n\n首先,微信搜索和百度搜索的使用场景不同。微信搜索主要用于社交场景,因此,它更多地用于搜索好友、群聊、文章等信息,而百度搜索则更加注重为用户提供更丰富的信息,因此它更多地用于搜索各种知识信息和新闻信息。\n\n\n其次,微信搜索和百度搜索的搜索结果也不同。微信搜索的搜索结果主要是来自用户自己的微信聊天记录和朋友圈发布的信息,而百度搜索的搜索结果则是来自互联网上的各种信息,包括网站、新闻、论坛等。因此,微信搜索的搜索结果更加个性化,而百度搜索的搜索结果则更加全面。\n\n总之,微信搜索和百度搜索都是帮助用户快速搜索到想要的信息的工具,但是它们在使用场景、搜索结果等方面存在明显的差异。\n\n刘飞:\n\n那字节跳动的搜索跟它们相比呢?\n\nChatGPT:\n\n字节跳动的搜索也是一款帮助用户快速搜索到想要的信息的工具。但是,字节跳动的搜索主要是针对短视频内容而设计的,因此,它的搜索结果主要是来自抖音、头条等短视频平台上的视频内容。与微信搜索和百度搜索相比,字节跳动的搜索更加专注于短视频内容,并且搜索结果也更加丰富多样。\n\n——\n\n\n参考:我们离财务自由的距离\n\n\n刘飞:\n\n怎样才算财务自由?\n\nChatGPT:\n\n财务自由是指一个人在不必工作的情况下,能够通过自己的财富维持生活的质量和生活水平。通常情况下,一个人可以通过节约开支、投资理财等方式来实现财务自由。\n\n具体来说,要达到财务自由,一个人需要符合以下几个条件:\n\n\n实现收入和支出平衡。要想实现财务自由,首先需要通过节约开支来控制支出,并通过提高收入来提升生活水平。\n\n\n建立良好的投资理财计划。要想实现财务自由,需要通过投资理财来增加财富的增长速度,并且要有一个完善的理财计划来指导投资决策。\n\n\n控制风险。在投资理财的过程中,要尽量降低风险,并通过多样化投资来降低投资的风险。` + }, + temperature: 0.5 + } + ]; + + /** + * 生成QA + */ + const generateQA = async () => { + const prompt = queue.pop(); + if (!prompt) return; + + try { + const response = await chatAPI.createChatCompletion( + { + model: 'gpt-3.5-turbo', + temperature: prompt.temperature, + n: 1, + messages: [systemPrompt, prompt.requestPrompts] + }, + { + timeout: 60000, + httpsAgent + } + ); + return response.data.choices[0].message?.content; + } catch (error) { + console.log('部分训练出现错误'); + } + }; + // 格式化文本长度 + // for (let i = 0; i <= replaceText.length / 2048; i++) { + // const slicePrompts: ChatCompletionRequestMessage = { + // role: 'user', + // content: replaceText.slice(i * 2048, (i + 1) * 2048) + // }; + + // [0.1, 0.3, 0.5, 0.7, 0.9].forEach((temperature) => { + // queue.push({ + // temperature, + // requestPrompts: slicePrompts + // }); + // }); + // } + + jsonRes(res, { + data: await generateQA() + }); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} + +/** + * 检查文本是否按格式返回 + */ +function splitText(text: string) {} diff --git a/src/pages/model/components/CreateModel.tsx b/src/pages/model/components/CreateModel.tsx index ec4e396a0..27d6c98bb 100644 --- a/src/pages/model/components/CreateModel.tsx +++ b/src/pages/model/components/CreateModel.tsx @@ -18,7 +18,7 @@ import { import { useForm } from 'react-hook-form'; import { postCreateModel } from '@/api/model'; import type { ModelSchema } from '@/types/mongoSchema'; -import { ModelList } from '@/constants/model'; +import { modelList } from '@/constants/model'; import { formatPrice } from '@/utils/user'; interface CreateFormType { @@ -45,7 +45,7 @@ const CreateModel = ({ formState: { errors } } = useForm({ defaultValues: { - serviceModelName: ModelList[0].model + serviceModelName: modelList[0].model } }); @@ -98,7 +98,7 @@ const CreateModel = ({ required: '底层模型不能为空' })} > - {ModelList.map((item) => ( + {modelList.map((item) => ( @@ -110,7 +110,7 @@ const CreateModel = ({ {formatPrice( - ModelList.find((item) => item.model === getValues('serviceModelName'))?.price || 0 + modelList.find((item) => item.model === getValues('serviceModelName'))?.price || 0 ) * 1000} 元/1000字(包括上下文和标点符号) diff --git a/src/pages/model/detail.tsx b/src/pages/model/detail.tsx index 646769f36..2eb5437b4 100644 --- a/src/pages/model/detail.tsx +++ b/src/pages/model/detail.tsx @@ -13,7 +13,7 @@ import { Card, Box, Flex, Button, Tag, Grid } from '@chakra-ui/react'; import { useToast } from '@/hooks/useToast'; import { useConfirm } from '@/hooks/useConfirm'; import { useForm } from 'react-hook-form'; -import { formatModelStatus, ModelStatusEnum, ModelList, defaultModel } from '@/constants/model'; +import { formatModelStatus, ModelStatusEnum, modelList, defaultModel } from '@/constants/model'; import { useGlobalStore } from '@/store/global'; import { useScreen } from '@/hooks/useScreen'; import ModelEditForm from './components/ModelEditForm'; @@ -38,7 +38,7 @@ const ModelDetail = ({ modelId }: { modelId: string }) => { }); const canTrain = useMemo(() => { - const openai = ModelList.find((item) => item.model === model?.service.modelName); + const openai = modelList.find((item) => item.model === model?.service.modelName); return openai && openai.trainName; }, [model]); diff --git a/src/pages/model/list.tsx b/src/pages/model/list.tsx index 81f16d397..1c70c68d0 100644 --- a/src/pages/model/list.tsx +++ b/src/pages/model/list.tsx @@ -14,7 +14,7 @@ import { useUserStore } from '@/store/user'; const CreateModel = dynamic(() => import('./components/CreateModel')); -const ModelList = () => { +const modelList = () => { const { toast } = useToast(); const { isPc } = useScreen(); const router = useRouter(); @@ -87,4 +87,4 @@ const ModelList = () => { ); }; -export default ModelList; +export default modelList; diff --git a/src/pages/number/components/PayModal.tsx b/src/pages/number/components/PayModal.tsx index 903ec39ea..edd7a7f0a 100644 --- a/src/pages/number/components/PayModal.tsx +++ b/src/pages/number/components/PayModal.tsx @@ -10,12 +10,21 @@ import { Button, Input, Box, - Grid + Grid, + Table, + Thead, + Tbody, + Tr, + Th, + Td, + TableContainer } from '@chakra-ui/react'; import { getPayCode, checkPayResult } from '@/api/user'; import { useToast } from '@/hooks/useToast'; import { useQuery } from '@tanstack/react-query'; import { useRouter } from 'next/router'; +import { modelList } from '@/constants/model'; +import { formatPrice } from '../../../utils/user'; const PayModal = ({ onClose }: { onClose: () => void }) => { const router = useRouter(); @@ -82,9 +91,28 @@ const PayModal = ({ onClose }: { onClose: () => void }) => { 充值 {!payId && } - + {!payId && ( <> + {/* 价格表 */} + + + + + + + + + + {modelList.map((item, i) => ( + + + + + ))} + +
模型类型价格(元/1000字符,包含所有上下文)
{item.name}{formatPrice(item.price) * 1000}
+
{[5, 10, 20, 50].map((item) => ( + + 如果填写了自己的 openai 账号,将不会计费 + diff --git a/src/service/events/bill.ts b/src/service/events/bill.ts index d75e9a955..18cff393b 100644 --- a/src/service/events/bill.ts +++ b/src/service/events/bill.ts @@ -1,5 +1,5 @@ import { connectToDatabase, Bill, User } from '../mongo'; -import { ModelList } from '@/constants/model'; +import { modelList } from '@/constants/model'; export const pushBill = async ({ modelName, @@ -12,16 +12,23 @@ export const pushBill = async ({ chatId: string; textLen: number; }) => { + await connectToDatabase(); + + const modelItem = modelList.find((item) => item.model === modelName); + + if (!modelItem) return; + + let billId; + try { await connectToDatabase(); - const modelItem = ModelList.find((item) => item.model === modelName); + const modelItem = modelList.find((item) => item.model === modelName); if (!modelItem) return; const price = modelItem.price * textLen; - let billId; try { // 插入 Bill 记录 const res = await Bill.create({ diff --git a/src/service/models/data.ts b/src/service/models/data.ts new file mode 100644 index 000000000..98825a396 --- /dev/null +++ b/src/service/models/data.ts @@ -0,0 +1,27 @@ +import { Schema, model, models } from 'mongoose'; + +const DataSchema = new Schema({ + userId: { + type: Schema.Types.ObjectId, + ref: 'user', + required: true + }, + docId: { + type: String, + required: true + }, + createTime: { + type: Date, + default: () => new Date() + }, + q: { + type: String, + required: true + }, + a: { + type: String, + required: true + } +}); + +export const Data = models['data'] || model('data', DataSchema); diff --git a/src/service/mongo.ts b/src/service/mongo.ts index 11773eae8..5b6dbab70 100644 --- a/src/service/mongo.ts +++ b/src/service/mongo.ts @@ -32,3 +32,4 @@ export * from './models/user'; export * from './models/training'; export * from './models/bill'; export * from './models/pay'; +export * from './models/data'; diff --git a/src/service/utils/tools.ts b/src/service/utils/tools.ts index b15914454..f33314eb8 100644 --- a/src/service/utils/tools.ts +++ b/src/service/utils/tools.ts @@ -2,6 +2,8 @@ import crypto from 'crypto'; import jwt from 'jsonwebtoken'; import { User } from '../models/user'; import tunnel from 'tunnel'; +import type { UserModelSchema } from '@/types/mongoSchema'; +import { formatPrice } from '@/utils/user'; /* 密码加密 */ export const hashPassword = (psw: string) => { @@ -49,7 +51,37 @@ export const getUserOpenaiKey = async (userId: string) => { return Promise.reject('缺少ApiKey, 无法请求'); } - return Promise.resolve(userApiKey); + return Promise.resolve(userApiKey as string); +}; + +/* 获取key,如果没有就用平台的,用平台记得加账单 */ +export const getOpenApiKey = async (authorization?: string) => { + const userId = await authToken(authorization); + const user = await User.findById(userId); + + if (!user) return Promise.reject('用户不存在'); + + const userApiKey = user.accounts?.find((item: any) => item.type === 'openai')?.value; + + // 有自己的key, 直接使用 + if (userApiKey) { + return { + userId, + userApiKey: await getUserOpenaiKey(userId), + systemKey: '' + }; + } + + // 余额校验 + if (formatPrice(user.balance) <= 0) { + return Promise.reject('该账号余额不足'); + } + + return { + userId, + userApiKey: '', + systemKey: process.env.OPENAIKEY as string + }; }; /* 代理 */