mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-27 08:25:07 +00:00
feat: refresh max token
This commit is contained in:
@@ -45,8 +45,8 @@ export const triggerModelCollection = (appId: string) =>
|
|||||||
POST<number>(`/app/share/collection?appId=${appId}`);
|
POST<number>(`/app/share/collection?appId=${appId}`);
|
||||||
|
|
||||||
// ====================== data
|
// ====================== data
|
||||||
export const getTokenUsage = (data: { appId: string }) =>
|
export const getAppTotalUsage = (data: { appId: string }) =>
|
||||||
POST<{ tokenLen: number; date: Date }[]>(`/app/data/tokenUsage`, {
|
POST<{ total: number; date: Date }[]>(`/app/data/totalUsage`, {
|
||||||
...data,
|
...data,
|
||||||
start: addDays(new Date(), -7),
|
start: addDays(new Date(), -7),
|
||||||
end: new Date()
|
end: new Date()
|
||||||
|
@@ -84,6 +84,7 @@ export const HistoryModule: AppModuleTemplateItemType = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultModel = chatModelList[0];
|
||||||
export const ChatModule: AppModuleTemplateItemType = {
|
export const ChatModule: AppModuleTemplateItemType = {
|
||||||
logo: '/imgs/module/AI.png',
|
logo: '/imgs/module/AI.png',
|
||||||
name: 'AI 对话',
|
name: 'AI 对话',
|
||||||
@@ -94,9 +95,9 @@ export const ChatModule: AppModuleTemplateItemType = {
|
|||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
key: 'model',
|
key: 'model',
|
||||||
type: FlowInputItemTypeEnum.select,
|
type: FlowInputItemTypeEnum.custom,
|
||||||
label: '对话模型',
|
label: '对话模型',
|
||||||
value: chatModelList[0]?.model,
|
value: defaultModel?.model,
|
||||||
list: chatModelList.map((item) => ({ label: item.name, value: item.model }))
|
list: chatModelList.map((item) => ({ label: item.name, value: item.model }))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,13 +117,16 @@ export const ChatModule: AppModuleTemplateItemType = {
|
|||||||
key: 'maxToken',
|
key: 'maxToken',
|
||||||
type: FlowInputItemTypeEnum.slider,
|
type: FlowInputItemTypeEnum.slider,
|
||||||
label: '回复上限',
|
label: '回复上限',
|
||||||
value: 3000,
|
value: defaultModel ? defaultModel.contextMaxToken / 2 : 2000,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 4000,
|
max: defaultModel?.contextMaxToken || 4000,
|
||||||
step: 50,
|
step: 50,
|
||||||
markList: [
|
markList: [
|
||||||
{ label: '0', value: 0 },
|
{ label: '0', value: 0 },
|
||||||
{ label: '4000', value: 4000 }
|
{
|
||||||
|
label: `${defaultModel?.contextMaxToken || 4000}`,
|
||||||
|
value: defaultModel?.contextMaxToken || 4000
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,6 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Bill } from '@/service/mongo';
|
import { connectToDatabase, Bill } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import type { ChatHistoryItemType } from '@/types/chat';
|
|
||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
|
|
||||||
/* get one app chat history content number. */
|
/* get one app chat history content number. */
|
||||||
@@ -28,14 +27,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
month: { $month: '$time' },
|
month: { $month: '$time' },
|
||||||
day: { $dayOfMonth: '$time' }
|
day: { $dayOfMonth: '$time' }
|
||||||
},
|
},
|
||||||
tokenLen: { $sum: '$tokenLen' } // 对tokenLen的值求和
|
total: { $sum: '$total' }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$project: {
|
$project: {
|
||||||
_id: 0,
|
_id: 0,
|
||||||
date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } },
|
date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } },
|
||||||
tokenLen: 1
|
total: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ $sort: { date: 1 } }
|
{ $sort: { date: 1 } }
|
@@ -15,7 +15,7 @@ import { Types } from 'mongoose';
|
|||||||
import { moduleFetch } from '@/service/api/request';
|
import { moduleFetch } from '@/service/api/request';
|
||||||
import { AppModuleItemType, RunningModuleItemType } from '@/types/app';
|
import { AppModuleItemType, RunningModuleItemType } from '@/types/app';
|
||||||
import { FlowInputItemTypeEnum } from '@/constants/flow';
|
import { FlowInputItemTypeEnum } from '@/constants/flow';
|
||||||
import { finishTaskBill, createTaskBill } from '@/service/events/pushBill';
|
import { finishTaskBill, createTaskBill, delTaskBill } from '@/service/events/pushBill';
|
||||||
import { BillSourceEnum } from '@/constants/user';
|
import { BillSourceEnum } from '@/constants/user';
|
||||||
|
|
||||||
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string };
|
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string };
|
||||||
@@ -56,6 +56,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
variables = {}
|
variables = {}
|
||||||
} = req.body as Props;
|
} = req.body as Props;
|
||||||
|
|
||||||
|
let billId = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!messages) {
|
if (!messages) {
|
||||||
throw new Error('Prams Error');
|
throw new Error('Prams Error');
|
||||||
@@ -108,11 +110,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
res.setHeader('newHistoryId', String(newHistoryId));
|
res.setHeader('newHistoryId', String(newHistoryId));
|
||||||
}
|
}
|
||||||
|
|
||||||
const billId = await createTaskBill({
|
billId = await createTaskBill({
|
||||||
userId,
|
userId,
|
||||||
appName: app.name,
|
appName: app.name,
|
||||||
appId,
|
appId,
|
||||||
source: BillSourceEnum.fastgpt
|
source: authType === 'apikey' ? BillSourceEnum.api : BillSourceEnum.fastgpt
|
||||||
});
|
});
|
||||||
|
|
||||||
/* start process */
|
/* start process */
|
||||||
@@ -125,7 +127,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
userChatInput: prompt.value
|
userChatInput: prompt.value
|
||||||
},
|
},
|
||||||
stream,
|
stream,
|
||||||
billId: ''
|
billId
|
||||||
});
|
});
|
||||||
|
|
||||||
// save chat
|
// save chat
|
||||||
@@ -184,6 +186,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
billId
|
billId
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
delTaskBill(billId);
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
res.status(500);
|
res.status(500);
|
||||||
sseErrRes(res, err);
|
sseErrRes(res, err);
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import React, { useEffect, useMemo, useRef } from 'react';
|
import React, { useEffect, useMemo, useRef } from 'react';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
import { getTokenUsage } from '@/api/app';
|
import { getAppTotalUsage } from '@/api/app';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { formatPrice } from '@/utils/user';
|
||||||
|
|
||||||
const map = {
|
const map = {
|
||||||
blue: {
|
blue: {
|
||||||
@@ -98,7 +99,7 @@ const TokenUsage = ({ appId }: { appId: string }) => {
|
|||||||
|
|
||||||
const Dom = useRef<HTMLDivElement>(null);
|
const Dom = useRef<HTMLDivElement>(null);
|
||||||
const myChart = useRef<echarts.ECharts>();
|
const myChart = useRef<echarts.ECharts>();
|
||||||
const { data = [] } = useQuery(['init'], () => getTokenUsage({ appId }));
|
const { data = [] } = useQuery(['init'], () => getAppTotalUsage({ appId }));
|
||||||
|
|
||||||
const option = useMemo(
|
const option = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -110,7 +111,7 @@ const TokenUsage = ({ appId }: { appId: string }) => {
|
|||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
max: Math.max(...data.map((item) => item.tokenLen)),
|
max: Math.max(...data.map((item) => item.total)),
|
||||||
min: 0
|
min: 0
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
@@ -132,14 +133,14 @@ const TokenUsage = ({ appId }: { appId: string }) => {
|
|||||||
return `
|
return `
|
||||||
<div>
|
<div>
|
||||||
<div>${dayjs(data.axisValue).format('YYYY/MM/DD')}</div>
|
<div>${dayjs(data.axisValue).format('YYYY/MM/DD')}</div>
|
||||||
<div>${((e[0]?.value || 0) / 1000).toFixed(2)}k Tokens</div>
|
<div>${formatPrice(e[0]?.value || 0)}元</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
data: data.map((item) => item.tokenLen),
|
data: data.map((item) => item.total),
|
||||||
type: 'line',
|
type: 'line',
|
||||||
showSymbol: true,
|
showSymbol: true,
|
||||||
animationDuration: 300,
|
animationDuration: 300,
|
||||||
@@ -170,7 +171,7 @@ const TokenUsage = ({ appId }: { appId: string }) => {
|
|||||||
if (!Dom.current || myChart?.current?.getOption()) return;
|
if (!Dom.current || myChart?.current?.getOption()) return;
|
||||||
myChart.current = echarts.init(Dom.current);
|
myChart.current = echarts.init(Dom.current);
|
||||||
myChart.current && myChart.current.setOption(option);
|
myChart.current && myChart.current.setOption(option);
|
||||||
}, [Dom]);
|
}, []);
|
||||||
|
|
||||||
// data changed, update
|
// data changed, update
|
||||||
useEffect(() => {
|
useEffect(() => {
|
@@ -14,8 +14,8 @@ import Avatar from '@/components/Avatar';
|
|||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
|
|
||||||
const InfoModal = dynamic(() => import('./InfoModal'));
|
const InfoModal = dynamic(() => import('./InfoModal'));
|
||||||
const TokenUsage = dynamic(() => import('./Charts/TokenUsage'));
|
const TotalUsage = dynamic(() => import('./Charts/TotalUsage'), { ssr: false });
|
||||||
const AppEdit = dynamic(() => import('./edit'));
|
const AppEdit = dynamic(() => import('./edit'), { ssr: false });
|
||||||
import styles from '../../list/index.module.scss';
|
import styles from '../../list/index.module.scss';
|
||||||
|
|
||||||
const Settings = ({ appId }: { appId: string }) => {
|
const Settings = ({ appId }: { appId: string }) => {
|
||||||
@@ -143,10 +143,10 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Box mb={2} fontSize={['md', 'xl']}>
|
<Box mb={2} fontSize={['md', 'xl']}>
|
||||||
近 7 日 Tokens 消耗
|
近 7 日消费
|
||||||
</Box>
|
</Box>
|
||||||
<Box h={'150px'}>
|
<Box h={'150px'} w={'100%'}>
|
||||||
<TokenUsage appId={appId} />
|
<TotalUsage appId={appId} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@@ -7,6 +7,8 @@ import Container from './modules/Container';
|
|||||||
import RenderInput from './render/RenderInput';
|
import RenderInput from './render/RenderInput';
|
||||||
import RenderOutput from './render/RenderOutput';
|
import RenderOutput from './render/RenderOutput';
|
||||||
import { FlowOutputItemTypeEnum } from '@/constants/flow';
|
import { FlowOutputItemTypeEnum } from '@/constants/flow';
|
||||||
|
import MySelect from '@/components/Select';
|
||||||
|
import { chatModelList } from '@/store/static';
|
||||||
|
|
||||||
const NodeChat = ({
|
const NodeChat = ({
|
||||||
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
||||||
@@ -15,11 +17,57 @@ const NodeChat = ({
|
|||||||
() => outputs.filter((item) => item.type !== FlowOutputItemTypeEnum.hidden).length,
|
() => outputs.filter((item) => item.type !== FlowOutputItemTypeEnum.hidden).length,
|
||||||
[outputs]
|
[outputs]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeCard minW={'400px'} logo={'/icon/logo.png'} name={'对话'} moduleId={moduleId} {...props}>
|
<NodeCard minW={'400px'} moduleId={moduleId} {...props}>
|
||||||
<Divider text="Input" />
|
<Divider text="Input" />
|
||||||
<Container>
|
<Container>
|
||||||
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
|
<RenderInput
|
||||||
|
moduleId={moduleId}
|
||||||
|
onChangeNode={onChangeNode}
|
||||||
|
flowInputList={inputs}
|
||||||
|
CustomComponent={{
|
||||||
|
model: (inputItem) => (
|
||||||
|
<MySelect
|
||||||
|
width={'100%'}
|
||||||
|
value={inputItem.value}
|
||||||
|
list={inputItem.list || []}
|
||||||
|
onchange={(e) => {
|
||||||
|
onChangeNode({
|
||||||
|
moduleId,
|
||||||
|
key: inputItem.key,
|
||||||
|
value: e
|
||||||
|
});
|
||||||
|
// update max tokens
|
||||||
|
const model = chatModelList.find((item) => item.model === e);
|
||||||
|
if (!model) return;
|
||||||
|
|
||||||
|
onChangeNode({
|
||||||
|
moduleId,
|
||||||
|
key: 'maxToken',
|
||||||
|
valueKey: 'markList',
|
||||||
|
value: [
|
||||||
|
{ label: '0', value: 0 },
|
||||||
|
{ label: `${model.contextMaxToken}`, value: model.contextMaxToken }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
onChangeNode({
|
||||||
|
moduleId,
|
||||||
|
key: 'maxToken',
|
||||||
|
valueKey: 'max',
|
||||||
|
value: model.contextMaxToken
|
||||||
|
});
|
||||||
|
onChangeNode({
|
||||||
|
moduleId,
|
||||||
|
key: 'maxToken',
|
||||||
|
valueKey: 'value',
|
||||||
|
value: model.contextMaxToken / 2
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
{outputsLen > 0 && (
|
{outputsLen > 0 && (
|
||||||
<>
|
<>
|
||||||
|
Reference in New Issue
Block a user