mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
simple mode tool reason (#3984)
* simple mode tool reason * model config cannot set empty * perf: read files code * perf: mongo gridfs chunks * perf: doc
This commit is contained in:
@@ -47,6 +47,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv490' \
|
|||||||
1. 知识库数据不再限制索引数量,可无限自定义。同时可自动更新输入文本的索引,不影响自定义索引。
|
1. 知识库数据不再限制索引数量,可无限自定义。同时可自动更新输入文本的索引,不影响自定义索引。
|
||||||
2. Markdown 解析,增加链接后中文标点符号检测,增加空格。
|
2. Markdown 解析,增加链接后中文标点符号检测,增加空格。
|
||||||
3. Prompt 模式工具调用,支持思考模型。同时优化其格式检测,减少空输出的概率。
|
3. Prompt 模式工具调用,支持思考模型。同时优化其格式检测,减少空输出的概率。
|
||||||
|
4. Mongo 文件读取流合并,减少计算量。同时优化存储 chunks,极大提高大文件读取速度。50M PDF 读取时间提高 3 倍。
|
||||||
|
|
||||||
## 🐛 修复
|
## 🐛 修复
|
||||||
|
|
||||||
|
@@ -52,7 +52,9 @@ export async function uploadFile({
|
|||||||
const stats = await fsp.stat(path);
|
const stats = await fsp.stat(path);
|
||||||
if (!stats.isFile()) return Promise.reject(`${path} is not a file`);
|
if (!stats.isFile()) return Promise.reject(`${path} is not a file`);
|
||||||
|
|
||||||
const readStream = fs.createReadStream(path);
|
const readStream = fs.createReadStream(path, {
|
||||||
|
highWaterMark: 256 * 1024
|
||||||
|
});
|
||||||
|
|
||||||
// Add default metadata
|
// Add default metadata
|
||||||
metadata.teamId = teamId;
|
metadata.teamId = teamId;
|
||||||
@@ -62,9 +64,27 @@ export async function uploadFile({
|
|||||||
// create a gridfs bucket
|
// create a gridfs bucket
|
||||||
const bucket = getGridBucket(bucketName);
|
const bucket = getGridBucket(bucketName);
|
||||||
|
|
||||||
|
const fileSize = stats.size;
|
||||||
|
const chunkSizeBytes = (() => {
|
||||||
|
// 计算理想块大小:文件大小 ÷ 目标块数(10)
|
||||||
|
const idealChunkSize = Math.ceil(fileSize / 10);
|
||||||
|
|
||||||
|
// 确保块大小至少为512KB
|
||||||
|
const minChunkSize = 512 * 1024; // 512KB
|
||||||
|
|
||||||
|
// 取理想块大小和最小块大小中的较大值
|
||||||
|
let chunkSize = Math.max(idealChunkSize, minChunkSize);
|
||||||
|
|
||||||
|
// 将块大小向上取整到最接近的64KB的倍数,使其更整齐
|
||||||
|
chunkSize = Math.ceil(chunkSize / (64 * 1024)) * (64 * 1024);
|
||||||
|
|
||||||
|
return chunkSize;
|
||||||
|
})();
|
||||||
|
|
||||||
const stream = bucket.openUploadStream(filename, {
|
const stream = bucket.openUploadStream(filename, {
|
||||||
metadata,
|
metadata,
|
||||||
contentType
|
contentType,
|
||||||
|
chunkSizeBytes
|
||||||
});
|
});
|
||||||
|
|
||||||
// save to gridfs
|
// save to gridfs
|
||||||
|
@@ -3,15 +3,13 @@ import { PassThrough } from 'stream';
|
|||||||
|
|
||||||
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
const chunks: Buffer[] = [];
|
const chunks: Uint8Array[] = [];
|
||||||
let totalLength = 0;
|
|
||||||
|
|
||||||
stream.on('data', (chunk) => {
|
stream.on('data', (chunk) => {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
totalLength += chunk.length;
|
|
||||||
});
|
});
|
||||||
stream.on('end', () => {
|
stream.on('end', () => {
|
||||||
const resultBuffer = Buffer.concat(chunks, totalLength); // 一次性拼接
|
const resultBuffer = Buffer.concat(chunks); // 一次性拼接
|
||||||
resolve(resultBuffer);
|
resolve(resultBuffer);
|
||||||
});
|
});
|
||||||
stream.on('error', (err) => {
|
stream.on('error', (err) => {
|
||||||
@@ -21,25 +19,26 @@ export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const stream2Encoding = async (stream: NodeJS.ReadableStream) => {
|
export const stream2Encoding = async (stream: NodeJS.ReadableStream) => {
|
||||||
const start = Date.now();
|
|
||||||
const copyStream = stream.pipe(new PassThrough());
|
const copyStream = stream.pipe(new PassThrough());
|
||||||
|
|
||||||
/* get encoding */
|
/* get encoding */
|
||||||
const buffer = await (() => {
|
const buffer = await (() => {
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
let tmpBuffer: Buffer = Buffer.from([]);
|
const chunks: Uint8Array[] = [];
|
||||||
|
let totalLength = 0;
|
||||||
|
|
||||||
stream.on('data', (chunk) => {
|
stream.on('data', (chunk) => {
|
||||||
if (tmpBuffer.length < 200) {
|
if (totalLength < 200) {
|
||||||
tmpBuffer = Buffer.concat([tmpBuffer, chunk]);
|
chunks.push(chunk);
|
||||||
|
totalLength += chunk.length;
|
||||||
|
|
||||||
if (tmpBuffer.length >= 200) {
|
if (totalLength >= 200) {
|
||||||
resolve(tmpBuffer);
|
resolve(Buffer.concat(chunks));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stream.on('end', () => {
|
stream.on('end', () => {
|
||||||
resolve(tmpBuffer);
|
resolve(Buffer.concat(chunks));
|
||||||
});
|
});
|
||||||
stream.on('error', (err) => {
|
stream.on('error', (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
@@ -43,13 +43,13 @@ export async function text2Speech({
|
|||||||
const readableStream = response.body as unknown as NodeJS.ReadableStream;
|
const readableStream = response.body as unknown as NodeJS.ReadableStream;
|
||||||
readableStream.pipe(res);
|
readableStream.pipe(res);
|
||||||
|
|
||||||
let bufferStore = Buffer.from([]);
|
const chunks: Uint8Array[] = [];
|
||||||
|
|
||||||
readableStream.on('data', (chunk) => {
|
readableStream.on('data', (chunk) => {
|
||||||
bufferStore = Buffer.concat([bufferStore, chunk]);
|
chunks.push(chunk);
|
||||||
});
|
});
|
||||||
readableStream.on('end', () => {
|
readableStream.on('end', () => {
|
||||||
onSuccess({ model, buffer: bufferStore });
|
onSuccess({ model, buffer: Buffer.concat(chunks) });
|
||||||
});
|
});
|
||||||
readableStream.on('error', (e) => {
|
readableStream.on('error', (e) => {
|
||||||
onError(e);
|
onError(e);
|
||||||
|
@@ -46,8 +46,8 @@
|
|||||||
"defaultConfig": {},
|
"defaultConfig": {},
|
||||||
"fieldMap": {},
|
"fieldMap": {},
|
||||||
"type": "llm",
|
"type": "llm",
|
||||||
"showTopP": true,
|
"showTopP": false,
|
||||||
"showStopSign": true
|
"showStopSign": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
"edit_channel": "Channel configuration",
|
"edit_channel": "Channel configuration",
|
||||||
"enable_channel": "Enable",
|
"enable_channel": "Enable",
|
||||||
"forbid_channel": "Disabled",
|
"forbid_channel": "Disabled",
|
||||||
|
"input maxToken_tip": "The model max_tokens parameter, if left blank, means that the model does not support it.",
|
||||||
"key_type": "API key format:",
|
"key_type": "API key format:",
|
||||||
"log": "Call log",
|
"log": "Call log",
|
||||||
"log_detail": "Log details",
|
"log_detail": "Log details",
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"log_status": "Status",
|
"log_status": "Status",
|
||||||
"mapping": "Model Mapping",
|
"mapping": "Model Mapping",
|
||||||
"mapping_tip": "A valid Json is required. \nThe model can be mapped when sending a request to the actual address. \nFor example:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\nWhen FastGPT requests the gpt-4o model, the gpt-4o-test model is sent to the actual address, instead of gpt-4o.",
|
"mapping_tip": "A valid Json is required. \nThe model can be mapped when sending a request to the actual address. \nFor example:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\nWhen FastGPT requests the gpt-4o model, the gpt-4o-test model is sent to the actual address, instead of gpt-4o.",
|
||||||
|
"max_temperature_tip": "If the model temperature parameter is not filled in, it means that the model does not support the temperature parameter.",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
"model_name": "Model name",
|
"model_name": "Model name",
|
||||||
"model_test": "Model testing",
|
"model_test": "Model testing",
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
"edit_channel": "渠道配置",
|
"edit_channel": "渠道配置",
|
||||||
"enable_channel": "启用",
|
"enable_channel": "启用",
|
||||||
"forbid_channel": "禁用",
|
"forbid_channel": "禁用",
|
||||||
|
"input maxToken_tip": "模型 max_tokens 参数,如果留空,则代表模型不支持该参数。",
|
||||||
"key_type": "API key 格式: ",
|
"key_type": "API key 格式: ",
|
||||||
"log": "调用日志",
|
"log": "调用日志",
|
||||||
"log_detail": "日志详情",
|
"log_detail": "日志详情",
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"log_status": "状态",
|
"log_status": "状态",
|
||||||
"mapping": "模型映射",
|
"mapping": "模型映射",
|
||||||
"mapping_tip": "需填写一个有效 Json。可在向实际地址发送请求时,对模型进行映射。例如:\n{\n \"gpt-4o\": \"gpt-4o-test\"\n}\n当 FastGPT 请求 gpt-4o 模型时,会向实际地址发送 gpt-4o-test 的模型,而不是 gpt-4o。",
|
"mapping_tip": "需填写一个有效 Json。可在向实际地址发送请求时,对模型进行映射。例如:\n{\n \"gpt-4o\": \"gpt-4o-test\"\n}\n当 FastGPT 请求 gpt-4o 模型时,会向实际地址发送 gpt-4o-test 的模型,而不是 gpt-4o。",
|
||||||
|
"max_temperature_tip": "模型 temperature 参数,不填则代表模型不支持 temperature 参数。",
|
||||||
"model": "模型",
|
"model": "模型",
|
||||||
"model_name": "模型名",
|
"model_name": "模型名",
|
||||||
"model_test": "模型测试",
|
"model_test": "模型测试",
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
"edit_channel": "渠道配置",
|
"edit_channel": "渠道配置",
|
||||||
"enable_channel": "啟用",
|
"enable_channel": "啟用",
|
||||||
"forbid_channel": "禁用",
|
"forbid_channel": "禁用",
|
||||||
|
"input maxToken_tip": "模型 max_tokens 參數,如果留空,則代表模型不支持該參數。",
|
||||||
"key_type": "API key 格式:",
|
"key_type": "API key 格式:",
|
||||||
"log": "調用日誌",
|
"log": "調用日誌",
|
||||||
"log_detail": "日誌詳情",
|
"log_detail": "日誌詳情",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
"log_status": "狀態",
|
"log_status": "狀態",
|
||||||
"mapping": "模型映射",
|
"mapping": "模型映射",
|
||||||
"mapping_tip": "需填寫一個有效 Json。\n可在向實際地址發送請求時,對模型進行映射。\n例如:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\n當 FastGPT 請求 gpt-4o 模型時,會向實際地址發送 gpt-4o-test 的模型,而不是 gpt-4o。",
|
"mapping_tip": "需填寫一個有效 Json。\n可在向實際地址發送請求時,對模型進行映射。\n例如:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\n當 FastGPT 請求 gpt-4o 模型時,會向實際地址發送 gpt-4o-test 的模型,而不是 gpt-4o。",
|
||||||
|
"max_temperature_tip": "模型 temperature 參數,不填則代表模型不支持 temperature 參數。",
|
||||||
"model": "模型",
|
"model": "模型",
|
||||||
"model_name": "模型名",
|
"model_name": "模型名",
|
||||||
"model_test": "模型測試",
|
"model_test": "模型測試",
|
||||||
|
@@ -38,6 +38,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import { Prompt_CQJson, Prompt_ExtractJson } from '@fastgpt/global/core/ai/prompt/agent';
|
import { Prompt_CQJson, Prompt_ExtractJson } from '@fastgpt/global/core/ai/prompt/agent';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
|
|
||||||
export const AddModelButton = ({
|
export const AddModelButton = ({
|
||||||
onCreate,
|
onCreate,
|
||||||
@@ -134,6 +135,14 @@ export const ModelEditModal = ({
|
|||||||
|
|
||||||
const { runAsync: updateModel, loading: updatingModel } = useRequest2(
|
const { runAsync: updateModel, loading: updatingModel } = useRequest2(
|
||||||
async (data: SystemModelItemType) => {
|
async (data: SystemModelItemType) => {
|
||||||
|
for (const key in data) {
|
||||||
|
// @ts-ignore
|
||||||
|
const val = data[key];
|
||||||
|
if (val === null || val === undefined || Number.isNaN(val)) {
|
||||||
|
// @ts-ignore
|
||||||
|
data[key] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
return putSystemModel({
|
return putSystemModel({
|
||||||
model: data.model,
|
model: data.model,
|
||||||
metadata: data
|
metadata: data
|
||||||
@@ -295,7 +304,16 @@ export const ModelEditModal = ({
|
|||||||
{isLLMModel && (
|
{isLLMModel && (
|
||||||
<>
|
<>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td>{t('common:core.ai.Max context')}</Td>
|
<Td>
|
||||||
|
<FormLabel
|
||||||
|
required
|
||||||
|
color={'myGray.600'}
|
||||||
|
fontSize={'md'}
|
||||||
|
fontWeight={'normal'}
|
||||||
|
>
|
||||||
|
{t('common:core.ai.Max context')}
|
||||||
|
</FormLabel>
|
||||||
|
</Td>
|
||||||
<Td textAlign={'right'}>
|
<Td textAlign={'right'}>
|
||||||
<Flex justifyContent={'flex-end'}>
|
<Flex justifyContent={'flex-end'}>
|
||||||
<MyNumberInput
|
<MyNumberInput
|
||||||
@@ -308,7 +326,16 @@ export const ModelEditModal = ({
|
|||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td>{t('account:model.max_quote')}</Td>
|
<Td>
|
||||||
|
<FormLabel
|
||||||
|
required
|
||||||
|
color={'myGray.600'}
|
||||||
|
fontSize={'md'}
|
||||||
|
fontWeight={'normal'}
|
||||||
|
>
|
||||||
|
{t('account:model.max_quote')}
|
||||||
|
</FormLabel>
|
||||||
|
</Td>
|
||||||
<Td textAlign={'right'}>
|
<Td textAlign={'right'}>
|
||||||
<Flex justifyContent={'flex-end'}>
|
<Flex justifyContent={'flex-end'}>
|
||||||
<MyNumberInput
|
<MyNumberInput
|
||||||
@@ -321,7 +348,12 @@ export const ModelEditModal = ({
|
|||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td>{t('common:core.chat.response.module maxToken')}</Td>
|
<Td>
|
||||||
|
<HStack spacing={1}>
|
||||||
|
<Box>{t('common:core.chat.response.module maxToken')}</Box>
|
||||||
|
<QuestionTip label={t('account_model:input maxToken_tip')} />
|
||||||
|
</HStack>
|
||||||
|
</Td>
|
||||||
<Td textAlign={'right'}>
|
<Td textAlign={'right'}>
|
||||||
<Flex justifyContent={'flex-end'}>
|
<Flex justifyContent={'flex-end'}>
|
||||||
<MyNumberInput register={register} name="maxResponse" {...InputStyles} />
|
<MyNumberInput register={register} name="maxResponse" {...InputStyles} />
|
||||||
@@ -329,7 +361,12 @@ export const ModelEditModal = ({
|
|||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td>{t('account:model.max_temperature')}</Td>
|
<Td>
|
||||||
|
<HStack spacing={1}>
|
||||||
|
<Box>{t('account:model.max_temperature')}</Box>
|
||||||
|
<QuestionTip label={t('account_model:max_temperature_tip')} />
|
||||||
|
</HStack>
|
||||||
|
</Td>
|
||||||
<Td textAlign={'right'}>
|
<Td textAlign={'right'}>
|
||||||
<Flex justifyContent={'flex-end'}>
|
<Flex justifyContent={'flex-end'}>
|
||||||
<MyNumberInput
|
<MyNumberInput
|
||||||
|
@@ -504,6 +504,13 @@ export function form2AppWorkflow(
|
|||||||
label: '',
|
label: '',
|
||||||
valueType: WorkflowIOValueTypeEnum.boolean,
|
valueType: WorkflowIOValueTypeEnum.boolean,
|
||||||
value: true
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: NodeInputKeyEnum.aiChatReasoning,
|
||||||
|
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||||
|
label: '',
|
||||||
|
valueType: WorkflowIOValueTypeEnum.boolean,
|
||||||
|
value: formData.aiSettings.aiChatReasoning
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outputs: ToolModule.outputs
|
outputs: ToolModule.outputs
|
||||||
|
Reference in New Issue
Block a user