mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-20 18:54:09 +00:00
perf: bill
This commit is contained in:
@@ -8,6 +8,7 @@ import { ChatRoleEnum } from '@/constants/chat';
|
|||||||
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
|
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
|
||||||
import type { RecognizeIntentionAgentItemType } from '@/types/app';
|
import type { RecognizeIntentionAgentItemType } from '@/types/app';
|
||||||
import { countModelPrice, pushTaskBillListItem } from '@/service/events/pushBill';
|
import { countModelPrice, pushTaskBillListItem } from '@/service/events/pushBill';
|
||||||
|
import { getModel } from '@/service/utils/data';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
@@ -115,7 +116,7 @@ export async function classifyQuestion({
|
|||||||
billId,
|
billId,
|
||||||
moduleName: 'Recognize Intention',
|
moduleName: 'Recognize Intention',
|
||||||
amount: countModelPrice({ model: agentModel, tokens: totalTokens }),
|
amount: countModelPrice({ model: agentModel, tokens: totalTokens }),
|
||||||
model: agentModel,
|
model: getModel(agentModel)?.name,
|
||||||
tokenLen: totalTokens
|
tokenLen: totalTokens
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -31,21 +31,10 @@ export type Response = { [SpecificInputEnum.answerText]: string; totalTokens: nu
|
|||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
let { model, temperature = 0, stream } = req.body as Props;
|
let { model, temperature = 0, stream } = req.body as Props;
|
||||||
try {
|
try {
|
||||||
// temperature adapt
|
|
||||||
const modelConstantsData = getChatModel(model);
|
|
||||||
|
|
||||||
if (!modelConstantsData) {
|
|
||||||
throw new Error('The chat model is undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
// FastGpt temperature range: 1~10
|
|
||||||
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
|
|
||||||
|
|
||||||
const response = await chatCompletion({
|
const response = await chatCompletion({
|
||||||
...req.body,
|
...req.body,
|
||||||
res,
|
res,
|
||||||
model,
|
model
|
||||||
temperature
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
@@ -88,6 +77,16 @@ export async function chatCompletion({
|
|||||||
limitPrompt = '',
|
limitPrompt = '',
|
||||||
billId
|
billId
|
||||||
}: Props & { res: NextApiResponse }): Promise<Response> {
|
}: Props & { res: NextApiResponse }): Promise<Response> {
|
||||||
|
// temperature adapt
|
||||||
|
const modelConstantsData = getChatModel(model);
|
||||||
|
|
||||||
|
if (!modelConstantsData) {
|
||||||
|
return Promise.reject('The chat model is undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
// FastGpt temperature range: 1~10
|
||||||
|
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
|
||||||
|
|
||||||
const messages: ChatItemType[] = [
|
const messages: ChatItemType[] = [
|
||||||
...(quotePrompt
|
...(quotePrompt
|
||||||
? [
|
? [
|
||||||
@@ -189,7 +188,7 @@ export async function chatCompletion({
|
|||||||
billId,
|
billId,
|
||||||
moduleName: 'AI Chat',
|
moduleName: 'AI Chat',
|
||||||
amount: countModelPrice({ model, tokens: totalTokens }),
|
amount: countModelPrice({ model, tokens: totalTokens }),
|
||||||
model,
|
model: modelConstantsData.name,
|
||||||
tokenLen: totalTokens
|
tokenLen: totalTokens
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@ import { ChatRoleEnum } from '@/constants/chat';
|
|||||||
import { modelToolMap } from '@/utils/plugin';
|
import { modelToolMap } from '@/utils/plugin';
|
||||||
import { getVector } from '../../plugin/vector';
|
import { getVector } from '../../plugin/vector';
|
||||||
import { countModelPrice, pushTaskBillListItem } from '@/service/events/pushBill';
|
import { countModelPrice, pushTaskBillListItem } from '@/service/events/pushBill';
|
||||||
|
import { getModel } from '@/service/utils/data';
|
||||||
|
|
||||||
export type QuoteItemType = {
|
export type QuoteItemType = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -95,7 +96,7 @@ export async function kbSearch({
|
|||||||
billId,
|
billId,
|
||||||
moduleName: 'Vector Generate',
|
moduleName: 'Vector Generate',
|
||||||
amount: countModelPrice({ model: vectorModel, tokens: tokenLen }),
|
amount: countModelPrice({ model: vectorModel, tokens: tokenLen }),
|
||||||
model: vectorModel,
|
model: getModel(vectorModel)?.name,
|
||||||
tokenLen
|
tokenLen
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
@@ -9,7 +9,7 @@ import { UserUpdateParams } from '@/types/user';
|
|||||||
/* 更新一些基本信息 */
|
/* 更新一些基本信息 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { openaiKey, avatar } = req.body as UserUpdateParams;
|
const { avatar } = req.body as UserUpdateParams;
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const { userId } = await authUser({ req, authToken: true });
|
||||||
|
|
||||||
@@ -20,8 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
_id: userId
|
_id: userId
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...(avatar && { avatar }),
|
...(avatar && { avatar })
|
||||||
...(openaiKey !== undefined && { openaiKey })
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
83
client/src/pages/number/components/BillDetail.tsx
Normal file
83
client/src/pages/number/components/BillDetail.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
ModalOverlay,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalBody,
|
||||||
|
Flex,
|
||||||
|
Box,
|
||||||
|
Table,
|
||||||
|
Thead,
|
||||||
|
Tbody,
|
||||||
|
Tr,
|
||||||
|
Th,
|
||||||
|
Td,
|
||||||
|
TableContainer
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { UserBillType } from '@/types/user';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { BillSourceMap } from '@/constants/user';
|
||||||
|
import { formatPrice } from '@/utils/user';
|
||||||
|
|
||||||
|
const BillDetail = ({ bill, onClose }: { bill: UserBillType; onClose: () => void }) => {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true} onClose={onClose} isCentered>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent minW={'min(90vw,600px)'}>
|
||||||
|
<ModalHeader>账单详情</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<Flex alignItems={'center'} pb={4}>
|
||||||
|
<Box flex={'0 0 80px'}>订单号:</Box>
|
||||||
|
<Box>{bill.id}</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} pb={4}>
|
||||||
|
<Box flex={'0 0 80px'}>生成时间:</Box>
|
||||||
|
<Box>{dayjs(bill.time).format('YYYY/MM/DD HH:mm:ss')}</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} pb={4}>
|
||||||
|
<Box flex={'0 0 80px'}>应用名:</Box>
|
||||||
|
<Box>{bill.appName}</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} pb={4}>
|
||||||
|
<Box flex={'0 0 80px'}>来源:</Box>
|
||||||
|
<Box>{BillSourceMap[bill.source]}</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} pb={4}>
|
||||||
|
<Box flex={'0 0 80px'}>总金额:</Box>
|
||||||
|
<Box fontWeight={'bold'}>{bill.total}元</Box>
|
||||||
|
</Flex>
|
||||||
|
<Box pb={4}>
|
||||||
|
<Box flex={'0 0 80px'} mb={1}>
|
||||||
|
扣费模块
|
||||||
|
</Box>
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<Thead>
|
||||||
|
<Tr>
|
||||||
|
<Th>模块名</Th>
|
||||||
|
<Th>AI模型</Th>
|
||||||
|
<Th>Token长度</Th>
|
||||||
|
<Th>费用</Th>
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
<Tbody>
|
||||||
|
{bill.list.map((item, i) => (
|
||||||
|
<Tr key={i}>
|
||||||
|
<Td>{item.moduleName}</Td>
|
||||||
|
<Td>{item.model}</Td>
|
||||||
|
<Td>{item.tokenLen}</Td>
|
||||||
|
<Td>{formatPrice(item.amount)}</Td>
|
||||||
|
</Tr>
|
||||||
|
))}
|
||||||
|
</Tbody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Box>
|
||||||
|
</ModalBody>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BillDetail;
|
@@ -1,5 +1,16 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Flex, Box } from '@chakra-ui/react';
|
import {
|
||||||
|
Table,
|
||||||
|
Thead,
|
||||||
|
Tbody,
|
||||||
|
Tr,
|
||||||
|
Th,
|
||||||
|
Td,
|
||||||
|
TableContainer,
|
||||||
|
Flex,
|
||||||
|
Box,
|
||||||
|
Button
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { BillSourceMap } from '@/constants/user';
|
import { BillSourceMap } from '@/constants/user';
|
||||||
import { getUserBills } from '@/api/user';
|
import { getUserBills } from '@/api/user';
|
||||||
import type { UserBillType } from '@/types/user';
|
import type { UserBillType } from '@/types/user';
|
||||||
@@ -9,6 +20,9 @@ import dayjs from 'dayjs';
|
|||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
|
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
|
||||||
import { addDays } from 'date-fns';
|
import { addDays } from 'date-fns';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
|
const BillDetail = dynamic(() => import('./BillDetail'));
|
||||||
|
|
||||||
const BillTable = () => {
|
const BillTable = () => {
|
||||||
const { Loading } = useLoading();
|
const { Loading } = useLoading();
|
||||||
@@ -30,6 +44,8 @@ const BillTable = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [billDetail, setBillDetail] = useState<UserBillType>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableContainer position={'relative'} minH={'100px'}>
|
<TableContainer position={'relative'} minH={'100px'}>
|
||||||
@@ -39,7 +55,8 @@ const BillTable = () => {
|
|||||||
<Th>时间</Th>
|
<Th>时间</Th>
|
||||||
<Th>来源</Th>
|
<Th>来源</Th>
|
||||||
<Th>应用名</Th>
|
<Th>应用名</Th>
|
||||||
<Th>金额</Th>
|
<Th>总金额</Th>
|
||||||
|
<Th></Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody fontSize={'sm'}>
|
<Tbody fontSize={'sm'}>
|
||||||
@@ -49,6 +66,11 @@ const BillTable = () => {
|
|||||||
<Td>{BillSourceMap[item.source]}</Td>
|
<Td>{BillSourceMap[item.source]}</Td>
|
||||||
<Td>{item.appName || '-'}</Td>
|
<Td>{item.appName || '-'}</Td>
|
||||||
<Td>{item.total}元</Td>
|
<Td>{item.total}元</Td>
|
||||||
|
<Td>
|
||||||
|
<Button size={'sm'} variant={'base'} onClick={() => setBillDetail(item)}>
|
||||||
|
详情
|
||||||
|
</Button>
|
||||||
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
))}
|
))}
|
||||||
</Tbody>
|
</Tbody>
|
||||||
@@ -75,6 +97,7 @@ const BillTable = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Loading loading={isLoading} fixed={false} />
|
<Loading loading={isLoading} fixed={false} />
|
||||||
|
{!!billDetail && <BillDetail bill={billDetail} onClose={() => setBillDetail(undefined)} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -83,20 +83,10 @@ const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => {
|
|||||||
async (data: UserUpdateParams) => {
|
async (data: UserUpdateParams) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
if (data.openaiKey) {
|
|
||||||
const text = await authOpenAiKey(data.openaiKey);
|
|
||||||
text &&
|
|
||||||
toast({
|
|
||||||
title: text,
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await putUserInfo({
|
await putUserInfo({
|
||||||
openaiKey: data.openaiKey,
|
|
||||||
avatar: data.avatar
|
avatar: data.avatar
|
||||||
});
|
});
|
||||||
updateUserInfo({
|
updateUserInfo({
|
||||||
openaiKey: data.openaiKey,
|
|
||||||
avatar: data.avatar
|
avatar: data.avatar
|
||||||
});
|
});
|
||||||
reset(data);
|
reset(data);
|
||||||
@@ -193,25 +183,7 @@ const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => {
|
|||||||
充值
|
充值
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box fontSize={'xs'} color={'blackAlpha.500'}>
|
|
||||||
如果填写了自己的 openai 账号,网页上 openai 模型对话不会计费。
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
|
||||||
<Flex mt={6} alignItems={'center'}>
|
|
||||||
<Box flex={'0 0 85px'}>openaiKey:</Box>
|
|
||||||
<Input
|
|
||||||
{...register(`openaiKey`)}
|
|
||||||
maxW={'350px'}
|
|
||||||
placeholder={'openai账号。回车或失去焦点保存'}
|
|
||||||
size={'sm'}
|
|
||||||
onBlur={handleSubmit(onclickSave)}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
handleSubmit(onclickSave)();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
></Input>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
</Card>
|
||||||
<Card px={6} py={4}>
|
<Card px={6} py={4}>
|
||||||
<Box fontSize={'xl'} fontWeight={'bold'}>
|
<Box fontSize={'xl'} fontWeight={'bold'}>
|
||||||
@@ -220,7 +192,6 @@ const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => {
|
|||||||
{[
|
{[
|
||||||
{ label: '佣金比例', value: `${userInfo?.promotion.rate || 15}%` },
|
{ label: '佣金比例', value: `${userInfo?.promotion.rate || 15}%` },
|
||||||
{ label: '已注册用户数', value: `${invitedAmount}人` },
|
{ label: '已注册用户数', value: `${invitedAmount}人` },
|
||||||
{ label: '累计佣金', value: `¥${historyAmount}` },
|
|
||||||
{ label: '可用佣金', value: `¥${residueAmount}` }
|
{ label: '可用佣金', value: `¥${residueAmount}` }
|
||||||
].map((item) => (
|
].map((item) => (
|
||||||
<Flex key={item.label} alignItems={'center'} mt={4} justifyContent={'space-between'}>
|
<Flex key={item.label} alignItems={'center'} mt={4} justifyContent={'space-between'}>
|
||||||
|
@@ -42,10 +42,6 @@ const UserSchema = new Schema({
|
|||||||
default: 15
|
default: 15
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openaiKey: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
limit: {
|
limit: {
|
||||||
exportKbTime: {
|
exportKbTime: {
|
||||||
// Every half hour
|
// Every half hour
|
||||||
|
3
client/src/types/user.d.ts
vendored
3
client/src/types/user.d.ts
vendored
@@ -4,7 +4,6 @@ export interface UserType {
|
|||||||
_id: string;
|
_id: string;
|
||||||
username: string;
|
username: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
openaiKey: string;
|
|
||||||
balance: number;
|
balance: number;
|
||||||
promotion: {
|
promotion: {
|
||||||
rate: number;
|
rate: number;
|
||||||
@@ -14,7 +13,6 @@ export interface UserType {
|
|||||||
export interface UserUpdateParams {
|
export interface UserUpdateParams {
|
||||||
balance?: number;
|
balance?: number;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
openaiKey?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserBillType {
|
export interface UserBillType {
|
||||||
@@ -23,4 +21,5 @@ export interface UserBillType {
|
|||||||
appName: string;
|
appName: string;
|
||||||
source: BillSchema['source'];
|
source: BillSchema['source'];
|
||||||
total: number;
|
total: number;
|
||||||
|
list: BillSchema['list'];
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,8 @@ export const adaptBill = (bill: BillSchema): UserBillType => {
|
|||||||
source: bill.source,
|
source: bill.source,
|
||||||
time: bill.time,
|
time: bill.time,
|
||||||
total: formatPrice(bill.total),
|
total: formatPrice(bill.total),
|
||||||
appName: bill.appName
|
appName: bill.appName,
|
||||||
|
list: bill.list
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user