mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
fix: export log dateend;feat: file selector render (#6072)
* fix: export log dateend * feat: file selector render
This commit is contained in:
parent
3c20d9475f
commit
9f09fbffbb
|
|
@ -32,6 +32,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
|
|||
6. 支持配置对话文件白名单。
|
||||
7. S3 支持 pathStyle 配置。
|
||||
8. 支持通过 Sealos 来进行多租户自定义域名配置。
|
||||
9. 工作流中引用工具时,文件输入支持手动填写(原本只支持变量引用)。
|
||||
|
||||
## ⚙️ 优化
|
||||
|
||||
|
|
@ -57,6 +58,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
|
|||
10. 发布渠道文档链接定位错误。
|
||||
11. Checkbox 在禁用状态时,hover 样式错误。
|
||||
12. 模型头像缺失情况下,默认 huggingface.svg 图标显示错误。
|
||||
13. 日志导出时,结束时间会多出一天。
|
||||
|
||||
## 插件
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@
|
|||
"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/4143.mdx": "2025-11-26T20:52:05+08:00",
|
||||
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-10T11:41:15+08:00",
|
||||
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-10T11:23:18+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/42.mdx": "2025-08-02T19:38:37+08:00",
|
||||
|
|
@ -201,4 +201,4 @@
|
|||
"document/content/docs/use-cases/external-integration/openapi.mdx": "2025-09-29T11:34:11+08:00",
|
||||
"document/content/docs/use-cases/external-integration/wecom.mdx": "2025-12-09T23:33:32+08:00",
|
||||
"document/content/docs/use-cases/index.mdx": "2025-07-24T14:23:04+08:00"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,3 +16,10 @@ export const parseI18nString = (str: I18nStringType | string = '', lang = 'en')
|
|||
// 最后回退到英文
|
||||
return str['en'] || '';
|
||||
};
|
||||
|
||||
export const formatI18nLocationToZhEn = (locale: localeType = 'zh-CN'): 'zh' | 'en' => {
|
||||
if (locale.toLocaleLowerCase().startsWith('zh')) {
|
||||
return 'zh';
|
||||
}
|
||||
return 'en';
|
||||
};
|
||||
|
|
|
|||
|
|
@ -209,14 +209,15 @@ export type DispatchNodeResponseType = {
|
|||
headers?: Record<string, any>;
|
||||
httpResult?: Record<string, any>;
|
||||
|
||||
// plugin output
|
||||
// Tool
|
||||
toolInput?: Record<string, any>;
|
||||
pluginOutput?: Record<string, any>;
|
||||
pluginDetail?: ChatHistoryItemResType[];
|
||||
|
||||
// if-else
|
||||
ifElseResult?: string;
|
||||
|
||||
// tool
|
||||
// tool call
|
||||
toolCallInputTokens?: number;
|
||||
toolCallOutputTokens?: number;
|
||||
toolDetail?: ChatHistoryItemResType[];
|
||||
|
|
@ -225,9 +226,6 @@ export type DispatchNodeResponseType = {
|
|||
// code
|
||||
codeLog?: string;
|
||||
|
||||
// plugin
|
||||
pluginOutput?: Record<string, any>;
|
||||
|
||||
// read files
|
||||
readFilesResult?: string;
|
||||
readFiles?: ReadFileNodeResponse;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import fs from 'node:fs';
|
|||
import type { ReaderModel } from '@maxmind/geoip2-node';
|
||||
import { Reader } from '@maxmind/geoip2-node';
|
||||
import { cleanupIntervalMs, dbPath, privateOrOtherLocationName } from './constants';
|
||||
import type { I18nName, LocationName } from './type';
|
||||
import type { LocationName } from './type';
|
||||
import { extractLocationData } from './utils';
|
||||
import type { NextApiRequest } from 'next';
|
||||
import { getClientIp } from 'request-ip';
|
||||
import { addLog } from '../system/log';
|
||||
import type { localeType } from '@fastgpt/global/common/i18n/type';
|
||||
import { formatI18nLocationToZhEn } from '@fastgpt/global/common/i18n/utils';
|
||||
|
||||
let reader: ReaderModel | null = null;
|
||||
|
||||
|
|
@ -25,21 +27,23 @@ export function getGeoReader() {
|
|||
return reader;
|
||||
}
|
||||
|
||||
export function getLocationFromIp(ip?: string, locale: keyof I18nName = 'zh') {
|
||||
export function getLocationFromIp(ip?: string, locale: localeType = 'zh-CN') {
|
||||
const formatedLocale = formatI18nLocationToZhEn(locale);
|
||||
|
||||
if (!ip) {
|
||||
return privateOrOtherLocationName.country?.[locale];
|
||||
return privateOrOtherLocationName.country?.[formatedLocale];
|
||||
}
|
||||
const reader = getGeoReader();
|
||||
|
||||
let locationName = locationIpMap.get(ip);
|
||||
if (locationName) {
|
||||
return [
|
||||
locationName.country?.[locale],
|
||||
locationName.province?.[locale],
|
||||
locationName.city?.[locale]
|
||||
locationName.country?.[formatedLocale],
|
||||
locationName.province?.[formatedLocale],
|
||||
locationName.city?.[formatedLocale]
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(locale === 'zh' ? ',' : ',');
|
||||
.join(formatedLocale === 'zh' ? ',' : ',');
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -62,15 +66,15 @@ export function getLocationFromIp(ip?: string, locale: keyof I18nName = 'zh') {
|
|||
locationIpMap.set(ip, locationName);
|
||||
|
||||
return [
|
||||
locationName.country?.[locale],
|
||||
locationName.province?.[locale],
|
||||
locationName.city?.[locale]
|
||||
locationName.country?.[formatedLocale],
|
||||
locationName.province?.[formatedLocale],
|
||||
locationName.city?.[formatedLocale]
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(locale === 'zh' ? ',' : ', ');
|
||||
.join(formatedLocale === 'zh' ? ',' : ', ');
|
||||
} catch (error) {
|
||||
locationIpMap.set(ip, privateOrOtherLocationName);
|
||||
return privateOrOtherLocationName.country?.[locale];
|
||||
return privateOrOtherLocationName.country?.[formatedLocale];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
} = props;
|
||||
|
||||
const systemToolId = toolConfig?.systemTool?.toolId;
|
||||
let toolInput: Record<string, any> = {};
|
||||
|
||||
try {
|
||||
// run system tool
|
||||
|
|
@ -78,10 +79,11 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
return dbPlugin?.inputListVal || {};
|
||||
}
|
||||
})();
|
||||
toolInput = Object.fromEntries(
|
||||
Object.entries(params).filter(([key]) => key !== NodeInputKeyEnum.systemInputConfig)
|
||||
);
|
||||
const inputs = {
|
||||
...Object.fromEntries(
|
||||
Object.entries(params).filter(([key]) => key !== NodeInputKeyEnum.systemInputConfig)
|
||||
),
|
||||
...toolInput,
|
||||
...inputConfigParams
|
||||
};
|
||||
|
||||
|
|
@ -132,6 +134,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
return {
|
||||
data: res.error,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolInput,
|
||||
toolRes: res.error,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
|
|
@ -148,6 +151,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
return {
|
||||
error: res.error,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolInput,
|
||||
error: res.error,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
|
|
@ -179,6 +183,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
data: result,
|
||||
[DispatchNodeResponseKeyEnum.answerText]: answerText,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolInput,
|
||||
toolRes: result,
|
||||
moduleLogo: avatar,
|
||||
totalPoints: usagePoints
|
||||
|
|
@ -213,10 +218,12 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
});
|
||||
props.mcpClientMemory[url] = mcpClient;
|
||||
|
||||
toolInput = params;
|
||||
const result = await mcpClient.toolCall({ toolName, params, closeConnection: false });
|
||||
return {
|
||||
data: { [NodeOutputKeyEnum.rawResponse]: result },
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolInput,
|
||||
toolRes: result,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
|
|
@ -241,6 +248,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
throw new Error(`HTTP tool ${toolName} not found`);
|
||||
}
|
||||
|
||||
toolInput = params;
|
||||
const { data, errorMsg } = await runHTTPTool({
|
||||
baseUrl: baseUrl || '',
|
||||
toolPath: httpTool.path,
|
||||
|
|
@ -262,6 +270,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
return {
|
||||
error: { [NodeOutputKeyEnum.errorText]: errorMsg },
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolInput,
|
||||
toolRes: errorMsg,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
|
|
@ -274,6 +283,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
return {
|
||||
data: { [NodeOutputKeyEnum.rawResponse]: data, ...(typeof data === 'object' ? data : {}) },
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolInput,
|
||||
toolRes: data,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
|
|
@ -290,6 +300,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
storeSecret: headerSecret
|
||||
})
|
||||
});
|
||||
toolInput = restParams;
|
||||
const result = await mcpClient.toolCall({ toolName, params: restParams });
|
||||
|
||||
return {
|
||||
|
|
@ -297,6 +308,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
[NodeOutputKeyEnum.rawResponse]: result
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolInput,
|
||||
toolRes: result,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
|
|
@ -318,6 +330,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
|||
return getNodeErrResponse({
|
||||
error,
|
||||
customNodeResponse: {
|
||||
toolInput,
|
||||
moduleLogo: avatar
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -105,6 +105,12 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||
let val = data[input.key] ?? input.value;
|
||||
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.password)) {
|
||||
val = anyValueDecrypt(val);
|
||||
} else if (
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) &&
|
||||
Array.isArray(val) &&
|
||||
data[input.key]
|
||||
) {
|
||||
data[input.key] = val.map((item) => item.url);
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -172,6 +178,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
moduleLogo: plugin.avatar,
|
||||
totalPoints: usagePoints,
|
||||
toolInput: data,
|
||||
pluginOutput: output?.pluginOutput,
|
||||
pluginDetail: pluginData?.permission?.hasWritePer // Not system plugin
|
||||
? flowResponses.filter((item) => {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ export const dispatchPluginInput = async (
|
|||
return {
|
||||
data: {
|
||||
...params,
|
||||
|
||||
// 旧版本适配
|
||||
[NodeOutputKeyEnum.userFiles]: files
|
||||
.map((item) => {
|
||||
return item?.url ?? '';
|
||||
|
|
|
|||
|
|
@ -51,61 +51,56 @@ const NodeInputSelect = ({
|
|||
{
|
||||
type: FlowNodeInputTypeEnum.textarea,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.textarea].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.JSONEditor,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.JSONEditor].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.addInputParam,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.addInputParam].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.dynamicTargetInput')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.selectLLMModel,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.selectLLMModel].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual select')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.settingLLMModel,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.settingLLMModel].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual select')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.selectDataset,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.selectDataset].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual select')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.selectDatasetParamsModal,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.selectDatasetParamsModal].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual select')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.settingDatasetQuotePrompt,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.settingDatasetQuotePrompt].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.hidden].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.custom,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.custom].icon,
|
||||
|
||||
title: t('common:core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
type: FlowNodeInputTypeEnum.fileSelect,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.fileSelect].icon,
|
||||
title: t('common:core.workflow.inputType.Manual input')
|
||||
}
|
||||
]);
|
||||
|
|
@ -122,7 +117,7 @@ const NodeInputSelect = ({
|
|||
onChange(input.type);
|
||||
}
|
||||
})),
|
||||
[renderType]
|
||||
[onChange, renderType]
|
||||
);
|
||||
|
||||
const filterMenuList = useMemo(
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@
|
|||
"expand_tool_create": "Expand MCP/Http create",
|
||||
"export_config_successful": "Configuration copied, some sensitive information automatically filtered. Please check for any remaining sensitive data.",
|
||||
"export_configs": "Export",
|
||||
"export_log_filename": "{{name}} chat logs.csv",
|
||||
"fastgpt_marketplace": "FastGPT plug-in market",
|
||||
"feedback_count": "User Feedback",
|
||||
"file_quote_link": "Files",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"citations": "{{num}} References",
|
||||
"clear_input_value": "Clear input",
|
||||
"click_contextual_preview": "Click to see contextual preview",
|
||||
"click_to_add_url": "Click to add link",
|
||||
"click_to_add_url": "Enter file link",
|
||||
"completion_finish_close": "Disconnection",
|
||||
"completion_finish_content_filter": "Trigger safe wind control",
|
||||
"completion_finish_function_call": "Function Calls",
|
||||
|
|
@ -51,6 +51,7 @@
|
|||
"home.no_available_tools": "No tools available",
|
||||
"home.select_tools": "Select Tool",
|
||||
"home.tools": "Tool: {{num}}",
|
||||
"images_collection_not_supported": "Image collection is not supported open the original file",
|
||||
"in_progress": "In Progress",
|
||||
"input_guide": "Input Guide",
|
||||
"input_guide_lexicon": "Lexicon",
|
||||
|
|
@ -75,7 +76,6 @@
|
|||
"query_extension_result": "Problem optimization results",
|
||||
"question_tip": "From top to bottom, the response order of each module",
|
||||
"read_raw_source": "Open the original text",
|
||||
"images_collection_not_supported": "Image collection is not supported open the original file",
|
||||
"reasoning_text": "Thinking process",
|
||||
"release_cancel": "Release Cancel",
|
||||
"release_send": "Release send, slide up to cancel",
|
||||
|
|
@ -167,6 +167,8 @@
|
|||
"start_chat": "Start",
|
||||
"stream_output": "Stream Output",
|
||||
"task_has_continued": "Task has continued running",
|
||||
"tool_input": "tool input",
|
||||
"tool_output": "Tool output",
|
||||
"unsupported_file_type": "Unsupported file types",
|
||||
"upload": "Upload",
|
||||
"variable_invisable_in_share": "External variables are not visible in login-free links",
|
||||
|
|
|
|||
|
|
@ -427,7 +427,6 @@
|
|||
"core.chat.response.module query": "Question/Search Term",
|
||||
"core.chat.response.module similarity": "Similarity",
|
||||
"core.chat.response.module temperature": "Temperature",
|
||||
"core.chat.response.plugin output": "Plugin Output Value",
|
||||
"core.chat.response.search using reRank": "Result Re-Rank",
|
||||
"core.chat.response.text output": "Text Output",
|
||||
"core.chat.response.update_var_result": "Variable Update Result (Displays Multiple Variable Update Results in Order)",
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@
|
|||
"expand_tool_create": "展开MCP、Http创建",
|
||||
"export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据",
|
||||
"export_configs": "导出配置",
|
||||
"export_log_filename": "{{name}} 对话日志.csv",
|
||||
"fastgpt_marketplace": "FastGPT 插件市场",
|
||||
"feedback_count": "用户反馈",
|
||||
"file_quote_link": "文件链接",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"citations": "{{num}}条引用",
|
||||
"clear_input_value": "清空输入",
|
||||
"click_contextual_preview": "点击查看上下文预览",
|
||||
"click_to_add_url": "点击添加链接",
|
||||
"click_to_add_url": "输入文件链接",
|
||||
"completion_finish_close": "连接断开",
|
||||
"completion_finish_content_filter": "触发安全风控",
|
||||
"completion_finish_function_call": "函数调用",
|
||||
|
|
@ -51,6 +51,7 @@
|
|||
"home.no_available_tools": "暂无可用工具",
|
||||
"home.select_tools": "选择工具",
|
||||
"home.tools": "工具:{{num}}",
|
||||
"images_collection_not_supported": "图片数据集不支持打开原文",
|
||||
"in_progress": "进行中",
|
||||
"input_guide": "输入引导",
|
||||
"input_guide_lexicon": "词库",
|
||||
|
|
@ -75,7 +76,6 @@
|
|||
"query_extension_result": "问题优化结果",
|
||||
"question_tip": "从上到下,为各个模块的响应顺序",
|
||||
"read_raw_source": "打开原文",
|
||||
"images_collection_not_supported": "图片数据集不支持打开原文",
|
||||
"reasoning_text": "思考过程",
|
||||
"release_cancel": "松开取消",
|
||||
"release_send": "松开发送,上滑取消",
|
||||
|
|
@ -170,6 +170,8 @@
|
|||
"start_chat": "开始对话",
|
||||
"stream_output": "流输出",
|
||||
"task_has_continued": "任务已继续运行",
|
||||
"tool_input": "工具输入",
|
||||
"tool_output": "工具输出",
|
||||
"unsupported_file_type": "不支持的文件类型",
|
||||
"upload": "上传",
|
||||
"variable_invisable_in_share": "外部变量在免登录链接中不可见",
|
||||
|
|
|
|||
|
|
@ -430,7 +430,6 @@
|
|||
"core.chat.response.module query": "问题/检索词",
|
||||
"core.chat.response.module similarity": "相似度",
|
||||
"core.chat.response.module temperature": "温度",
|
||||
"core.chat.response.plugin output": "插件输出值",
|
||||
"core.chat.response.search using reRank": "结果重排",
|
||||
"core.chat.response.text output": "文本输出",
|
||||
"core.chat.response.update_var_result": "变量更新结果(按顺序展示多个变量更新结果)",
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@
|
|||
"expand_tool_create": "展開 MCP、Http 創建",
|
||||
"export_config_successful": "已複製設定,自動過濾部分敏感資訊,請注意檢查是否仍有敏感資料",
|
||||
"export_configs": "匯出設定",
|
||||
"export_log_filename": "{{name}} 對話日誌.csv",
|
||||
"fastgpt_marketplace": "FastGPT 插件市場",
|
||||
"feedback_count": "使用者回饋",
|
||||
"file_quote_link": "檔案連結",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"citations": "{{num}} 筆引用",
|
||||
"clear_input_value": "清空輸入",
|
||||
"click_contextual_preview": "點選檢視上下文預覽",
|
||||
"click_to_add_url": "點擊添加鏈接",
|
||||
"click_to_add_url": "輸入文件鏈接",
|
||||
"completion_finish_close": "連接斷開",
|
||||
"completion_finish_content_filter": "觸發安全風控",
|
||||
"completion_finish_function_call": "函式呼叫",
|
||||
|
|
@ -51,6 +51,7 @@
|
|||
"home.no_available_tools": "暫無可用工具",
|
||||
"home.select_tools": "選擇工具",
|
||||
"home.tools": "工具:{{num}}",
|
||||
"images_collection_not_supported": "圖片資料集不支持開啟原文",
|
||||
"in_progress": "進行中",
|
||||
"input_guide": "輸入導引",
|
||||
"input_guide_lexicon": "詞彙庫",
|
||||
|
|
@ -75,7 +76,6 @@
|
|||
"query_extension_result": "問題優化結果",
|
||||
"question_tip": "由上至下,各個模組的回應順序",
|
||||
"read_raw_source": "開啟原文",
|
||||
"images_collection_not_supported": "圖片資料集不支持開啟原文",
|
||||
"reasoning_text": "思考過程",
|
||||
"release_cancel": "鬆開取消",
|
||||
"release_send": "鬆開傳送,上滑取消",
|
||||
|
|
@ -167,6 +167,8 @@
|
|||
"start_chat": "開始對話",
|
||||
"stream_output": "串流輸出",
|
||||
"task_has_continued": "任務已繼續運行",
|
||||
"tool_input": "工具輸入",
|
||||
"tool_output": "工具輸出",
|
||||
"unsupported_file_type": "不支援的檔案類型",
|
||||
"upload": "上傳",
|
||||
"variable_invisable_in_share": "外部變量在免登錄鏈接中不可見",
|
||||
|
|
|
|||
|
|
@ -427,7 +427,6 @@
|
|||
"core.chat.response.module query": "問題/搜尋詞",
|
||||
"core.chat.response.module similarity": "相似度",
|
||||
"core.chat.response.module temperature": "溫度",
|
||||
"core.chat.response.plugin output": "外掛程式輸出值",
|
||||
"core.chat.response.search using reRank": "結果重新排名",
|
||||
"core.chat.response.text output": "文字輸出",
|
||||
"core.chat.response.update_var_result": "變數更新結果(依序顯示多個變數更新結果)",
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import { POST } from '@/web/common/api/request';
|
|||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { formatFileSize } from '@fastgpt/global/common/file/tools';
|
||||
import { WorkflowRuntimeContext } from '@/components/core/chat/ChatContainer/context/workflowRuntimeContext';
|
||||
import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation';
|
||||
|
||||
const FileSelector = ({
|
||||
value,
|
||||
|
|
@ -53,7 +54,7 @@ const FileSelector = ({
|
|||
}) => {
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const { t } = useSafeTranslation();
|
||||
|
||||
const appId = useContextSelector(WorkflowRuntimeContext, (v) => v.appId);
|
||||
const chatId = useContextSelector(WorkflowRuntimeContext, (v) => v.chatId);
|
||||
|
|
@ -491,7 +492,7 @@ const FileSelector = ({
|
|||
</HStack>
|
||||
{file?.error && (
|
||||
<Box mt={1} fontSize={'xs'} color={'red.600'}>
|
||||
{file?.error}
|
||||
{t(file.error)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -350,10 +350,8 @@ export const WholeResponseContent = ({
|
|||
</>
|
||||
{/* plugin */}
|
||||
<>
|
||||
<Row
|
||||
label={t('common:core.chat.response.plugin output')}
|
||||
value={activeModule?.pluginOutput}
|
||||
/>
|
||||
<Row label={t('chat:tool_input')} value={activeModule?.toolInput} />
|
||||
<Row label={t('chat:tool_output')} value={activeModule?.pluginOutput} />
|
||||
</>
|
||||
{/* text output */}
|
||||
<Row label={t('common:core.chat.response.text output')} value={activeModule?.textOutput} />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export type GetAppChatLogsProps = {
|
|||
sources?: ChatSourceEnum[];
|
||||
tmbIds?: string[];
|
||||
chatSearch?: string;
|
||||
locale?: keyof I18nName;
|
||||
};
|
||||
|
||||
export type GetAppChatLogsParams = PaginationProps<GetAppChatLogsProps>;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import MultipleSelect, {
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import DateRangePicker from '@fastgpt/web/components/common/DateRangePicker';
|
||||
import { addDays } from 'date-fns';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import { getTeamMembers } from '@/web/support/user/team/api';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
|
|
@ -50,7 +49,8 @@ import dynamic from 'next/dynamic';
|
|||
import type { HeaderControlProps } from './LogChart';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import type { I18nName } from '@fastgpt/service/common/geo/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '../context';
|
||||
|
||||
const DetailLogsModal = dynamic(() => import('./DetailLogsModal'));
|
||||
|
||||
|
|
@ -65,10 +65,11 @@ const LogTable = ({
|
|||
showSourceSelector = true,
|
||||
px = [4, 8]
|
||||
}: HeaderControlProps) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const [detailLogsId, setDetailLogsId] = useState<string>();
|
||||
const appName = useContextSelector(AppContext, (v) => v.appDetail.name);
|
||||
|
||||
// source
|
||||
const sourceList = useMemo(
|
||||
|
|
@ -147,15 +148,14 @@ const LogTable = ({
|
|||
const headerTitle = enabledKeys.map((k) => t(AppLogKeysEnumMap[k])).join(',');
|
||||
await downloadFetch({
|
||||
url: '/api/core/app/exportChatLogs',
|
||||
filename: 'chat_logs.csv',
|
||||
filename: t('app:export_log_filename', { name: appName }),
|
||||
body: {
|
||||
appId,
|
||||
dateStart: dayjs(dateRange.from || new Date()).format(),
|
||||
dateEnd: dayjs(addDays(dateRange.to || new Date(), 1)).format(),
|
||||
dateEnd: dayjs(dateRange.to || new Date()).format(),
|
||||
sources: isSelectAllSource ? undefined : chatSources,
|
||||
tmbIds: isSelectAllTmb ? undefined : selectTmbIds,
|
||||
chatSearch,
|
||||
locale: i18n.language === 'zh-CN' ? 'zh' : 'en',
|
||||
title: `${headerTitle},${t('app:logs_keys_chatDetails')}`,
|
||||
logKeys: enabledKeys,
|
||||
sourcesMap: Object.fromEntries(
|
||||
|
|
@ -180,8 +180,7 @@ const LogTable = ({
|
|||
dateEnd: dateRange.to!,
|
||||
sources: isSelectAllSource ? undefined : chatSources,
|
||||
tmbIds: isSelectAllTmb ? undefined : selectTmbIds,
|
||||
chatSearch,
|
||||
locale: (i18n.language === 'zh-CN' ? 'zh' : 'en') as keyof I18nName
|
||||
chatSearch
|
||||
}),
|
||||
[
|
||||
appId,
|
||||
|
|
@ -191,8 +190,7 @@ const LogTable = ({
|
|||
isSelectAllSource,
|
||||
selectTmbIds,
|
||||
isSelectAllTmb,
|
||||
chatSearch,
|
||||
i18n.language
|
||||
chatSearch
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const RenderList: Record<
|
|||
Component: dynamic(() => import('./templates/Reference'))
|
||||
},
|
||||
[FlowNodeInputTypeEnum.fileSelect]: {
|
||||
Component: dynamic(() => import('./templates/Reference'))
|
||||
Component: dynamic(() => import('./templates/FileSelect'))
|
||||
},
|
||||
[FlowNodeInputTypeEnum.selectApp]: {
|
||||
Component: dynamic(() => import('./templates/SelectApp'))
|
||||
|
|
@ -135,6 +135,8 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
|
|||
|
||||
if (!RenderItem) return null;
|
||||
|
||||
console.log(renderType, input);
|
||||
|
||||
return {
|
||||
Component: (
|
||||
<RenderItem.Component inputs={filterProInputs} item={input} nodeId={nodeId} />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { Box, Button, HStack, Input, InputGroup, useDisclosure, VStack } from '@chakra-ui/react';
|
||||
import type { SelectAppItemType } from '@fastgpt/global/core/workflow/template/system/abandoned/runApp/type';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import SelectAppModal from '../../../../SelectAppModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { getAppDetailById } from '@/web/core/app/api';
|
||||
import { WorkflowActionsContext } from '@/pageComponents/app/detail/WorkflowComponents/context/workflowActionsContext';
|
||||
import { AppContext } from '@/pageComponents/app/detail/context';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyDivider from '@fastgpt/web/components/common/MyDivider';
|
||||
import { getFileIcon } from '@fastgpt/global/common/file/icon';
|
||||
import MyAvatar from '@fastgpt/web/components/common/Avatar';
|
||||
import IconButton from '@/pageComponents/account/team/OrgManage/IconButton';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
|
||||
const FileSelectRender = ({ item, nodeId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const onChangeNode = useContextSelector(WorkflowActionsContext, (v) => v.onChangeNode);
|
||||
|
||||
const [urlInput, setUrlInput] = useState('');
|
||||
const values = useMemo(() => {
|
||||
if (Array.isArray(item.value)) {
|
||||
return item.value;
|
||||
}
|
||||
return [];
|
||||
}, [item.value]);
|
||||
const maxSelectFiles = item.maxFiles || 10;
|
||||
const isMaxSelected = values.length >= maxSelectFiles;
|
||||
|
||||
const handleAddUrl = useCallback(
|
||||
(value: string) => {
|
||||
if (!value.trim()) return;
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: [value.trim(), ...values]
|
||||
}
|
||||
});
|
||||
setUrlInput('');
|
||||
},
|
||||
[item, nodeId, onChangeNode, values]
|
||||
);
|
||||
const handleDeleteUrl = useCallback(
|
||||
(index: number) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: values.filter((_, i) => i !== index)
|
||||
}
|
||||
});
|
||||
},
|
||||
[item, nodeId, onChangeNode, values]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box w={'500px'}>
|
||||
<Box w={'100%'}>
|
||||
<InputGroup display={'flex'} alignItems={'center'}>
|
||||
<MyIcon
|
||||
position={'absolute'}
|
||||
left={2.5}
|
||||
name="common/addLight"
|
||||
w={'1.2rem'}
|
||||
color={'primary.600'}
|
||||
zIndex={10}
|
||||
/>
|
||||
<Input
|
||||
isDisabled={isMaxSelected}
|
||||
value={urlInput}
|
||||
onChange={(e) => setUrlInput(e.target.value)}
|
||||
onBlur={(e) => handleAddUrl(e.target.value)}
|
||||
border={'1.5px dashed'}
|
||||
borderColor={'myGray.250'}
|
||||
borderRadius={'md'}
|
||||
pl={8}
|
||||
py={1.5}
|
||||
placeholder={
|
||||
isMaxSelected ? t('file:reached_max_file_count') : t('chat:click_to_add_url')
|
||||
}
|
||||
/>
|
||||
</InputGroup>
|
||||
</Box>
|
||||
{/* Render */}
|
||||
{values.length > 0 && (
|
||||
<>
|
||||
<MyDivider />
|
||||
<VStack>
|
||||
{values.map((url, index) => {
|
||||
const fileIcon = getFileIcon(url, 'common/link');
|
||||
return (
|
||||
<Box key={index} w={'full'}>
|
||||
<HStack py={2} px={3} bg={'white'} borderRadius={'md'} border={'sm'}>
|
||||
<MyAvatar src={fileIcon} w={'1.2rem'} />
|
||||
<Box fontSize={'sm'} flex={'1 0 0'} title={url} className="textEllipsis">
|
||||
{url}
|
||||
</Box>
|
||||
{/* Status icon */}
|
||||
<MyIconButton
|
||||
icon={'close'}
|
||||
onClick={() => handleDeleteUrl(index)}
|
||||
hoverColor="red.600"
|
||||
hoverBg="red.50"
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</VStack>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FileSelectRender);
|
||||
|
|
@ -26,7 +26,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controlle
|
|||
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getTimezoneCodeFromStr } from '@fastgpt/global/common/time/timezone';
|
||||
import { getLocationFromIp } from '@fastgpt/service/common/geo';
|
||||
import type { I18nName } from '@fastgpt/service/common/geo/type';
|
||||
import { getLocale } from '@fastgpt/service/common/middle/i18n';
|
||||
|
||||
const formatJsonString = (data: any) => {
|
||||
if (data == null) return '';
|
||||
|
|
@ -40,7 +40,6 @@ export type ExportChatLogsBody = GetAppChatLogsProps & {
|
|||
title: string;
|
||||
sourcesMap: Record<string, { label: string }>;
|
||||
logKeys: AppLogKeysEnum[];
|
||||
locale?: keyof I18nName;
|
||||
};
|
||||
|
||||
async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextApiResponse) {
|
||||
|
|
@ -51,7 +50,6 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
|
|||
sources,
|
||||
tmbIds,
|
||||
chatSearch,
|
||||
locale = 'en',
|
||||
title,
|
||||
sourcesMap,
|
||||
logKeys = []
|
||||
|
|
@ -61,6 +59,7 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
|
|||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
const locale = getLocale(req);
|
||||
const timezoneCode = getTimezoneCodeFromStr(dateStart);
|
||||
|
||||
const { teamId, tmbId, app } = await authApp({
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { NextApiResponse } from 'next';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { type AppLogsListItemType } from '@/types/app';
|
||||
import { Types } from '@fastgpt/service/common/mongo';
|
||||
import { addDays } from 'date-fns';
|
||||
import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import {
|
||||
|
|
@ -19,12 +18,13 @@ import { getLocationFromIp } from '@fastgpt/service/common/geo';
|
|||
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { getLocale } from '@fastgpt/service/common/middle/i18n';
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<GetAppChatLogsParams>,
|
||||
_res: NextApiResponse
|
||||
): Promise<PaginationResponse<AppLogsListItemType>> {
|
||||
const { appId, dateStart, dateEnd, sources, tmbIds, chatSearch, locale = 'en' } = req.body;
|
||||
const { appId, dateStart, dateEnd, sources, tmbIds, chatSearch } = req.body;
|
||||
|
||||
const { pageSize = 20, offset } = parsePaginationRequest(req);
|
||||
|
||||
|
|
@ -294,7 +294,7 @@ async function handler(
|
|||
|
||||
const listWithRegion = list.map((item) => {
|
||||
const ip = item.region;
|
||||
const region = getLocationFromIp(ip, locale);
|
||||
const region = getLocationFromIp(ip, getLocale(req));
|
||||
|
||||
return {
|
||||
...item,
|
||||
|
|
|
|||
Loading…
Reference in New Issue