V4.8.17 feature (#3493)

* split tokens into input and output (#3477)

* split tokens into input and output

* query extension & tool call & question guide

* fix

* perf: input and output tokens

* perf: tool call if else

* perf: remove code

* fix: extract usage count

* fix: qa usage count

---------

Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
Archer 2024-12-30 10:13:25 +08:00 committed by GitHub
parent da2831b948
commit 50bf7f9a3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 467 additions and 230 deletions

View File

@ -28,6 +28,11 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4817' \
会将用户绑定的 OpenAI 账号移动到团队中。 会将用户绑定的 OpenAI 账号移动到团队中。
## 调整 completions 接口返回值
/api/v1/chat/completions 接口返回值调整,对话节点、工具节点等使用到模型的节点,将不再返回 `tokens` 字段,改为返回 `inputTokens``outputTokens` 字段,分别表示输入和输出的 Token 数量。
## 完整更新内容 ## 完整更新内容
1. 新增 - 简易模式工具调用支持数组类型插件。 1. 新增 - 简易模式工具调用支持数组类型插件。
@ -36,10 +41,12 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4817' \
4. 新增 - 商业版支持后台配置模板市场。 4. 新增 - 商业版支持后台配置模板市场。
5. 新增 - 商业版支持后台配置自定义工作流变量,用于与业务系统鉴权打通。 5. 新增 - 商业版支持后台配置自定义工作流变量,用于与业务系统鉴权打通。
6. 新增 - 搜索测试接口支持问题优化。 6. 新增 - 搜索测试接口支持问题优化。
7. 优化 - Markdown 大小测试,超出 20 万字符不使用 Markdown 组件,避免崩溃。 7. 新增 - 工作流中 Input Token 和 Output Token 分开记录展示。并修复部分请求未记录输出 Token 计费问题。
8. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。 8. 优化 - Markdown 大小测试,超出 20 万字符不使用 Markdown 组件,避免崩溃。
9. 优化 - 可用模型展示 9. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。
10. 优化 - Mongo 查询语句,增加 virtual 字段。 10. 优化 - 可用模型展示UI。
11. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。 11. 优化 - Mongo 查询语句,增加 virtual 字段。
12. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。 12. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。
13. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。 13. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。
14. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。
15. 修复 - 豆包模型无法工具调用。

View File

@ -1,6 +1,13 @@
import type { ModelProviderIdType } from './provider'; import type { ModelProviderIdType } from './provider';
export type LLMModelItemType = { type PriceType = {
charsPointsPrice?: number; // 1k chars=n points; 60s=n points;
// If inputPrice is set, the input-output charging scheme is adopted
inputPrice?: number; // 1k tokens=n points
outputPrice?: number; // 1k tokens=n points
};
export type LLMModelItemType = PriceType & {
provider: ModelProviderIdType; provider: ModelProviderIdType;
model: string; model: string;
name: string; name: string;
@ -10,8 +17,6 @@ export type LLMModelItemType = {
quoteMaxToken: number; quoteMaxToken: number;
maxTemperature: number; maxTemperature: number;
charsPointsPrice: number; // 1k chars=n points
censor?: boolean; censor?: boolean;
vision?: boolean; vision?: boolean;
@ -33,13 +38,12 @@ export type LLMModelItemType = {
fieldMap?: Record<string, string>; fieldMap?: Record<string, string>;
}; };
export type VectorModelItemType = { export type VectorModelItemType = PriceType & {
provider: ModelProviderIdType; provider: ModelProviderIdType;
model: string; // model name model: string; // model name
name: string; // show name name: string; // show name
avatar?: string; avatar?: string;
defaultToken: number; // split text default token defaultToken: number; // split text default token
charsPointsPrice: number; // 1k tokens=n points
maxToken: number; // model max token maxToken: number; // model max token
weight: number; // training weight weight: number; // training weight
hidden?: boolean; // Disallow creation hidden?: boolean; // Disallow creation
@ -48,25 +52,22 @@ export type VectorModelItemType = {
queryConfig?: Record<string, any>; // Custom parameters for query queryConfig?: Record<string, any>; // Custom parameters for query
}; };
export type ReRankModelItemType = { export type ReRankModelItemType = PriceType & {
model: string; model: string;
name: string; name: string;
charsPointsPrice: number;
requestUrl: string; requestUrl: string;
requestAuth: string; requestAuth: string;
}; };
export type AudioSpeechModelType = { export type AudioSpeechModelType = PriceType & {
provider: ModelProviderIdType; provider: ModelProviderIdType;
model: string; model: string;
name: string; name: string;
charsPointsPrice: number;
voices: { label: string; value: string; bufferId: string }[]; voices: { label: string; value: string; bufferId: string }[];
}; };
export type STTModelType = { export type STTModelType = PriceType & {
provider: ModelProviderIdType; provider: ModelProviderIdType;
model: string; model: string;
name: string; name: string;
charsPointsPrice: number; // 60s = n points
}; };

View File

@ -107,7 +107,9 @@ export type DispatchNodeResponseType = {
mergeSignId?: string; mergeSignId?: string;
// bill // bill
tokens?: number; tokens?: number; // deprecated
inputTokens?: number;
outputTokens?: number;
model?: string; model?: string;
contextTotalLen?: number; contextTotalLen?: number;
totalPoints?: number; totalPoints?: number;
@ -157,6 +159,8 @@ export type DispatchNodeResponseType = {
// tool // tool
toolCallTokens?: number; toolCallTokens?: number;
toolCallInputTokens?: number;
toolCallOutputTokens?: number;
toolDetail?: ChatHistoryItemResType[]; toolDetail?: ChatHistoryItemResType[];
toolStop?: boolean; toolStop?: boolean;

View File

@ -23,7 +23,8 @@ export type BillSchemaType = {
}; };
export type ChatNodeUsageType = { export type ChatNodeUsageType = {
tokens?: number; inputTokens?: number;
outputTokens?: number;
totalPoints: number; totalPoints: number;
moduleName: string; moduleName: string;
model?: string; model?: string;

View File

@ -2,9 +2,13 @@ import { CreateUsageProps } from './api';
import { UsageSourceEnum } from './constants'; import { UsageSourceEnum } from './constants';
export type UsageListItemCountType = { export type UsageListItemCountType = {
tokens?: number; inputTokens?: number;
outputTokens?: number;
charsLength?: number; charsLength?: number;
duration?: number; duration?: number;
// deprecated
tokens?: number;
}; };
export type UsageListItemType = UsageListItemCountType & { export type UsageListItemType = UsageListItemCountType & {
moduleName: string; moduleName: string;

View File

@ -2,7 +2,7 @@
"author": "", "author": "",
"version": "488", "version": "488",
"name": "飞书 webhook", "name": "飞书 webhook",
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg", "avatar": "core/app/templates/plugin-feishu",
"intro": "向飞书机器人发起 webhook 请求。", "intro": "向飞书机器人发起 webhook 请求。",
"courseUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5", "courseUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
"showStatus": false, "showStatus": false,

View File

@ -15,6 +15,9 @@ export const initFastGPTConfig = (config?: FastGPTConfigFileType) => {
global.subPlans = config.subPlans; global.subPlans = config.subPlans;
global.llmModels = config.llmModels; global.llmModels = config.llmModels;
global.llmModelPriceType = global.llmModels.some((item) => typeof item.inputPrice === 'number')
? 'IO'
: 'Tokens';
global.vectorModels = config.vectorModels; global.vectorModels = config.vectorModels;
global.audioSpeechModels = config.audioSpeechModels; global.audioSpeechModels = config.audioSpeechModels;
global.whisperModel = config.whisperModel; global.whisperModel = config.whisperModel;

View File

@ -1,6 +1,6 @@
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d'; import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
import { createChatCompletion } from '../config'; import { createChatCompletion } from '../config';
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index'; import { countGptMessagesTokens, countPromptTokens } from '../../../common/string/tiktoken/index';
import { loadRequestMessages } from '../../chat/utils'; import { loadRequestMessages } from '../../chat/utils';
import { llmCompletionsBodyFormat } from '../utils'; import { llmCompletionsBodyFormat } from '../utils';
import { import {
@ -20,7 +20,8 @@ export async function createQuestionGuide({
customPrompt?: string; customPrompt?: string;
}): Promise<{ }): Promise<{
result: string[]; result: string[];
tokens: number; inputTokens: number;
outputTokens: number;
}> { }> {
const concatMessages: ChatCompletionMessageParam[] = [ const concatMessages: ChatCompletionMessageParam[] = [
...messages, ...messages,
@ -29,6 +30,10 @@ export async function createQuestionGuide({
content: `${customPrompt || PROMPT_QUESTION_GUIDE}\n${PROMPT_QUESTION_GUIDE_FOOTER}` content: `${customPrompt || PROMPT_QUESTION_GUIDE}\n${PROMPT_QUESTION_GUIDE_FOOTER}`
} }
]; ];
const requestMessages = await loadRequestMessages({
messages: concatMessages,
useVision: false
});
const { response: data } = await createChatCompletion({ const { response: data } = await createChatCompletion({
body: llmCompletionsBodyFormat( body: llmCompletionsBodyFormat(
@ -36,10 +41,7 @@ export async function createQuestionGuide({
model, model,
temperature: 0.1, temperature: 0.1,
max_tokens: 200, max_tokens: 200,
messages: await loadRequestMessages({ messages: requestMessages,
messages: concatMessages,
useVision: false
}),
stream: false stream: false
}, },
model model
@ -51,13 +53,15 @@ export async function createQuestionGuide({
const start = answer.indexOf('['); const start = answer.indexOf('[');
const end = answer.lastIndexOf(']'); const end = answer.lastIndexOf(']');
const tokens = await countGptMessagesTokens(concatMessages); const inputTokens = await countGptMessagesTokens(requestMessages);
const outputTokens = await countPromptTokens(answer);
if (start === -1 || end === -1) { if (start === -1 || end === -1) {
addLog.warn('Create question guide error', { answer }); addLog.warn('Create question guide error', { answer });
return { return {
result: [], result: [],
tokens: 0 inputTokens: 0,
outputTokens: 0
}; };
} }
@ -69,14 +73,16 @@ export async function createQuestionGuide({
try { try {
return { return {
result: json5.parse(jsonStr), result: json5.parse(jsonStr),
tokens inputTokens,
outputTokens
}; };
} catch (error) { } catch (error) {
console.log(error); console.log(error);
return { return {
result: [], result: [],
tokens: 0 inputTokens: 0,
outputTokens: 0
}; };
} }
} }

View File

@ -1,7 +1,7 @@
import { replaceVariable } from '@fastgpt/global/common/string/tools'; import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { createChatCompletion } from '../config'; import { createChatCompletion } from '../config';
import { ChatItemType } from '@fastgpt/global/core/chat/type'; import { ChatItemType } from '@fastgpt/global/core/chat/type';
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index'; import { countGptMessagesTokens, countPromptTokens } from '../../../common/string/tiktoken/index';
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt'; import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
import { getLLMModel } from '../model'; import { getLLMModel } from '../model';
import { llmCompletionsBodyFormat } from '../utils'; import { llmCompletionsBodyFormat } from '../utils';
@ -121,7 +121,8 @@ export const queryExtension = async ({
rawQuery: string; rawQuery: string;
extensionQueries: string[]; extensionQueries: string[];
model: string; model: string;
tokens: number; inputTokens: number;
outputTokens: number;
}> => { }> => {
const systemFewShot = chatBg const systemFewShot = chatBg
? `Q: 对话背景。 ? `Q: 对话背景。
@ -166,7 +167,8 @@ A: ${chatBg}
rawQuery: query, rawQuery: query,
extensionQueries: [], extensionQueries: [],
model, model,
tokens: 0 inputTokens: 0,
outputTokens: 0
}; };
} }
@ -181,7 +183,8 @@ A: ${chatBg}
rawQuery: query, rawQuery: query,
extensionQueries: Array.isArray(queries) ? queries : [], extensionQueries: Array.isArray(queries) ? queries : [],
model, model,
tokens: await countGptMessagesTokens(messages) inputTokens: await countGptMessagesTokens(messages),
outputTokens: await countPromptTokens(answer)
}; };
} catch (error) { } catch (error) {
addLog.error(`Query extension error`, error); addLog.error(`Query extension error`, error);
@ -189,7 +192,8 @@ A: ${chatBg}
rawQuery: query, rawQuery: query,
extensionQueries: [], extensionQueries: [],
model, model,
tokens: 0 inputTokens: 0,
outputTokens: 0
}; };
} }
}; };

View File

@ -4,6 +4,7 @@ export const getLLMModel = (model?: string) => {
global.llmModels[0] global.llmModels[0]
); );
}; };
export const getDatasetModel = (model?: string) => { export const getDatasetModel = (model?: string) => {
return ( return (
global.llmModels global.llmModels

View File

@ -1,5 +1,8 @@
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt'; import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
import { countMessagesTokens } from '../../../../common/string/tiktoken/index'; import {
countGptMessagesTokens,
countPromptTokens
} from '../../../../common/string/tiktoken/index';
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { createChatCompletion } from '../../../ai/config'; import { createChatCompletion } from '../../../ai/config';
@ -49,7 +52,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
const { arg, tokens } = await completions({ const { arg, inputTokens, outputTokens } = await completions({
...props, ...props,
histories: chatHistories, histories: chatHistories,
cqModel cqModel
@ -59,7 +62,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model: cqModel.model, model: cqModel.model,
tokens, inputTokens: inputTokens,
outputTokens: outputTokens,
modelType: ModelTypeEnum.llm modelType: ModelTypeEnum.llm
}); });
@ -72,7 +76,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName, model: modelName,
query: userChatInput, query: userChatInput,
tokens, inputTokens: inputTokens,
outputTokens: outputTokens,
cqList: agents, cqList: agents,
cqResult: result.value, cqResult: result.value,
contextTotalLen: chatHistories.length + 2 contextTotalLen: chatHistories.length + 2
@ -82,7 +87,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
moduleName: name, moduleName: name,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName, model: modelName,
tokens inputTokens: inputTokens,
outputTokens: outputTokens
} }
] ]
}; };
@ -148,7 +154,8 @@ const completions = async ({
} }
return { return {
tokens: await countMessagesTokens(messages), inputTokens: await countGptMessagesTokens(requestMessages),
outputTokens: await countPromptTokens(answer),
arg: { type: id } arg: { type: id }
}; };
}; };

View File

@ -3,7 +3,8 @@ import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../chat/
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { import {
countMessagesTokens, countMessagesTokens,
countGptMessagesTokens countGptMessagesTokens,
countPromptTokens
} from '../../../../common/string/tiktoken/index'; } from '../../../../common/string/tiktoken/index';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { createChatCompletion } from '../../../ai/config'; import { createChatCompletion } from '../../../ai/config';
@ -59,7 +60,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
const extractModel = getLLMModel(model); const extractModel = getLLMModel(model);
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
const { arg, tokens } = await (async () => { const { arg, inputTokens, outputTokens } = await (async () => {
if (extractModel.toolChoice) { if (extractModel.toolChoice) {
return toolChoice({ return toolChoice({
...props, ...props,
@ -114,7 +115,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model: extractModel.model, model: extractModel.model,
tokens, inputTokens: inputTokens,
outputTokens: outputTokens,
modelType: ModelTypeEnum.llm modelType: ModelTypeEnum.llm
}); });
@ -126,7 +128,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName, model: modelName,
query: content, query: content,
tokens, inputTokens,
outputTokens,
extractDescription: description, extractDescription: description,
extractResult: arg, extractResult: arg,
contextTotalLen: chatHistories.length + 2 contextTotalLen: chatHistories.length + 2
@ -136,7 +139,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
moduleName: name, moduleName: name,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName, model: modelName,
tokens inputTokens,
outputTokens
} }
] ]
}; };
@ -249,15 +253,18 @@ const toolChoice = async (props: ActionProps) => {
} }
})(); })();
const completeMessages: ChatCompletionMessageParam[] = [ const AIMessages: ChatCompletionMessageParam[] = [
...filterMessages,
{ {
role: ChatCompletionRequestMessageRoleEnum.Assistant, role: ChatCompletionRequestMessageRoleEnum.Assistant,
tool_calls: response.choices?.[0]?.message?.tool_calls tool_calls: response.choices?.[0]?.message?.tool_calls
} }
]; ];
const inputTokens = await countGptMessagesTokens(filterMessages, tools);
const outputTokens = await countGptMessagesTokens(AIMessages);
return { return {
tokens: await countGptMessagesTokens(completeMessages, tools), inputTokens,
outputTokens,
arg arg
}; };
}; };
@ -286,17 +293,21 @@ const functionCall = async (props: ActionProps) => {
try { try {
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || ''); const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
const completeMessages: ChatCompletionMessageParam[] = [
...filterMessages, const AIMessages: ChatCompletionMessageParam[] = [
{ {
role: ChatCompletionRequestMessageRoleEnum.Assistant, role: ChatCompletionRequestMessageRoleEnum.Assistant,
function_call: response.choices?.[0]?.message?.function_call function_call: response.choices?.[0]?.message?.function_call
} }
]; ];
const inputTokens = await countGptMessagesTokens(filterMessages, undefined, functions);
const outputTokens = await countGptMessagesTokens(AIMessages);
return { return {
arg, arg,
tokens: await countGptMessagesTokens(completeMessages, undefined, functions) inputTokens,
outputTokens
}; };
} catch (error) { } catch (error) {
console.log(response.choices?.[0]?.message); console.log(response.choices?.[0]?.message);
@ -305,7 +316,8 @@ const functionCall = async (props: ActionProps) => {
return { return {
arg: {}, arg: {},
tokens: 0 inputTokens: 0,
outputTokens: 0
}; };
} }
}; };
@ -370,7 +382,8 @@ Human: ${content}`
if (!jsonStr) { if (!jsonStr) {
return { return {
rawResponse: answer, rawResponse: answer,
tokens: await countMessagesTokens(messages), inputTokens: await countMessagesTokens(messages),
outputTokens: await countPromptTokens(answer),
arg: {} arg: {}
}; };
} }
@ -378,7 +391,8 @@ Human: ${content}`
try { try {
return { return {
rawResponse: answer, rawResponse: answer,
tokens: await countMessagesTokens(messages), inputTokens: await countMessagesTokens(messages),
outputTokens: await countPromptTokens(answer),
arg: json5.parse(jsonStr) as Record<string, any> arg: json5.parse(jsonStr) as Record<string, any>
}; };
} catch (error) { } catch (error) {
@ -386,7 +400,8 @@ Human: ${content}`
console.log(error); console.log(error);
return { return {
rawResponse: answer, rawResponse: answer,
tokens: await countMessagesTokens(messages), inputTokens: await countMessagesTokens(messages),
outputTokens: await countPromptTokens(answer),
arg: {} arg: {}
}; };
} }

View File

@ -109,7 +109,8 @@ export const runToolWithFunctionCall = async (
return { return {
dispatchFlowResponse: [toolRunResponse], dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0, toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
completeMessages: requestMessages, completeMessages: requestMessages,
assistantResponses: toolRunResponse.assistantResponses, assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes, runTimes: toolRunResponse.runTimes,
@ -126,7 +127,8 @@ export const runToolWithFunctionCall = async (
}, },
{ {
dispatchFlowResponse: [toolRunResponse], dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0, toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
assistantResponses: toolRunResponse.assistantResponses, assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes runTimes: toolRunResponse.runTimes
} }
@ -340,7 +342,9 @@ export const runToolWithFunctionCall = async (
assistantToolMsgParams assistantToolMsgParams
] as ChatCompletionMessageParam[]; ] as ChatCompletionMessageParam[];
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply // Only toolCall tokens are counted here, Tool response tokens count towards the next reply
const tokens = await countGptMessagesTokens(concatToolMessages, undefined, functions); // const tokens = await countGptMessagesTokens(concatToolMessages, undefined, functions);
const inputTokens = await countGptMessagesTokens(requestMessages, undefined, functions);
const outputTokens = await countGptMessagesTokens([assistantToolMsgParams]);
/* /*
... ...
user user
@ -375,7 +379,12 @@ export const runToolWithFunctionCall = async (
const runTimes = const runTimes =
(response?.runTimes || 0) + (response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0); flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens; const toolNodeInputTokens = response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens;
const toolNodeOutputTokens = response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens;
// Check stop signal // Check stop signal
const hasStopSignal = flatToolsResponseData.some( const hasStopSignal = flatToolsResponseData.some(
@ -408,7 +417,8 @@ export const runToolWithFunctionCall = async (
return { return {
dispatchFlowResponse, dispatchFlowResponse,
toolNodeTokens, toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages, completeMessages,
assistantResponses: toolNodeAssistants, assistantResponses: toolNodeAssistants,
runTimes, runTimes,
@ -423,7 +433,8 @@ export const runToolWithFunctionCall = async (
}, },
{ {
dispatchFlowResponse, dispatchFlowResponse,
toolNodeTokens, toolNodeInputTokens,
toolNodeOutputTokens,
assistantResponses: toolNodeAssistants, assistantResponses: toolNodeAssistants,
runTimes runTimes
} }
@ -435,7 +446,8 @@ export const runToolWithFunctionCall = async (
content: answer content: answer
}; };
const completeMessages = filterMessages.concat(gptAssistantResponse); const completeMessages = filterMessages.concat(gptAssistantResponse);
const tokens = await countGptMessagesTokens(completeMessages, undefined, functions); const inputTokens = await countGptMessagesTokens(requestMessages, undefined, functions);
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
// console.log(tokens, 'response token'); // console.log(tokens, 'response token');
// concat tool assistant // concat tool assistant
@ -443,7 +455,12 @@ export const runToolWithFunctionCall = async (
return { return {
dispatchFlowResponse: response?.dispatchFlowResponse || [], dispatchFlowResponse: response?.dispatchFlowResponse || [],
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens, toolNodeInputTokens: response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens,
toolNodeOutputTokens: response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens,
completeMessages, completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value], assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1 runTimes: (response?.runTimes || 0) + 1

View File

@ -165,6 +165,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
toolWorkflowInteractiveResponse, toolWorkflowInteractiveResponse,
dispatchFlowResponse, // tool flow response dispatchFlowResponse, // tool flow response
toolNodeTokens, toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages = [], // The actual message sent to AI(just save text) completeMessages = [], // The actual message sent to AI(just save text)
assistantResponses = [], // FastGPT system store assistant.value response assistantResponses = [], // FastGPT system store assistant.value response
runTimes runTimes
@ -225,7 +227,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model, model,
tokens: toolNodeTokens, inputTokens: toolNodeInputTokens,
outputTokens: toolNodeOutputTokens,
modelType: ModelTypeEnum.llm modelType: ModelTypeEnum.llm
}); });
const toolAIUsage = externalProvider.openaiAccount?.key ? 0 : totalPoints; const toolAIUsage = externalProvider.openaiAccount?.key ? 0 : totalPoints;
@ -255,6 +258,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
// 展示的积分消耗 // 展示的积分消耗
totalPoints: totalPointsUsage, totalPoints: totalPointsUsage,
toolCallTokens: toolNodeTokens, toolCallTokens: toolNodeTokens,
toolCallInputTokens: toolNodeInputTokens,
toolCallOutputTokens: toolNodeOutputTokens,
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0), childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
model: modelName, model: modelName,
query: userChatInput, query: userChatInput,
@ -270,9 +275,10 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
// 工具调用本身的积分消耗 // 工具调用本身的积分消耗
{ {
moduleName: name, moduleName: name,
totalPoints: toolAIUsage,
model: modelName, model: modelName,
tokens: toolNodeTokens totalPoints: toolAIUsage,
inputTokens: toolNodeInputTokens,
outputTokens: toolNodeOutputTokens
}, },
// 工具的消耗 // 工具的消耗
...flatUsages ...flatUsages

View File

@ -115,7 +115,8 @@ export const runToolWithPromptCall = async (
return { return {
dispatchFlowResponse: [toolRunResponse], dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0, toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
completeMessages: concatMessages, completeMessages: concatMessages,
assistantResponses: toolRunResponse.assistantResponses, assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes, runTimes: toolRunResponse.runTimes,
@ -131,7 +132,8 @@ export const runToolWithPromptCall = async (
}, },
{ {
dispatchFlowResponse: [toolRunResponse], dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0, toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
assistantResponses: toolRunResponse.assistantResponses, assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes runTimes: toolRunResponse.runTimes
} }
@ -286,15 +288,20 @@ export const runToolWithPromptCall = async (
content: replaceAnswer content: replaceAnswer
}; };
const completeMessages = filterMessages.concat(gptAssistantResponse); const completeMessages = filterMessages.concat(gptAssistantResponse);
const tokens = await countGptMessagesTokens(completeMessages, undefined); const inputTokens = await countGptMessagesTokens(requestMessages);
// console.log(tokens, 'response token'); const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
// concat tool assistant // concat tool assistant
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType; const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
return { return {
dispatchFlowResponse: response?.dispatchFlowResponse || [], dispatchFlowResponse: response?.dispatchFlowResponse || [],
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens, toolNodeInputTokens: response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens,
toolNodeOutputTokens: response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens,
completeMessages, completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value], assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1 runTimes: (response?.runTimes || 0) + 1
@ -366,17 +373,9 @@ export const runToolWithPromptCall = async (
function_call: toolJson function_call: toolJson
}; };
/*
...
user
assistant: tool data
*/
const concatToolMessages = [
...requestMessages,
assistantToolMsgParams
] as ChatCompletionMessageParam[];
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply // Only toolCall tokens are counted here, Tool response tokens count towards the next reply
const tokens = await countGptMessagesTokens(concatToolMessages, undefined); const inputTokens = await countGptMessagesTokens(requestMessages);
const outputTokens = await countGptMessagesTokens([assistantToolMsgParams]);
/* /*
... ...
@ -437,7 +436,12 @@ ANSWER: `;
} }
const runTimes = (response?.runTimes || 0) + toolsRunResponse.toolResponse.runTimes; const runTimes = (response?.runTimes || 0) + toolsRunResponse.toolResponse.runTimes;
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens; const toolNodeInputTokens = response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens;
const toolNodeOutputTokens = response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens;
// Check stop signal // Check stop signal
const hasStopSignal = toolsRunResponse.toolResponse.flowResponses.some((item) => !!item.toolStop); const hasStopSignal = toolsRunResponse.toolResponse.flowResponses.some((item) => !!item.toolStop);
@ -460,7 +464,8 @@ ANSWER: `;
return { return {
dispatchFlowResponse, dispatchFlowResponse,
toolNodeTokens, toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages: filterMessages, completeMessages: filterMessages,
assistantResponses: toolNodeAssistants, assistantResponses: toolNodeAssistants,
runTimes, runTimes,
@ -475,7 +480,8 @@ ANSWER: `;
}, },
{ {
dispatchFlowResponse, dispatchFlowResponse,
toolNodeTokens, toolNodeInputTokens,
toolNodeOutputTokens,
assistantResponses: toolNodeAssistants, assistantResponses: toolNodeAssistants,
runTimes runTimes
} }

View File

@ -158,7 +158,8 @@ export const runToolWithToolChoice = async (
return { return {
dispatchFlowResponse: [toolRunResponse], dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0, toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
completeMessages: requestMessages, completeMessages: requestMessages,
assistantResponses: toolRunResponse.assistantResponses, assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes, runTimes: toolRunResponse.runTimes,
@ -176,7 +177,8 @@ export const runToolWithToolChoice = async (
}, },
{ {
dispatchFlowResponse: [toolRunResponse], dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0, toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
assistantResponses: toolRunResponse.assistantResponses, assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes runTimes: toolRunResponse.runTimes
} }
@ -428,7 +430,9 @@ export const runToolWithToolChoice = async (
] as ChatCompletionMessageParam[]; ] as ChatCompletionMessageParam[];
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply // Only toolCall tokens are counted here, Tool response tokens count towards the next reply
const tokens = await countGptMessagesTokens(concatToolMessages, tools); const inputTokens = await countGptMessagesTokens(requestMessages, tools);
const outputTokens = await countGptMessagesTokens(assistantToolMsgParams);
/* /*
... ...
user user
@ -463,7 +467,10 @@ export const runToolWithToolChoice = async (
const runTimes = const runTimes =
(response?.runTimes || 0) + (response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0); flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
const toolNodeTokens = response ? response.toolNodeTokens + tokens : tokens; const toolNodeInputTokens = response ? response.toolNodeInputTokens + inputTokens : inputTokens;
const toolNodeOutputTokens = response
? response.toolNodeOutputTokens + outputTokens
: outputTokens;
// Check stop signal // Check stop signal
const hasStopSignal = flatToolsResponseData.some( const hasStopSignal = flatToolsResponseData.some(
@ -496,7 +503,8 @@ export const runToolWithToolChoice = async (
return { return {
dispatchFlowResponse, dispatchFlowResponse,
toolNodeTokens, toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages, completeMessages,
assistantResponses: toolNodeAssistants, assistantResponses: toolNodeAssistants,
runTimes, runTimes,
@ -512,7 +520,8 @@ export const runToolWithToolChoice = async (
}, },
{ {
dispatchFlowResponse, dispatchFlowResponse,
toolNodeTokens, toolNodeInputTokens,
toolNodeOutputTokens,
assistantResponses: toolNodeAssistants, assistantResponses: toolNodeAssistants,
runTimes runTimes
} }
@ -524,14 +533,17 @@ export const runToolWithToolChoice = async (
content: answer content: answer
}; };
const completeMessages = filterMessages.concat(gptAssistantResponse); const completeMessages = filterMessages.concat(gptAssistantResponse);
const tokens = await countGptMessagesTokens(completeMessages, tools); const inputTokens = await countGptMessagesTokens(requestMessages, tools);
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
// concat tool assistant // concat tool assistant
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType; const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
return { return {
dispatchFlowResponse: response?.dispatchFlowResponse || [], dispatchFlowResponse: response?.dispatchFlowResponse || [],
toolNodeTokens: response ? response.toolNodeTokens + tokens : tokens, toolNodeInputTokens: response ? response.toolNodeInputTokens + inputTokens : inputTokens,
toolNodeOutputTokens: response ? response.toolNodeOutputTokens + outputTokens : outputTokens,
completeMessages, completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value], assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1 runTimes: (response?.runTimes || 0) + 1
@ -578,7 +590,8 @@ async function streamResponse({
text: content text: content
}) })
}); });
} else if (responseChoice?.tool_calls?.[0]) { }
if (responseChoice?.tool_calls?.[0]) {
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0]; const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
// In a stream response, only one tool is returned at a time. If have id, description is executing a tool // In a stream response, only one tool is returned at a time. If have id, description is executing a tool
if (toolCall.id || callingTool) { if (toolCall.id || callingTool) {

View File

@ -31,7 +31,9 @@ export type DispatchToolModuleProps = ModuleDispatchProps<{
export type RunToolResponse = { export type RunToolResponse = {
dispatchFlowResponse: DispatchFlowResponse[]; dispatchFlowResponse: DispatchFlowResponse[];
toolNodeTokens: number; toolNodeTokens?: number; // deprecated
toolNodeInputTokens: number;
toolNodeOutputTokens: number;
completeMessages?: ChatCompletionMessageParam[]; completeMessages?: ChatCompletionMessageParam[];
assistantResponses?: AIChatItemValueItemType[]; assistantResponses?: AIChatItemValueItemType[];
toolWorkflowInteractiveResponse?: WorkflowInteractiveResponseType; toolWorkflowInteractiveResponse?: WorkflowInteractiveResponseType;

View File

@ -5,13 +5,17 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils'; import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { createChatCompletion } from '../../../ai/config'; import { createChatCompletion } from '../../../ai/config';
import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d'; import type {
ChatCompletion,
ChatCompletionMessageParam,
StreamChatType
} from '@fastgpt/global/core/ai/type.d';
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils'; import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { postTextCensor } from '../../../../common/api/requestPlusApi'; import { postTextCensor } from '../../../../common/api/requestPlusApi';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants'; import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
import type { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type'; import type { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { countMessagesTokens } from '../../../../common/string/tiktoken/index'; import { countGptMessagesTokens } from '../../../../common/string/tiktoken/index';
import { import {
chats2GPTMessages, chats2GPTMessages,
chatValue2RuntimePrompt, chatValue2RuntimePrompt,
@ -214,16 +218,23 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
return Promise.reject(getEmptyResponseTip()); return Promise.reject(getEmptyResponseTip());
} }
const completeMessages = requestMessages.concat({ const AIMessages: ChatCompletionMessageParam[] = [
role: ChatCompletionRequestMessageRoleEnum.Assistant, {
content: answerText role: ChatCompletionRequestMessageRoleEnum.Assistant,
}); content: answerText
}
];
const completeMessages = [...requestMessages, ...AIMessages];
const chatCompleteMessages = GPTMessages2Chats(completeMessages); const chatCompleteMessages = GPTMessages2Chats(completeMessages);
const tokens = await countMessagesTokens(chatCompleteMessages); const inputTokens = await countGptMessagesTokens(requestMessages);
const outputTokens = await countGptMessagesTokens(AIMessages);
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model, model,
tokens, inputTokens,
outputTokens,
modelType: ModelTypeEnum.llm modelType: ModelTypeEnum.llm
}); });
@ -232,7 +243,9 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
[DispatchNodeResponseKeyEnum.nodeResponse]: { [DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName, model: modelName,
tokens, tokens: inputTokens + outputTokens,
inputTokens: inputTokens,
outputTokens: outputTokens,
query: `${userChatInput}`, query: `${userChatInput}`,
maxToken: max_tokens, maxToken: max_tokens,
historyPreview: getHistoryPreview( historyPreview: getHistoryPreview(
@ -247,7 +260,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
moduleName: name, moduleName: name,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName, model: modelName,
tokens inputTokens: inputTokens,
outputTokens: outputTokens
} }
], ],
[DispatchNodeResponseKeyEnum.toolResponses]: answerText, [DispatchNodeResponseKeyEnum.toolResponses]: answerText,

View File

@ -120,14 +120,14 @@ export async function dispatchDatasetSearch(
// vector // vector
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model: vectorModel.model, model: vectorModel.model,
tokens, inputTokens: tokens,
modelType: ModelTypeEnum.vector modelType: ModelTypeEnum.vector
}); });
const responseData: DispatchNodeResponseType & { totalPoints: number } = { const responseData: DispatchNodeResponseType & { totalPoints: number } = {
totalPoints, totalPoints,
query: concatQueries.join('\n'), query: concatQueries.join('\n'),
model: modelName, model: modelName,
tokens, inputTokens: tokens,
similarity: usingSimilarityFilter ? similarity : undefined, similarity: usingSimilarityFilter ? similarity : undefined,
limit, limit,
searchMode, searchMode,
@ -139,19 +139,21 @@ export async function dispatchDatasetSearch(
totalPoints, totalPoints,
moduleName: node.name, moduleName: node.name,
model: modelName, model: modelName,
tokens inputTokens: tokens
} }
]; ];
if (aiExtensionResult) { if (aiExtensionResult) {
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model: aiExtensionResult.model, model: aiExtensionResult.model,
tokens: aiExtensionResult.tokens, inputTokens: aiExtensionResult.inputTokens,
outputTokens: aiExtensionResult.outputTokens,
modelType: ModelTypeEnum.llm modelType: ModelTypeEnum.llm
}); });
responseData.totalPoints += totalPoints; responseData.totalPoints += totalPoints;
responseData.tokens = aiExtensionResult.tokens; responseData.inputTokens = aiExtensionResult.inputTokens;
responseData.outputTokens = aiExtensionResult.outputTokens;
responseData.extensionModel = modelName; responseData.extensionModel = modelName;
responseData.extensionResult = responseData.extensionResult =
aiExtensionResult.extensionQueries?.join('\n') || aiExtensionResult.extensionQueries?.join('\n') ||
@ -161,7 +163,8 @@ export async function dispatchDatasetSearch(
totalPoints, totalPoints,
moduleName: 'core.module.template.Query extension', moduleName: 'core.module.template.Query extension',
model: modelName, model: modelName,
tokens: aiExtensionResult.tokens inputTokens: aiExtensionResult.inputTokens,
outputTokens: aiExtensionResult.outputTokens
}); });
} }

View File

@ -130,8 +130,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{ {
moduleName: plugin.name, moduleName: plugin.name,
totalPoints: usagePoints, totalPoints: usagePoints
tokens: 0
} }
], ],
[DispatchNodeResponseKeyEnum.toolResponses]: output?.pluginOutput [DispatchNodeResponseKeyEnum.toolResponses]: output?.pluginOutput

View File

@ -153,8 +153,7 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{ {
moduleName: appData.name, moduleName: appData.name,
totalPoints: usagePoints, totalPoints: usagePoints
tokens: 0
} }
], ],
[DispatchNodeResponseKeyEnum.toolResponses]: text, [DispatchNodeResponseKeyEnum.toolResponses]: text,

View File

@ -31,7 +31,7 @@ export const dispatchQueryExtension = async ({
const queryExtensionModel = getLLMModel(model); const queryExtensionModel = getLLMModel(model);
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
const { extensionQueries, tokens } = await queryExtension({ const { extensionQueries, inputTokens, outputTokens } = await queryExtension({
chatBg: systemPrompt, chatBg: systemPrompt,
query: userChatInput, query: userChatInput,
histories: chatHistories, histories: chatHistories,
@ -42,7 +42,8 @@ export const dispatchQueryExtension = async ({
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model: queryExtensionModel.model, model: queryExtensionModel.model,
tokens, inputTokens,
outputTokens,
modelType: ModelTypeEnum.llm modelType: ModelTypeEnum.llm
}); });
@ -59,7 +60,8 @@ export const dispatchQueryExtension = async ({
[DispatchNodeResponseKeyEnum.nodeResponse]: { [DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints, totalPoints,
model: modelName, model: modelName,
tokens, inputTokens,
outputTokens,
query: userChatInput, query: userChatInput,
textOutput: JSON.stringify(filterSameQueries) textOutput: JSON.stringify(filterSameQueries)
}, },
@ -68,7 +70,8 @@ export const dispatchQueryExtension = async ({
moduleName: node.name, moduleName: node.name,
totalPoints, totalPoints,
model: modelName, model: modelName,
tokens inputTokens,
outputTokens
} }
], ],
[NodeOutputKeyEnum.text]: JSON.stringify(filterSameQueries) [NodeOutputKeyEnum.text]: JSON.stringify(filterSameQueries)

View File

@ -31,20 +31,23 @@ export const createTrainingUsage = async ({
{ {
moduleName: 'support.wallet.moduleName.index', moduleName: 'support.wallet.moduleName.index',
model: vectorModel, model: vectorModel,
tokens: 0, amount: 0,
amount: 0 inputTokens: 0,
outputTokens: 0
}, },
{ {
moduleName: 'support.wallet.moduleName.qa', moduleName: 'support.wallet.moduleName.qa',
model: agentModel, model: agentModel,
tokens: 0, amount: 0,
amount: 0 inputTokens: 0,
outputTokens: 0
}, },
{ {
moduleName: 'core.dataset.training.Auto mode', moduleName: 'core.dataset.training.Auto mode',
model: agentModel, model: agentModel,
tokens: 0, amount: 0,
amount: 0 inputTokens: 0,
outputTokens: 0
} }
] ]
} }

View File

@ -1,7 +1,7 @@
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema } = connectionMongo;
import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type'; import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type';
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
import { import {
TeamCollectionName, TeamCollectionName,
TeamMemberCollectionName TeamMemberCollectionName
@ -22,7 +22,7 @@ const UsageSchema = new Schema({
}, },
source: { source: {
type: String, type: String,
enum: Object.keys(UsageSourceMap), enum: Object.values(UsageSourceEnum),
required: true required: true
}, },
appName: { appName: {
@ -65,7 +65,7 @@ try {
// timer task. clear dead team // timer task. clear dead team
// UsageSchema.index({ teamId: 1, time: -1 }, { background: true }); // UsageSchema.index({ teamId: 1, time: -1 }, { background: true });
UsageSchema.index({ time: 1 }, { background: true, expireAfterSeconds: 720 * 24 * 60 * 60 }); UsageSchema.index({ time: 1 }, { background: true, expireAfterSeconds: 360 * 24 * 60 * 60 });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@ -1,17 +1,20 @@
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { ModelTypeEnum, getModelMap } from '../../../core/ai/model'; import { ModelTypeEnum, getModelMap } from '../../../core/ai/model';
export const formatModelChars2Points = ({ export const formatModelChars2Points = ({
model, model,
tokens = 0, inputTokens = 0,
outputTokens = 0,
modelType, modelType,
multiple = 1000 multiple = 1000
}: { }: {
model: string; model: string;
tokens: number; inputTokens?: number;
outputTokens?: number;
modelType: `${ModelTypeEnum}`; modelType: `${ModelTypeEnum}`;
multiple?: number; multiple?: number;
}) => { }) => {
const modelData = getModelMap?.[modelType]?.(model); const modelData = getModelMap?.[modelType]?.(model) as LLMModelItemType;
if (!modelData) { if (!modelData) {
return { return {
totalPoints: 0, totalPoints: 0,
@ -19,7 +22,12 @@ export const formatModelChars2Points = ({
}; };
} }
const totalPoints = (modelData.charsPointsPrice || 0) * (tokens / multiple); const isIOPriceType = typeof modelData.inputPrice === 'number';
const totalPoints = isIOPriceType
? (modelData.inputPrice || 0) * (inputTokens / multiple) +
(modelData.outputPrice || 0) * (outputTokens / multiple)
: (modelData.charsPointsPrice || 0) * ((inputTokens + outputTokens) / multiple);
return { return {
modelName: modelData.name, modelName: modelData.name,

View File

@ -18,6 +18,7 @@ declare global {
var subPlans: SubPlanType | undefined; var subPlans: SubPlanType | undefined;
var llmModels: LLMModelItemType[]; var llmModels: LLMModelItemType[];
var llmModelPriceType: 'IO' | 'Tokens';
var vectorModels: VectorModelItemType[]; var vectorModels: VectorModelItemType[];
var audioSpeechModels: AudioSpeechModelType[]; var audioSpeechModels: AudioSpeechModelType[];
var whisperModel: STTModelType; var whisperModel: STTModelType;

View File

@ -6,12 +6,14 @@
"details": "Details", "details": "Details",
"duration_seconds": "Duration (seconds)", "duration_seconds": "Duration (seconds)",
"generation_time": "Generation time", "generation_time": "Generation time",
"input_token_length": "input tokens",
"member": "member", "member": "member",
"member_name": "Member name", "member_name": "Member name",
"module_name": "module name", "module_name": "module name",
"month": "moon", "month": "moon",
"no_usage_records": "No usage record yet", "no_usage_records": "No usage record yet",
"order_number": "Order number", "order_number": "Order number",
"output_token_length": "output tokens",
"project_name": "Project name", "project_name": "Project name",
"source": "source", "source": "source",
"text_length": "text length", "text_length": "text length",
@ -20,4 +22,4 @@
"total_points_consumed": "AI points consumption", "total_points_consumed": "AI points consumption",
"usage_detail": "Usage details", "usage_detail": "Usage details",
"user_type": "type" "user_type": "type"
} }

View File

@ -447,6 +447,8 @@
"core.chat.response.Extension model": "Question Optimization Model", "core.chat.response.Extension model": "Question Optimization Model",
"core.chat.response.Read complete response": "View Details", "core.chat.response.Read complete response": "View Details",
"core.chat.response.Read complete response tips": "Click to View Detailed Process", "core.chat.response.Read complete response tips": "Click to View Detailed Process",
"core.chat.response.Tool call input tokens": "Tool Call Input Tokens Consumption",
"core.chat.response.Tool call output tokens": "Tool Call Output Tokens Consumption",
"core.chat.response.Tool call tokens": "Tool Call Tokens Consumption", "core.chat.response.Tool call tokens": "Tool Call Tokens Consumption",
"core.chat.response.context total length": "Total Context Length", "core.chat.response.context total length": "Total Context Length",
"core.chat.response.loop_input": "Loop Input Array", "core.chat.response.loop_input": "Loop Input Array",
@ -460,10 +462,12 @@
"core.chat.response.module historyPreview": "History Preview (Only Partial Content Displayed)", "core.chat.response.module historyPreview": "History Preview (Only Partial Content Displayed)",
"core.chat.response.module http result": "Response Body", "core.chat.response.module http result": "Response Body",
"core.chat.response.module if else Result": "Condition Result", "core.chat.response.module if else Result": "Condition Result",
"core.chat.response.module input tokens": "input tokens",
"core.chat.response.module limit": "Single Search Limit", "core.chat.response.module limit": "Single Search Limit",
"core.chat.response.module maxToken": "Max Response Tokens", "core.chat.response.module maxToken": "Max Response Tokens",
"core.chat.response.module model": "Model", "core.chat.response.module model": "Model",
"core.chat.response.module name": "Model Name", "core.chat.response.module name": "Model Name",
"core.chat.response.module output tokens": "output tokens",
"core.chat.response.module query": "Question/Search Term", "core.chat.response.module query": "Question/Search Term",
"core.chat.response.module quoteList": "Quote Content", "core.chat.response.module quoteList": "Quote Content",
"core.chat.response.module similarity": "Similarity", "core.chat.response.module similarity": "Similarity",
@ -1043,6 +1047,8 @@
"support.user.team.Team Tags Async Success": "Sync Completed", "support.user.team.Team Tags Async Success": "Sync Completed",
"support.user.team.member": "Member", "support.user.team.member": "Member",
"support.wallet.Ai point every thousand tokens": "{{points}} Points/1K Tokens", "support.wallet.Ai point every thousand tokens": "{{points}} Points/1K Tokens",
"support.wallet.Ai point every thousand tokens_input": "Input{{points}} points/1K tokens",
"support.wallet.Ai point every thousand tokens_output": "Output{{points}} points/1K tokens",
"support.wallet.Amount": "Amount", "support.wallet.Amount": "Amount",
"support.wallet.Buy": "Buy", "support.wallet.Buy": "Buy",
"support.wallet.Not sufficient": "Insufficient AI Points, Please Upgrade Your Package or Purchase Additional AI Points to Continue Using.", "support.wallet.Not sufficient": "Insufficient AI Points, Please Upgrade Your Package or Purchase Additional AI Points to Continue Using.",

View File

@ -1,23 +1,25 @@
{ {
"usage_detail": "使用详情",
"order_number": "订单号",
"generation_time": "生成时间",
"month": "月",
"app_name": "应用名",
"source": "来源",
"total_points_consumed": "AI 积分消耗",
"billing_module": "扣费模块",
"module_name": "模块名",
"ai_model": "AI 模型", "ai_model": "AI 模型",
"token_length": "token 长度",
"text_length": "文本长度",
"duration_seconds": "时长(秒)",
"all": "所有", "all": "所有",
"app_name": "应用名",
"billing_module": "扣费模块",
"details": "详情",
"duration_seconds": "时长(秒)",
"generation_time": "生成时间",
"input_token_length": "输入 tokens",
"member": "成员", "member": "成员",
"member_name": "成员名", "member_name": "成员名",
"user_type": "类型", "module_name": "模块名",
"month": "月",
"no_usage_records": "暂无使用记录",
"order_number": "订单号",
"output_token_length": "输出 tokens",
"project_name": "项目名", "project_name": "项目名",
"source": "来源",
"text_length": "文本长度",
"token_length": "token 长度",
"total_points": "AI 积分消耗", "total_points": "AI 积分消耗",
"details": "详情", "total_points_consumed": "AI 积分消耗",
"no_usage_records": "暂无使用记录" "usage_detail": "使用详情",
} "user_type": "类型"
}

View File

@ -450,6 +450,8 @@
"core.chat.response.Extension model": "问题优化模型", "core.chat.response.Extension model": "问题优化模型",
"core.chat.response.Read complete response": "查看详情", "core.chat.response.Read complete response": "查看详情",
"core.chat.response.Read complete response tips": "点击查看详细流程", "core.chat.response.Read complete response tips": "点击查看详细流程",
"core.chat.response.Tool call input tokens": "工具调用输入 Tokens",
"core.chat.response.Tool call output tokens": "工具调用输出 Tokens",
"core.chat.response.Tool call tokens": "工具调用 tokens 消耗", "core.chat.response.Tool call tokens": "工具调用 tokens 消耗",
"core.chat.response.context total length": "上下文总长度", "core.chat.response.context total length": "上下文总长度",
"core.chat.response.loop_input": "输入数组", "core.chat.response.loop_input": "输入数组",
@ -463,10 +465,12 @@
"core.chat.response.module historyPreview": "记录预览(仅展示部分内容)", "core.chat.response.module historyPreview": "记录预览(仅展示部分内容)",
"core.chat.response.module http result": "响应体", "core.chat.response.module http result": "响应体",
"core.chat.response.module if else Result": "判断器结果", "core.chat.response.module if else Result": "判断器结果",
"core.chat.response.module input tokens": "输入 Tokens",
"core.chat.response.module limit": "单次搜索上限", "core.chat.response.module limit": "单次搜索上限",
"core.chat.response.module maxToken": "最大响应 tokens", "core.chat.response.module maxToken": "最大响应 tokens",
"core.chat.response.module model": "模型", "core.chat.response.module model": "模型",
"core.chat.response.module name": "模型名", "core.chat.response.module name": "模型名",
"core.chat.response.module output tokens": "输出 Tokens",
"core.chat.response.module query": "问题/检索词", "core.chat.response.module query": "问题/检索词",
"core.chat.response.module quoteList": "引用内容", "core.chat.response.module quoteList": "引用内容",
"core.chat.response.module similarity": "相似度", "core.chat.response.module similarity": "相似度",
@ -1046,6 +1050,8 @@
"support.user.team.Team Tags Async Success": "同步完成", "support.user.team.Team Tags Async Success": "同步完成",
"support.user.team.member": "成员", "support.user.team.member": "成员",
"support.wallet.Ai point every thousand tokens": "{{points}} 积分/1K tokens", "support.wallet.Ai point every thousand tokens": "{{points}} 积分/1K tokens",
"support.wallet.Ai point every thousand tokens_input": "输入:{{points}} 积分/1K tokens",
"support.wallet.Ai point every thousand tokens_output": "输出:{{points}} 积分/1K tokens",
"support.wallet.Amount": "金额", "support.wallet.Amount": "金额",
"support.wallet.Buy": "购买", "support.wallet.Buy": "购买",
"support.wallet.Not sufficient": "您的 AI 积分不足,请先升级套餐或购买额外 AI 积分后继续使用。", "support.wallet.Not sufficient": "您的 AI 积分不足,请先升级套餐或购买额外 AI 积分后继续使用。",

View File

@ -6,12 +6,14 @@
"details": "詳情", "details": "詳情",
"duration_seconds": "時長(秒)", "duration_seconds": "時長(秒)",
"generation_time": "生成時間", "generation_time": "生成時間",
"input_token_length": "輸入 tokens",
"member": "成員", "member": "成員",
"member_name": "成員名", "member_name": "成員名",
"module_name": "模組名", "module_name": "模組名",
"month": "月", "month": "月",
"no_usage_records": "暫無使用紀錄", "no_usage_records": "暫無使用紀錄",
"order_number": "訂單編號", "order_number": "訂單編號",
"output_token_length": "輸出 tokens",
"project_name": "專案名", "project_name": "專案名",
"source": "來源", "source": "來源",
"text_length": "文字長度", "text_length": "文字長度",
@ -20,4 +22,4 @@
"total_points_consumed": "AI 積分消耗", "total_points_consumed": "AI 積分消耗",
"usage_detail": "使用詳情", "usage_detail": "使用詳情",
"user_type": "類型" "user_type": "類型"
} }

View File

@ -447,6 +447,8 @@
"core.chat.response.Extension model": "問題最佳化模型", "core.chat.response.Extension model": "問題最佳化模型",
"core.chat.response.Read complete response": "檢視詳細資料", "core.chat.response.Read complete response": "檢視詳細資料",
"core.chat.response.Read complete response tips": "點選檢視詳細流程", "core.chat.response.Read complete response tips": "點選檢視詳細流程",
"core.chat.response.Tool call input tokens": "工具呼叫輸入 Token 消耗",
"core.chat.response.Tool call output tokens": "工具呼叫輸出 Token 消耗",
"core.chat.response.Tool call tokens": "工具呼叫 Token 消耗", "core.chat.response.Tool call tokens": "工具呼叫 Token 消耗",
"core.chat.response.context total length": "上下文總長度", "core.chat.response.context total length": "上下文總長度",
"core.chat.response.loop_input": "輸入陣列", "core.chat.response.loop_input": "輸入陣列",
@ -460,10 +462,12 @@
"core.chat.response.module historyPreview": "記錄預覽(僅顯示部分內容)", "core.chat.response.module historyPreview": "記錄預覽(僅顯示部分內容)",
"core.chat.response.module http result": "回應內容", "core.chat.response.module http result": "回應內容",
"core.chat.response.module if else Result": "條件判斷結果", "core.chat.response.module if else Result": "條件判斷結果",
"core.chat.response.module input tokens": "輸入 tokens",
"core.chat.response.module limit": "單次搜尋上限", "core.chat.response.module limit": "單次搜尋上限",
"core.chat.response.module maxToken": "最大回應 Token 數", "core.chat.response.module maxToken": "最大回應 Token 數",
"core.chat.response.module model": "模型", "core.chat.response.module model": "模型",
"core.chat.response.module name": "模型名稱", "core.chat.response.module name": "模型名稱",
"core.chat.response.module output tokens": "輸出 tokens",
"core.chat.response.module query": "問題/搜尋詞", "core.chat.response.module query": "問題/搜尋詞",
"core.chat.response.module quoteList": "引用內容", "core.chat.response.module quoteList": "引用內容",
"core.chat.response.module similarity": "相似度", "core.chat.response.module similarity": "相似度",
@ -1043,6 +1047,8 @@
"support.user.team.Team Tags Async Success": "同步完成", "support.user.team.Team Tags Async Success": "同步完成",
"support.user.team.member": "成員", "support.user.team.member": "成員",
"support.wallet.Ai point every thousand tokens": "{{points}} 點數/1K tokens", "support.wallet.Ai point every thousand tokens": "{{points}} 點數/1K tokens",
"support.wallet.Ai point every thousand tokens_input": "輸入:{{points}} 积分/1K tokens",
"support.wallet.Ai point every thousand tokens_output": "輸出:{{points}} 积分/1K tokens",
"support.wallet.Amount": "金額", "support.wallet.Amount": "金額",
"support.wallet.Buy": "購買", "support.wallet.Buy": "購買",
"support.wallet.Not sufficient": "您的 AI 點數不足,請先升級方案或購買額外 AI 點數後繼續使用。", "support.wallet.Not sufficient": "您的 AI 點數不足,請先升級方案或購買額外 AI 點數後繼續使用。",

View File

@ -18,7 +18,6 @@ import {
Thead, Thead,
Tr, Tr,
Table, Table,
useDisclosure,
FlexProps FlexProps
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
@ -175,10 +174,28 @@ const AIChatSettingsModal = ({
<Tbody> <Tbody>
<Tr color={'myGray.900'}> <Tr color={'myGray.900'}>
<Td pt={0} pb={2}> <Td pt={0} pb={2}>
{t('common:support.wallet.Ai point every thousand tokens', { {typeof selectedModel?.inputPrice === 'number' ? (
points: selectedModel?.charsPointsPrice || 0 <>
})} <Box>
{t('common:support.wallet.Ai point every thousand tokens_input', {
points: selectedModel?.inputPrice || 0
})}
</Box>
<Box>
{t('common:support.wallet.Ai point every thousand tokens_output', {
points: selectedModel?.outputPrice || 0
})}
</Box>
</>
) : (
<>
{t('common:support.wallet.Ai point every thousand tokens', {
points: selectedModel?.charsPointsPrice || 0
})}
</>
)}
</Td> </Td>
<Td pt={0} pb={2}> <Td pt={0} pb={2}>
{Math.round((selectedModel?.maxContext || 4096) / 1000)}K {Math.round((selectedModel?.maxContext || 4096) / 1000)}K
</Td> </Td>

View File

@ -60,14 +60,32 @@ const ModelTable = () => {
const formatLLMModelList = llmModelList.map((item) => ({ const formatLLMModelList = llmModelList.map((item) => ({
...item, ...item,
typeLabel: t('common:model.type.chat'), typeLabel: t('common:model.type.chat'),
priceLabel: ( priceLabel:
<Flex color={'myGray.700'}> typeof item.inputPrice === 'number' ? (
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}> <Box>
{item.charsPointsPrice} <Flex>
{`${t('common:common.Input')}:`}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5} ml={2}>
{item.inputPrice || 0}
</Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
<Flex>
{`${t('common:common.Output')}:`}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5} ml={2}>
{item.outputPrice || 0}
</Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
</Box> </Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`} ) : (
</Flex> <Flex color={'myGray.700'}>
), <Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
),
tagColor: 'blue' tagColor: 'blue'
})); }));
const formatVectorModelList = vectorModelList.map((item) => ({ const formatVectorModelList = vectorModelList.map((item) => ({
@ -149,13 +167,13 @@ const ModelTable = () => {
return filterList; return filterList;
}, [ }, [
provider,
modelType,
llmModelList, llmModelList,
vectorModelList, vectorModelList,
audioSpeechModelList, audioSpeechModelList,
whisperModel, whisperModel,
t, t,
modelType,
provider,
search search
]); ]);

View File

@ -155,10 +155,26 @@ export const WholeResponseContent = ({
label={t('common:core.chat.response.module tokens')} label={t('common:core.chat.response.module tokens')}
value={`${activeModule?.tokens}`} value={`${activeModule?.tokens}`}
/> />
<Row
label={t('common:core.chat.response.module input tokens')}
value={`${activeModule?.inputTokens}`}
/>
<Row
label={t('common:core.chat.response.module output tokens')}
value={`${activeModule?.outputTokens}`}
/>
<Row <Row
label={t('common:core.chat.response.Tool call tokens')} label={t('common:core.chat.response.Tool call tokens')}
value={`${activeModule?.toolCallTokens}`} value={`${activeModule?.toolCallTokens}`}
/> />
<Row
label={t('common:core.chat.response.Tool call input tokens')}
value={`${activeModule?.toolCallInputTokens}`}
/>
<Row
label={t('common:core.chat.response.Tool call output tokens')}
value={`${activeModule?.toolCallOutputTokens}`}
/>
<Row label={t('common:core.chat.response.module query')} value={activeModule?.query} /> <Row label={t('common:core.chat.response.module query')} value={activeModule?.query} />
<Row <Row

View File

@ -26,37 +26,48 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
[usage.list] [usage.list]
); );
const { hasModel, hasToken, hasCharsLen, hasDuration } = useMemo(() => { const { hasModel, hasToken, hasInputToken, hasOutputToken, hasCharsLen, hasDuration } =
let hasModel = false; useMemo(() => {
let hasToken = false; let hasModel = false;
let hasCharsLen = false; let hasToken = false;
let hasDuration = false; let hasInputToken = false;
let hasDataLen = false; let hasOutputToken = false;
let hasCharsLen = false;
let hasDuration = false;
let hasDataLen = false;
usage.list.forEach((item) => { usage.list.forEach((item) => {
if (item.model !== undefined) { if (item.model !== undefined) {
hasModel = true; hasModel = true;
} }
if (typeof item.tokens === 'number') { if (typeof item.tokens === 'number') {
hasToken = true; hasToken = true;
} }
if (typeof item.charsLength === 'number') { if (typeof item.inputTokens === 'number') {
hasCharsLen = true; hasInputToken = true;
} }
if (typeof item.duration === 'number') { if (typeof item.outputTokens === 'number') {
hasDuration = true; hasOutputToken = true;
} }
}); if (typeof item.charsLength === 'number') {
hasCharsLen = true;
}
if (typeof item.duration === 'number') {
hasDuration = true;
}
});
return { return {
hasModel, hasModel,
hasToken, hasToken,
hasCharsLen, hasInputToken,
hasDuration, hasOutputToken,
hasDataLen hasCharsLen,
}; hasDuration,
}, [usage.list]); hasDataLen
};
}, [usage.list]);
return ( return (
<MyModal <MyModal
@ -98,6 +109,8 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
<Th>{t('account_usage:module_name')}</Th> <Th>{t('account_usage:module_name')}</Th>
{hasModel && <Th>{t('account_usage:ai_model')}</Th>} {hasModel && <Th>{t('account_usage:ai_model')}</Th>}
{hasToken && <Th>{t('account_usage:token_length')}</Th>} {hasToken && <Th>{t('account_usage:token_length')}</Th>}
{hasInputToken && <Th>{t('account_usage:input_token_length')}</Th>}
{hasOutputToken && <Th>{t('account_usage:output_token_length')}</Th>}
{hasCharsLen && <Th>{t('account_usage:text_length')}</Th>} {hasCharsLen && <Th>{t('account_usage:text_length')}</Th>}
{hasDuration && <Th>{t('account_usage:duration_seconds')}</Th>} {hasDuration && <Th>{t('account_usage:duration_seconds')}</Th>}
<Th>{t('account_usage:total_points_consumed')}</Th> <Th>{t('account_usage:total_points_consumed')}</Th>
@ -109,6 +122,8 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
<Td>{t(item.moduleName as any)}</Td> <Td>{t(item.moduleName as any)}</Td>
{hasModel && <Td>{item.model ?? '-'}</Td>} {hasModel && <Td>{item.model ?? '-'}</Td>}
{hasToken && <Td>{item.tokens ?? '-'}</Td>} {hasToken && <Td>{item.tokens ?? '-'}</Td>}
{hasInputToken && <Td>{item.inputTokens ?? '-'}</Td>}
{hasOutputToken && <Td>{item.outputTokens ?? '-'}</Td>}
{hasCharsLen && <Td>{item.charsLength ?? '-'}</Td>} {hasCharsLen && <Td>{item.charsLength ?? '-'}</Td>}
{hasDuration && <Td>{item.duration ?? '-'}</Td>} {hasDuration && <Td>{item.duration ?? '-'}</Td>}
<Td>{formatNumber(item.amount)}</Td> <Td>{formatNumber(item.amount)}</Td>

View File

@ -37,7 +37,7 @@ async function handler(
const qgModel = global.llmModels[0]; const qgModel = global.llmModels[0];
const { result, tokens } = await createQuestionGuide({ const { result, inputTokens, outputTokens } = await createQuestionGuide({
messages, messages,
model: qgModel.model model: qgModel.model
}); });
@ -47,7 +47,8 @@ async function handler(
}); });
pushQuestionGuideUsage({ pushQuestionGuideUsage({
tokens, inputTokens,
outputTokens,
teamId, teamId,
tmbId tmbId
}); });

View File

@ -52,14 +52,15 @@ async function handler(req: ApiRequestProps<CreateQuestionGuideParams>, res: Nex
const qgModel = questionGuide?.model || global.llmModels[0].model; const qgModel = questionGuide?.model || global.llmModels[0].model;
const { result, tokens } = await createQuestionGuide({ const { result, inputTokens, outputTokens } = await createQuestionGuide({
messages, messages,
model: qgModel, model: qgModel,
customPrompt: questionGuide?.customPrompt customPrompt: questionGuide?.customPrompt
}); });
pushQuestionGuideUsage({ pushQuestionGuideUsage({
tokens, inputTokens,
outputTokens,
teamId, teamId,
tmbId tmbId
}); });

View File

@ -89,7 +89,7 @@ async function handler(req: NextApiRequest) {
pushGenerateVectorUsage({ pushGenerateVectorUsage({
teamId, teamId,
tmbId, tmbId,
tokens, inputTokens: tokens,
model: vectorModelData.model model: vectorModelData.model
}); });

View File

@ -36,7 +36,7 @@ async function handler(req: ApiRequestProps<UpdateDatasetDataProps>) {
pushGenerateVectorUsage({ pushGenerateVectorUsage({
teamId, teamId,
tmbId, tmbId,
tokens, inputTokens: tokens,
model: vectorModel model: vectorModel
}); });
} else { } else {

View File

@ -74,14 +74,15 @@ async function handler(req: NextApiRequest) {
const { totalPoints } = pushGenerateVectorUsage({ const { totalPoints } = pushGenerateVectorUsage({
teamId, teamId,
tmbId, tmbId,
tokens, inputTokens: tokens,
model: dataset.vectorModel, model: dataset.vectorModel,
source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt, source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt,
...(aiExtensionResult && ...(aiExtensionResult &&
extensionModel && { extensionModel && {
extensionModel: extensionModel.name, extensionModel: extensionModel.name,
extensionTokens: aiExtensionResult.tokens extensionInputTokens: aiExtensionResult.inputTokens,
extensionOutputTokens: aiExtensionResult.outputTokens
}) })
}); });
if (apikey) { if (apikey) {

View File

@ -57,7 +57,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { totalPoints } = pushGenerateVectorUsage({ const { totalPoints } = pushGenerateVectorUsage({
teamId, teamId,
tmbId, tmbId,
tokens, inputTokens: tokens,
model, model,
billId, billId,
source: getUsageSourceByAuthType({ authType }) source: getUsageSourceByAuthType({ authType })

View File

@ -196,7 +196,7 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
chunkSize: vectorModel?.defaultToken ? vectorModel?.defaultToken * 2 : 1024, chunkSize: vectorModel?.defaultToken ? vectorModel?.defaultToken * 2 : 1024,
showChunkInput: false, showChunkInput: false,
showPromptInput: false, showPromptInput: false,
charsPointsPrice: agentModel.charsPointsPrice, charsPointsPrice: agentModel.charsPointsPrice || 0,
priceTip: t('dataset:import.Auto mode Estimated Price Tips', { priceTip: t('dataset:import.Auto mode Estimated Price Tips', {
price: agentModel.charsPointsPrice price: agentModel.charsPointsPrice
}), }),
@ -211,7 +211,7 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
chunkSize: embeddingChunkSize, chunkSize: embeddingChunkSize,
showChunkInput: true, showChunkInput: true,
showPromptInput: false, showPromptInput: false,
charsPointsPrice: vectorModel.charsPointsPrice, charsPointsPrice: vectorModel.charsPointsPrice || 0,
priceTip: t('dataset:import.Embedding Estimated Price Tips', { priceTip: t('dataset:import.Embedding Estimated Price Tips', {
price: vectorModel.charsPointsPrice price: vectorModel.charsPointsPrice
}), }),
@ -226,7 +226,7 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
chunkSize: qaChunkSize, chunkSize: qaChunkSize,
showChunkInput: true, showChunkInput: true,
showPromptInput: true, showPromptInput: true,
charsPointsPrice: agentModel.charsPointsPrice, charsPointsPrice: agentModel.charsPointsPrice || 0,
priceTip: t('dataset:import.Auto mode Estimated Price Tips', { priceTip: t('dataset:import.Auto mode Estimated Price Tips', {
price: agentModel.charsPointsPrice price: agentModel.charsPointsPrice
}), }),

View File

@ -12,7 +12,10 @@ import { getLLMModel } from '@fastgpt/service/core/ai/model';
import { checkTeamAiPointsAndLock } from './utils'; import { checkTeamAiPointsAndLock } from './utils';
import { checkInvalidChunkAndLock } from '@fastgpt/service/core/dataset/training/utils'; import { checkInvalidChunkAndLock } from '@fastgpt/service/core/dataset/training/utils';
import { addMinutes } from 'date-fns'; import { addMinutes } from 'date-fns';
import { countGptMessagesTokens } from '@fastgpt/service/common/string/tiktoken/index'; import {
countGptMessagesTokens,
countPromptTokens
} from '@fastgpt/service/common/string/tiktoken/index';
import { pushDataListToTrainingQueueByCollectionId } from '@fastgpt/service/core/dataset/training/controller'; import { pushDataListToTrainingQueueByCollectionId } from '@fastgpt/service/core/dataset/training/controller';
import { loadRequestMessages } from '@fastgpt/service/core/chat/utils'; import { loadRequestMessages } from '@fastgpt/service/core/chat/utils';
import { llmCompletionsBodyFormat } from '@fastgpt/service/core/ai/utils'; import { llmCompletionsBodyFormat } from '@fastgpt/service/core/ai/utils';
@ -153,7 +156,8 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
pushQAUsage({ pushQAUsage({
teamId: data.teamId, teamId: data.teamId,
tmbId: data.tmbId, tmbId: data.tmbId,
tokens: await countGptMessagesTokens(messages), inputTokens: await countGptMessagesTokens(messages),
outputTokens: await countPromptTokens(answer),
billId: data.billId, billId: data.billId,
model: modelData.model model: modelData.model
}); });

View File

@ -111,7 +111,7 @@ export async function generateVector(): Promise<any> {
pushGenerateVectorUsage({ pushGenerateVectorUsage({
teamId: data.teamId, teamId: data.teamId,
tmbId: data.tmbId, tmbId: data.tmbId,
tokens, inputTokens: tokens,
model: data.model, model: data.model,
billId: data.billId billId: data.billId
}); });

View File

@ -37,7 +37,8 @@ export const pushChatUsage = ({
moduleName: item.moduleName, moduleName: item.moduleName,
amount: item.totalPoints || 0, amount: item.totalPoints || 0,
model: item.model, model: item.model,
tokens: item.tokens inputTokens: item.inputTokens,
outputTokens: item.outputTokens
})) }))
}); });
addLog.info(`finish completions`, { addLog.info(`finish completions`, {
@ -52,20 +53,23 @@ export const pushQAUsage = async ({
teamId, teamId,
tmbId, tmbId,
model, model,
tokens, inputTokens,
outputTokens,
billId billId
}: { }: {
teamId: string; teamId: string;
tmbId: string; tmbId: string;
model: string; model: string;
tokens: number; inputTokens: number;
outputTokens: number;
billId: string; billId: string;
}) => { }) => {
// 计算价格 // 计算价格
const { totalPoints } = formatModelChars2Points({ const { totalPoints } = formatModelChars2Points({
model, model,
modelType: ModelTypeEnum.llm, modelType: ModelTypeEnum.llm,
tokens inputTokens,
outputTokens
}); });
concatUsage({ concatUsage({
@ -73,7 +77,8 @@ export const pushQAUsage = async ({
teamId, teamId,
tmbId, tmbId,
totalPoints, totalPoints,
tokens, inputTokens,
outputTokens,
listIndex: 1 listIndex: 1
}); });
@ -84,30 +89,32 @@ export const pushGenerateVectorUsage = ({
billId, billId,
teamId, teamId,
tmbId, tmbId,
tokens, inputTokens,
model, model,
source = UsageSourceEnum.fastgpt, source = UsageSourceEnum.fastgpt,
extensionModel, extensionModel,
extensionTokens extensionInputTokens,
extensionOutputTokens
}: { }: {
billId?: string; billId?: string;
teamId: string; teamId: string;
tmbId: string; tmbId: string;
tokens: number; inputTokens: number;
model: string; model: string;
source?: UsageSourceEnum; source?: UsageSourceEnum;
extensionModel?: string; extensionModel?: string;
extensionTokens?: number; extensionInputTokens?: number;
extensionOutputTokens?: number;
}) => { }) => {
const { totalPoints: totalVector, modelName: vectorModelName } = formatModelChars2Points({ const { totalPoints: totalVector, modelName: vectorModelName } = formatModelChars2Points({
modelType: ModelTypeEnum.vector, modelType: ModelTypeEnum.vector,
model, model,
tokens inputTokens
}); });
const { extensionTotalPoints, extensionModelName } = (() => { const { extensionTotalPoints, extensionModelName } = (() => {
if (!extensionModel || !extensionTokens) if (!extensionModel || !extensionInputTokens)
return { return {
extensionTotalPoints: 0, extensionTotalPoints: 0,
extensionModelName: '' extensionModelName: ''
@ -115,7 +122,8 @@ export const pushGenerateVectorUsage = ({
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
modelType: ModelTypeEnum.llm, modelType: ModelTypeEnum.llm,
model: extensionModel, model: extensionModel,
tokens: extensionTokens inputTokens: extensionInputTokens,
outputTokens: extensionOutputTokens
}); });
return { return {
extensionTotalPoints: totalPoints, extensionTotalPoints: totalPoints,
@ -132,7 +140,7 @@ export const pushGenerateVectorUsage = ({
tmbId, tmbId,
totalPoints, totalPoints,
billId, billId,
tokens, inputTokens,
listIndex: 0 listIndex: 0
}); });
} else { } else {
@ -147,7 +155,7 @@ export const pushGenerateVectorUsage = ({
moduleName: 'support.wallet.moduleName.index', moduleName: 'support.wallet.moduleName.index',
amount: totalVector, amount: totalVector,
model: vectorModelName, model: vectorModelName,
tokens inputTokens
}, },
...(extensionModel !== undefined ...(extensionModel !== undefined
? [ ? [
@ -155,7 +163,8 @@ export const pushGenerateVectorUsage = ({
moduleName: 'core.module.template.Query extension', moduleName: 'core.module.template.Query extension',
amount: extensionTotalPoints, amount: extensionTotalPoints,
model: extensionModelName, model: extensionModelName,
tokens: extensionTokens inputTokens: extensionInputTokens,
outputTokens: extensionOutputTokens
} }
] ]
: []) : [])
@ -166,17 +175,20 @@ export const pushGenerateVectorUsage = ({
}; };
export const pushQuestionGuideUsage = ({ export const pushQuestionGuideUsage = ({
tokens, inputTokens,
outputTokens,
teamId, teamId,
tmbId tmbId
}: { }: {
tokens: number; inputTokens: number;
outputTokens: number;
teamId: string; teamId: string;
tmbId: string; tmbId: string;
}) => { }) => {
const qgModel = global.llmModels[0]; const qgModel = global.llmModels[0];
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
tokens, inputTokens,
outputTokens,
model: qgModel.model, model: qgModel.model,
modelType: ModelTypeEnum.llm modelType: ModelTypeEnum.llm
}); });
@ -192,7 +204,8 @@ export const pushQuestionGuideUsage = ({
moduleName: 'core.app.Question Guide', moduleName: 'core.app.Question Guide',
amount: totalPoints, amount: totalPoints,
model: modelName, model: modelName,
tokens inputTokens,
outputTokens
} }
] ]
}); });
@ -215,7 +228,7 @@ export function pushAudioSpeechUsage({
}) { }) {
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model, model,
tokens: charsLength, inputTokens: charsLength,
modelType: ModelTypeEnum.audioSpeech modelType: ModelTypeEnum.audioSpeech
}); });
@ -251,7 +264,7 @@ export function pushWhisperUsage({
const { totalPoints, modelName } = formatModelChars2Points({ const { totalPoints, modelName } = formatModelChars2Points({
model: whisperModel.model, model: whisperModel.model,
tokens: duration, inputTokens: duration,
modelType: ModelTypeEnum.whisper, modelType: ModelTypeEnum.whisper,
multiple: 60 multiple: 60
}); });