Commit bdee2db7 by Archer Committed by GitHub

V4.14.4 dev (#6058)

* perf: faq

* index

* delete dataset

* delete dataset

* perf: delete dataset

* init

* fix: outLink UID (#6048)

* perf: query extension

* fix: s3 configs (#6050)

* fix: s3 configs

* s3

---------

Co-authored-by: archer <545436317@qq.com>

* s3 valid string check

* perf: completion api

* fix: model test

* perf: init

* fix: init

* fix: init shell

* fix: faq

---------

Co-authored-by: Roy <whoeverimf5@gmail.com>
parent 44f95038
...@@ -15,20 +15,20 @@ description: FastGPT 分享链接身份鉴权 ...@@ -15,20 +15,20 @@ description: FastGPT 分享链接身份鉴权
### 接口统一响应格式 ### 接口统一响应格式
```json ```jsonc
{ {
"success": true, "success": true,
"message": "错误提示", "message": "错误提示",
"msg": "同message, 错误提示", "msg": "同message, 错误提示",
"data": { "data": {
"uid": "用户唯一凭证" "uid": "用户唯一凭证" // 必须返回
} }
} }
``` ```
`FastGPT` 将会判断`success`是否为`true`决定是允许用户继续操作。`message`与`msg`是等同的,你可以选择返回其中一个,当`success`不为`true`时,将会提示这个错误。 `FastGPT` 将会判断`success`是否为`true`决定是允许用户继续操作。`message`与`msg`是等同的,你可以选择返回其中一个,当`success`不为`true`时,将会提示这个错误。
`uid`是用户的唯一凭证,将会用于拉取对话记录以及保存对话记录。可参考下方实践案例。 `uid` 是用户的唯一凭证,必须返回该 ID 且 ID 的格式为不包含 "|"、"/“、"\" 字符的、长度小于等于 255 的字符串,否则会返回 `Invalid UID` 的错误。`uid` 将会用于拉取对话记录以及保存对话记录,可参考下方实践案例。
### 触发流程 ### 触发流程
......
...@@ -30,6 +30,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \ ...@@ -30,6 +30,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
4. 通过 API 上传本地文件至知识库,保存至 S3。同时将旧版 Gridfs 代码全部移除。 4. 通过 API 上传本地文件至知识库,保存至 S3。同时将旧版 Gridfs 代码全部移除。
5. 新版订阅套餐逻辑。 5. 新版订阅套餐逻辑。
6. 支持配置对话文件白名单。 6. 支持配置对话文件白名单。
7. S3 支持 pathStyle 配置。
## ⚙️ 优化 ## ⚙️ 优化
...@@ -38,6 +39,8 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \ ...@@ -38,6 +39,8 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
3. 用户通知,支持中英文,以及优化模板。 3. 用户通知,支持中英文,以及优化模板。
4. 删除知识库采用队列异步删除模式。 4. 删除知识库采用队列异步删除模式。
5. LLM 请求时,图片无效报错提示。 5. LLM 请求时,图片无效报错提示。
6. completions 接口,非 stream 模式, detail=false 时,增加返回 reason_content。
7. 增加对于无效的 S3 key 检测。
## 🐛 修复 ## 🐛 修复
...@@ -47,6 +50,9 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \ ...@@ -47,6 +50,9 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
4. 工作台卡片在名字过长时错位。 4. 工作台卡片在名字过长时错位。
5. 分享链接中url query 中携带全局变量时,前端 UI 不会加载该值。 5. 分享链接中url query 中携带全局变量时,前端 UI 不会加载该值。
6. window 下判断 CSV 文件异常。 6. window 下判断 CSV 文件异常。
7. 模型测试时,如果模型未启动,会导致无法被测试。
8. MCP header 中带特殊内容时,会抛错。
9. 工作流引用其他 Agent 时,切换版本号后未及时更新 UI。
## 插件 ## 插件
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
"document/content/docs/introduction/development/openapi/chat.mdx": "2025-11-14T13:21:17+08:00", "document/content/docs/introduction/development/openapi/chat.mdx": "2025-11-14T13:21:17+08:00",
"document/content/docs/introduction/development/openapi/dataset.mdx": "2025-09-29T11:34:11+08:00", "document/content/docs/introduction/development/openapi/dataset.mdx": "2025-09-29T11:34:11+08:00",
"document/content/docs/introduction/development/openapi/intro.mdx": "2025-09-29T11:34:11+08:00", "document/content/docs/introduction/development/openapi/intro.mdx": "2025-09-29T11:34:11+08:00",
"document/content/docs/introduction/development/openapi/share.mdx": "2025-08-05T23:20:39+08:00", "document/content/docs/introduction/development/openapi/share.mdx": "2025-12-08T16:10:51+08:00",
"document/content/docs/introduction/development/proxy/cloudflare.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/development/proxy/cloudflare.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/development/proxy/http_proxy.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/development/proxy/http_proxy.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/development/proxy/nginx.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/development/proxy/nginx.mdx": "2025-07-23T21:35:03+08:00",
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
"document/content/docs/upgrading/4-14/4141.mdx": "2025-11-19T10:15:27+08:00", "document/content/docs/upgrading/4-14/4141.mdx": "2025-11-19T10:15:27+08:00",
"document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00", "document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00",
"document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00", "document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00",
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-08T01:44:15+08:00", "document/content/docs/upgrading/4-14/4144.mdx": "2025-12-08T17:57:59+08:00",
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",
......
...@@ -50,6 +50,7 @@ export class S3BaseBucket { ...@@ -50,6 +50,7 @@ export class S3BaseBucket {
port: externalPort, port: externalPort,
accessKey: options.accessKey, accessKey: options.accessKey,
secretKey: options.secretKey, secretKey: options.secretKey,
pathStyle: options.pathStyle,
transportAgent: options.transportAgent transportAgent: options.transportAgent
}); });
} }
......
...@@ -52,3 +52,9 @@ export const getSystemMaxFileSize = () => { ...@@ -52,3 +52,9 @@ export const getSystemMaxFileSize = () => {
const config = global.feConfigs?.uploadFileMaxSize || 1024; // MB, default 1024MB const config = global.feConfigs?.uploadFileMaxSize || 1024; // MB, default 1024MB
return config; // bytes return config; // bytes
}; };
export const S3_KEY_PATH_INVALID_CHARS_MAP: Record<string, boolean> = {
'/': true,
'\\': true,
'|': true
};
...@@ -4,6 +4,7 @@ import { setCron } from '../system/cron'; ...@@ -4,6 +4,7 @@ import { setCron } from '../system/cron';
import { checkTimerLock } from '../system/timerLock/utils'; import { checkTimerLock } from '../system/timerLock/utils';
import { TimerIdEnum } from '../system/timerLock/constants'; import { TimerIdEnum } from '../system/timerLock/constants';
import path from 'node:path'; import path from 'node:path';
import { S3Error } from 'minio';
export async function clearExpiredMinioFiles() { export async function clearExpiredMinioFiles() {
try { try {
...@@ -56,6 +57,12 @@ export async function clearExpiredMinioFiles() { ...@@ -56,6 +57,12 @@ export async function clearExpiredMinioFiles() {
addLog.warn(`Bucket not found: ${file.bucketName}`); addLog.warn(`Bucket not found: ${file.bucketName}`);
} }
} catch (error) { } catch (error) {
if (
error instanceof S3Error &&
error.message.includes('Object name contains unsupported characters.')
) {
await MongoS3TTL.deleteOne({ _id: file._id });
}
fail++; fail++;
addLog.error(`Failed to delete minio file: ${file.minioKey}`, error); addLog.error(`Failed to delete minio file: ${file.minioKey}`, error);
} }
......
...@@ -13,7 +13,7 @@ import { useTextCosine } from '../hooks/useTextCosine'; ...@@ -13,7 +13,7 @@ import { useTextCosine } from '../hooks/useTextCosine';
This module can eliminate referential ambiguity and expand queries based on context to improve retrieval. This module can eliminate referential ambiguity and expand queries based on context to improve retrieval.
Submodular Optimization Mode: Generate multiple candidate queries, then use submodular algorithm to select the optimal query combination Submodular Optimization Mode: Generate multiple candidate queries, then use submodular algorithm to select the optimal query combination
*/ */
const title = global.feConfigs?.systemTitle || 'FastAI'; const title = global.feConfigs?.systemTitle || 'Nginx';
const defaultPrompt = `## 你的任务 const defaultPrompt = `## 你的任务
你作为一个向量检索助手,你的任务是结合历史记录,为"原问题"生成{{count}}个不同版本的"检索词"。这些检索词应该从不同角度探索主题,以提高向量检索的语义丰富度和精度。 你作为一个向量检索助手,你的任务是结合历史记录,为"原问题"生成{{count}}个不同版本的"检索词"。这些检索词应该从不同角度探索主题,以提高向量检索的语义丰富度和精度。
...@@ -230,7 +230,7 @@ assistant: ${chatBg} ...@@ -230,7 +230,7 @@ assistant: ${chatBg}
.replace(/ /g, ''); .replace(/ /g, '');
try { try {
const queries = json5.parse(jsonStr) as string[]; let queries = json5.parse(jsonStr) as string[];
if (!Array.isArray(queries) || queries.length === 0) { if (!Array.isArray(queries) || queries.length === 0) {
return { return {
...@@ -248,6 +248,8 @@ assistant: ${chatBg} ...@@ -248,6 +248,8 @@ assistant: ${chatBg}
const { lazyGreedyQuerySelection, embeddingModel: useEmbeddingModel } = useTextCosine({ const { lazyGreedyQuerySelection, embeddingModel: useEmbeddingModel } = useTextCosine({
embeddingModel embeddingModel
}); });
queries = queries.map((item) => String(item));
const { selectedData: selectedQueries, embeddingTokens } = await lazyGreedyQuerySelection({ const { selectedData: selectedQueries, embeddingTokens } = await lazyGreedyQuerySelection({
originalText: query, originalText: query,
candidates: queries, candidates: queries,
......
...@@ -81,7 +81,7 @@ export const createLLMResponse = async <T extends CompletionsBodyType>( ...@@ -81,7 +81,7 @@ export const createLLMResponse = async <T extends CompletionsBodyType>(
return requestMessages; return requestMessages;
})(); })();
const requestBody = await llmCompletionsBodyFormat({ const { requestBody, modelData } = await llmCompletionsBodyFormat({
...body, ...body,
messages: rewriteMessages messages: rewriteMessages
}); });
...@@ -89,6 +89,7 @@ export const createLLMResponse = async <T extends CompletionsBodyType>( ...@@ -89,6 +89,7 @@ export const createLLMResponse = async <T extends CompletionsBodyType>(
// console.log(JSON.stringify(requestBody, null, 2)); // console.log(JSON.stringify(requestBody, null, 2));
const { response, isStreamResponse, getEmptyResponseTip } = await createChatCompletion({ const { response, isStreamResponse, getEmptyResponseTip } = await createChatCompletion({
body: requestBody, body: requestBody,
modelData,
userKey, userKey,
options: { options: {
headers: { headers: {
...@@ -491,10 +492,16 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({ ...@@ -491,10 +492,16 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({
parallel_tool_calls, parallel_tool_calls,
toolCallMode, toolCallMode,
...body ...body
}: LLMRequestBodyType<T>): Promise<InferCompletionsBody<T>> => { }: LLMRequestBodyType<T>): Promise<{
requestBody: InferCompletionsBody<T>;
modelData: LLMModelItemType;
}> => {
const modelData = getLLMModel(body.model); const modelData = getLLMModel(body.model);
if (!modelData) { if (!modelData) {
return body as unknown as InferCompletionsBody<T>; return {
requestBody: body as unknown as InferCompletionsBody<T>,
modelData
};
} }
const response_format = (() => { const response_format = (() => {
...@@ -548,7 +555,10 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({ ...@@ -548,7 +555,10 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({
}); });
} }
return requestBody as unknown as InferCompletionsBody<T>; return {
requestBody: requestBody as unknown as InferCompletionsBody<T>,
modelData
};
}; };
const createChatCompletion = async ({ const createChatCompletion = async ({
modelData, modelData,
...@@ -579,6 +589,7 @@ const createChatCompletion = async ({ ...@@ -579,6 +589,7 @@ const createChatCompletion = async ({
try { try {
// Rewrite model // Rewrite model
const modelConstantsData = modelData || getLLMModel(body.model); const modelConstantsData = modelData || getLLMModel(body.model);
if (!modelConstantsData) { if (!modelConstantsData) {
return Promise.reject(`${body.model} not found`); return Promise.reject(`${body.model} not found`);
} }
......
...@@ -115,6 +115,25 @@ export async function delDatasetRelevantData({ ...@@ -115,6 +115,25 @@ export async function delDatasetRelevantData({
// Delete vector data // Delete vector data
await deleteDatasetDataVector({ teamId, datasetIds }); await deleteDatasetDataVector({ teamId, datasetIds });
// Delete dataset_data_texts in batches by datasetId
for (const datasetId of datasetIds) {
await MongoDatasetDataText.deleteMany({
teamId,
datasetId
}).maxTimeMS(300000); // Reduce timeout for single batch
}
// Delete dataset_datas in batches by datasetId
for (const datasetId of datasetIds) {
await MongoDatasetData.deleteMany({
teamId,
datasetId
}).maxTimeMS(300000);
}
await delCollectionRelatedSource({ collections });
// Delete vector data
await deleteDatasetDataVector({ teamId, datasetIds });
// delete collections // delete collections
await MongoDatasetCollection.deleteMany({ await MongoDatasetCollection.deleteMany({
teamId, teamId,
......
...@@ -5,15 +5,11 @@ import { addDays } from 'date-fns'; ...@@ -5,15 +5,11 @@ import { addDays } from 'date-fns';
import { isS3ObjectKey, jwtSignS3ObjectKey } from '../../../common/s3/utils'; import { isS3ObjectKey, jwtSignS3ObjectKey } from '../../../common/s3/utils';
export const formatDatasetDataValue = ({ export const formatDatasetDataValue = ({
teamId,
datasetId,
q, q,
a, a,
imageId, imageId,
imageDescMap imageDescMap
}: { }: {
teamId: string;
datasetId: string;
q: string; q: string;
a?: string; a?: string;
imageId?: string; imageId?: string;
...@@ -73,8 +69,6 @@ export const getFormatDatasetCiteList = (list: DatasetDataSchemaType[]) => { ...@@ -73,8 +69,6 @@ export const getFormatDatasetCiteList = (list: DatasetDataSchemaType[]) => {
return list.map((item) => ({ return list.map((item) => ({
_id: item._id, _id: item._id,
...formatDatasetDataValue({ ...formatDatasetDataValue({
teamId: item.teamId,
datasetId: item.datasetId,
q: item.q, q: item.q,
a: item.a, a: item.a,
imageId: item.imageId imageId: item.imageId
......
...@@ -555,8 +555,6 @@ export async function searchDatasetData( ...@@ -555,8 +555,6 @@ export async function searchDatasetData(
id: String(data._id), id: String(data._id),
updateTime: data.updateTime, updateTime: data.updateTime,
...formatDatasetDataValue({ ...formatDatasetDataValue({
teamId,
datasetId: data.datasetId,
q: data.q, q: data.q,
a: data.a, a: data.a,
imageId: data.imageId, imageId: data.imageId,
...@@ -727,8 +725,6 @@ export async function searchDatasetData( ...@@ -727,8 +725,6 @@ export async function searchDatasetData(
collectionId: String(data.collectionId), collectionId: String(data.collectionId),
updateTime: data.updateTime, updateTime: data.updateTime,
...formatDatasetDataValue({ ...formatDatasetDataValue({
teamId,
datasetId: data.datasetId,
q: data.q, q: data.q,
a: data.a, a: data.a,
imageId: data.imageId, imageId: data.imageId,
......
...@@ -14,6 +14,7 @@ import { i18nT } from '../../../../../web/i18n/utils'; ...@@ -14,6 +14,7 @@ import { i18nT } from '../../../../../web/i18n/utils';
import { filterDatasetsByTmbId } from '../../../dataset/utils'; import { filterDatasetsByTmbId } from '../../../dataset/utils';
import { getDatasetSearchToolResponsePrompt } from '../../../../../global/core/ai/prompt/dataset'; import { getDatasetSearchToolResponsePrompt } from '../../../../../global/core/ai/prompt/dataset';
import { getNodeErrResponse } from '../utils'; import { getNodeErrResponse } from '../utils';
import { addLog } from '../../../../common/system/log';
type DatasetSearchProps = ModuleDispatchProps<{ type DatasetSearchProps = ModuleDispatchProps<{
[NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType[]; [NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType[];
...@@ -49,7 +50,6 @@ export async function dispatchDatasetSearch( ...@@ -49,7 +50,6 @@ export async function dispatchDatasetSearch(
const { const {
runningAppInfo: { teamId }, runningAppInfo: { teamId },
runningUserInfo: { tmbId }, runningUserInfo: { tmbId },
uid,
histories, histories,
node, node,
params: { params: {
...@@ -281,6 +281,7 @@ export async function dispatchDatasetSearch( ...@@ -281,6 +281,7 @@ export async function dispatchDatasetSearch(
: 'No results' : 'No results'
}; };
} catch (error) { } catch (error) {
addLog.error(`[Dataset search] error`, error);
return getNodeErrResponse({ error }); return getNodeErrResponse({ error });
} }
} }
...@@ -43,7 +43,7 @@ S3_ACCESS_KEY=minioadmin ...@@ -43,7 +43,7 @@ S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin S3_SECRET_KEY=minioadmin
S3_PUBLIC_BUCKET=fastgpt-public # 插件文件存储公开桶 S3_PUBLIC_BUCKET=fastgpt-public # 插件文件存储公开桶
S3_PRIVATE_BUCKET=fastgpt-private # 插件文件存储公开桶 S3_PRIVATE_BUCKET=fastgpt-private # 插件文件存储公开桶
S3_PATH_STYLE=false # forcePathStyle 默认为 true, 当且仅当设置为 false 时关闭, 其他值都为 true S3_PATH_STYLE=true # forcePathStyle 默认为 true, 当且仅当设置为 false 时关闭, 其他值都为 true
# Redis URL # Redis URL
REDIS_URL=redis://default:mypassword@127.0.0.1:6379 REDIS_URL=redis://default:mypassword@127.0.0.1:6379
......
...@@ -10,10 +10,6 @@ const FAQ = () => { ...@@ -10,10 +10,6 @@ const FAQ = () => {
desc: t('common:FAQ.switch_package_a') desc: t('common:FAQ.switch_package_a')
}, },
{ {
title: t('common:FAQ.year_day_q'),
desc: t('common:FAQ.year_day_a')
},
{
title: t('common:FAQ.check_subscription_q'), title: t('common:FAQ.check_subscription_q'),
desc: t('common:FAQ.check_subscription_a') desc: t('common:FAQ.check_subscription_a')
}, },
...@@ -43,6 +39,10 @@ const FAQ = () => { ...@@ -43,6 +39,10 @@ const FAQ = () => {
desc: t('common:FAQ.qpm_a') desc: t('common:FAQ.qpm_a')
}, },
{ {
title: t('common:FAQ.year_day_q'),
desc: t('common:FAQ.year_day_a')
},
{
title: t('common:FAQ.free_user_clean_q'), title: t('common:FAQ.free_user_clean_q'),
desc: t('common:FAQ.free_user_clean_a') desc: t('common:FAQ.free_user_clean_a')
} }
......
...@@ -244,7 +244,7 @@ async function processCollectionBatch({ ...@@ -244,7 +244,7 @@ async function processCollectionBatch({
{ {
projection: { projection: {
_id: 1, _id: 1,
metadata: { teamId: 1 } metadata: 1
} }
} }
) )
...@@ -259,7 +259,10 @@ async function processCollectionBatch({ ...@@ -259,7 +259,10 @@ async function processCollectionBatch({
// 2. 查找对应的 collections // 2. 查找对应的 collections
const fileIds = files.map((f) => f._id); const fileIds = files.map((f) => f._id);
const collections = await MongoDatasetCollection.find( const collections = await MongoDatasetCollection.find(
{ fileId: { $in: fileIds } }, {
teamId: { $in: Array.from(new Set(files.map((f) => f.metadata?.teamId).filter(Boolean))) },
fileId: { $in: fileIds }
},
'_id fileId teamId datasetId type parentId name updateTime' '_id fileId teamId datasetId type parentId name updateTime'
).lean(); ).lean();
...@@ -531,6 +534,11 @@ async function processImageBatch({ ...@@ -531,6 +534,11 @@ async function processImageBatch({
const imageIds = imageFiles.map((file) => file._id.toString()); const imageIds = imageFiles.map((file) => file._id.toString());
const dataList = await MongoDatasetData.find( const dataList = await MongoDatasetData.find(
{ {
teamId: { $in: Array.from(new Set(imageFiles.map((file) => file.metadata?.teamId))) },
datasetId: { $in: Array.from(new Set(imageFiles.map((file) => file.metadata?.datasetId))) },
collectionId: {
$in: Array.from(new Set(imageFiles.map((file) => file.metadata?.collectionId)))
},
imageId: { $in: imageIds } imageId: { $in: imageIds }
}, },
'_id imageId teamId datasetId collectionId updateTime' '_id imageId teamId datasetId collectionId updateTime'
......
...@@ -71,7 +71,7 @@ export default NextAPI(handler); ...@@ -71,7 +71,7 @@ export default NextAPI(handler);
const testLLMModel = async (model: LLMModelItemType, headers: Record<string, string>) => { const testLLMModel = async (model: LLMModelItemType, headers: Record<string, string>) => {
const { answerText } = await createLLMResponse({ const { answerText } = await createLLMResponse({
body: { body: {
model: model.model, model,
messages: [{ role: 'user', content: 'hi' }], messages: [{ role: 'user', content: 'hi' }],
stream: true stream: true
}, },
......
...@@ -80,8 +80,6 @@ async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuot ...@@ -80,8 +80,6 @@ async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuot
return { return {
collection, collection,
...formatDatasetDataValue({ ...formatDatasetDataValue({
teamId: datasetData.teamId,
datasetId: datasetData.datasetId,
q: datasetData.q, q: datasetData.q,
a: datasetData.a, a: datasetData.a,
imageId: datasetData.imageId imageId: datasetData.imageId
...@@ -98,8 +96,6 @@ async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuot ...@@ -98,8 +96,6 @@ async function handler(req: ApiRequestProps<GetQuoteDataProps>): Promise<GetQuot
return { return {
collection, collection,
...formatDatasetDataValue({ ...formatDatasetDataValue({
teamId: datasetData.teamId,
datasetId: datasetData.datasetId,
q: datasetData.q, q: datasetData.q,
a: datasetData.a, a: datasetData.a,
imageId: datasetData.imageId imageId: datasetData.imageId
......
...@@ -406,32 +406,59 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -406,32 +406,59 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res.end(); res.end();
} else { } else {
const responseContent = (() => { const formatResponseContent = removeAIResponseCite(assistantResponses, retainDatasetCite);
if (assistantResponses.length === 0) return ''; const formattdResponse = (() => {
if (assistantResponses.length === 1 && assistantResponses[0].text?.content) if (formatResponseContent.length === 0)
return assistantResponses[0].text?.content; return {
reasoning: '',
content: ''
};
if (formatResponseContent.length === 1) {
return {
reasoning: formatResponseContent[0].reasoning?.content,
content: formatResponseContent[0].text?.content
};
}
if (!detail) { if (!detail) {
return assistantResponses return {
.map((item) => item?.text?.content) reasoning: formatResponseContent
.filter(Boolean) .map((item) => item?.reasoning?.content)
.join('\n'); .filter(Boolean)
.join('\n'),
content: formatResponseContent
.map((item) => item?.text?.content)
.filter(Boolean)
.join('\n')
};
} }
return assistantResponses; return formatResponseContent;
})(); })();
const formatResponseContent = removeAIResponseCite(responseContent, retainDatasetCite);
const error = flowResponses[flowResponses.length - 1]?.error; const error =
flowResponses[flowResponses.length - 1]?.error ||
flowResponses[flowResponses.length - 1]?.errorText;
res.json({ res.json({
...(detail ? { responseData: feResponseData, newVariables } : {}), ...(detail ? { responseData: feResponseData, newVariables } : {}),
error, error,
id: saveChatId, id: chatId || '',
model: '', model: '',
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 1 }, usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 1 },
choices: [ choices: [
{ {
message: { role: 'assistant', content: formatResponseContent }, message: {
role: 'assistant',
...(Array.isArray(formattdResponse)
? { content: formattdResponse }
: {
content: formattdResponse.content,
...(formattdResponse.reasoning && {
reasoning_content: formattdResponse.reasoning
})
})
},
finish_reason: 'stop', finish_reason: 'stop',
index: 0 index: 0
} }
......
...@@ -402,22 +402,39 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -402,22 +402,39 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res.end(); res.end();
} else { } else {
const responseContent = (() => { const formatResponseContent = removeAIResponseCite(assistantResponses, retainDatasetCite);
if (assistantResponses.length === 0) return ''; const formattdResponse = (() => {
if (assistantResponses.length === 1 && assistantResponses[0].text?.content) if (formatResponseContent.length === 0)
return assistantResponses[0].text?.content; return {
reasoning: '',
content: ''
};
if (formatResponseContent.length === 1) {
return {
reasoning: formatResponseContent[0].reasoning?.content,
content: formatResponseContent[0].text?.content
};
}
if (!detail) { if (!detail) {
return assistantResponses return {
.map((item) => item?.text?.content) reasoning: formatResponseContent
.filter(Boolean) .map((item) => item?.reasoning?.content)
.join('\n'); .filter(Boolean)
.join('\n'),
content: formatResponseContent
.map((item) => item?.text?.content)
.filter(Boolean)
.join('\n')
};
} }
return assistantResponses; return formatResponseContent;
})(); })();
const formatResponseContent = removeAIResponseCite(responseContent, retainDatasetCite);
const error = flowResponses[flowResponses.length - 1]?.error; const error =
flowResponses[flowResponses.length - 1]?.error ||
flowResponses[flowResponses.length - 1]?.errorText;
res.json({ res.json({
...(detail ? { responseData: feResponseData, newVariables } : {}), ...(detail ? { responseData: feResponseData, newVariables } : {}),
...@@ -427,7 +444,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ...@@ -427,7 +444,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 1 }, usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 1 },
choices: [ choices: [
{ {
message: { role: 'assistant', content: formatResponseContent }, message: {
role: 'assistant',
...(Array.isArray(formattdResponse)
? { content: formattdResponse }
: {
content: formattdResponse.content,
...(formattdResponse.reasoning && {
reasoning_content: formattdResponse.reasoning
})
})
},
finish_reason: 'stop', finish_reason: 'stop',
index: 0 index: 0
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment