diff --git a/document/content/docs/upgrading/4-14/4144.mdx b/document/content/docs/upgrading/4-14/4144.mdx index 18614247d..ef43a969e 100644 --- a/document/content/docs/upgrading/4-14/4144.mdx +++ b/document/content/docs/upgrading/4-14/4144.mdx @@ -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. 日志导出时,结束时间会多出一天。 ## 插件 diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index 6b6a2b050..2154ec9e3 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -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" -} \ No newline at end of file +} diff --git a/packages/global/common/i18n/utils.ts b/packages/global/common/i18n/utils.ts index 16a65c528..d8b157581 100644 --- a/packages/global/common/i18n/utils.ts +++ b/packages/global/common/i18n/utils.ts @@ -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'; +}; diff --git a/packages/global/core/workflow/runtime/type.d.ts b/packages/global/core/workflow/runtime/type.d.ts index bf6838bd7..483a94489 100644 --- a/packages/global/core/workflow/runtime/type.d.ts +++ b/packages/global/core/workflow/runtime/type.d.ts @@ -209,14 +209,15 @@ export type DispatchNodeResponseType = { headers?: Record; httpResult?: Record; - // plugin output + // Tool + toolInput?: Record; pluginOutput?: Record; 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; - // read files readFilesResult?: string; readFiles?: ReadFileNodeResponse; diff --git a/packages/service/common/geo/index.ts b/packages/service/common/geo/index.ts index c7107089b..9c815d854 100644 --- a/packages/service/common/geo/index.ts +++ b/packages/service/common/geo/index.ts @@ -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]; } } diff --git a/packages/service/core/workflow/dispatch/child/runTool.ts b/packages/service/core/workflow/dispatch/child/runTool.ts index 8eca18130..ec805a77e 100644 --- a/packages/service/core/workflow/dispatch/child/runTool.ts +++ b/packages/service/core/workflow/dispatch/child/runTool.ts @@ -54,6 +54,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise = {}; try { // run system tool @@ -78,10 +79,11 @@ export const dispatchRunTool = async (props: RunToolProps): Promise 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 item.url); } return { @@ -172,6 +178,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise { diff --git a/packages/service/core/workflow/dispatch/plugin/runInput.ts b/packages/service/core/workflow/dispatch/plugin/runInput.ts index 5ecce849a..d5f693742 100644 --- a/packages/service/core/workflow/dispatch/plugin/runInput.ts +++ b/packages/service/core/workflow/dispatch/plugin/runInput.ts @@ -56,6 +56,8 @@ export const dispatchPluginInput = async ( return { data: { ...params, + + // 旧版本适配 [NodeOutputKeyEnum.userFiles]: files .map((item) => { return item?.url ?? ''; diff --git a/packages/web/components/core/workflow/NodeInputSelect.tsx b/packages/web/components/core/workflow/NodeInputSelect.tsx index 44accd481..4f3fcbb1e 100644 --- a/packages/web/components/core/workflow/NodeInputSelect.tsx +++ b/packages/web/components/core/workflow/NodeInputSelect.tsx @@ -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( diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index 17b5698db..0de82f169 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -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", diff --git a/packages/web/i18n/en/chat.json b/packages/web/i18n/en/chat.json index 4cbc5b08a..df1c6109a 100644 --- a/packages/web/i18n/en/chat.json +++ b/packages/web/i18n/en/chat.json @@ -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", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index a404eb816..6b4bf6e28 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -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)", diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index f58186211..db1ccd15f 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -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": "文件链接", diff --git a/packages/web/i18n/zh-CN/chat.json b/packages/web/i18n/zh-CN/chat.json index 23ed352f7..efb8baeba 100644 --- a/packages/web/i18n/zh-CN/chat.json +++ b/packages/web/i18n/zh-CN/chat.json @@ -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": "外部变量在免登录链接中不可见", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 3785e5a83..de1bf252f 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -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": "变量更新结果(按顺序展示多个变量更新结果)", diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index c8ac07101..a5400e276 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -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": "檔案連結", diff --git a/packages/web/i18n/zh-Hant/chat.json b/packages/web/i18n/zh-Hant/chat.json index 5abb70e67..3c09c3834 100644 --- a/packages/web/i18n/zh-Hant/chat.json +++ b/packages/web/i18n/zh-Hant/chat.json @@ -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": "外部變量在免登錄鏈接中不可見", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index 36d6938ce..757448ffe 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -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": "變數更新結果(依序顯示多個變數更新結果)", diff --git a/projects/app/src/components/core/app/FileSelector/index.tsx b/projects/app/src/components/core/app/FileSelector/index.tsx index d550e99d4..5e73e7569 100644 --- a/projects/app/src/components/core/app/FileSelector/index.tsx +++ b/projects/app/src/components/core/app/FileSelector/index.tsx @@ -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 = ({ {file?.error && ( - {file?.error} + {t(file.error)} )} diff --git a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx index fd067d98a..d5d2d499c 100644 --- a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx +++ b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx @@ -350,10 +350,8 @@ export const WholeResponseContent = ({ {/* plugin */} <> - + + {/* text output */} diff --git a/projects/app/src/global/core/api/appReq.d.ts b/projects/app/src/global/core/api/appReq.d.ts index 6fe202f19..4b5d53142 100644 --- a/projects/app/src/global/core/api/appReq.d.ts +++ b/projects/app/src/global/core/api/appReq.d.ts @@ -10,7 +10,6 @@ export type GetAppChatLogsProps = { sources?: ChatSourceEnum[]; tmbIds?: string[]; chatSearch?: string; - locale?: keyof I18nName; }; export type GetAppChatLogsParams = PaginationProps; diff --git a/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx b/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx index 88e99ba73..a581a81bb 100644 --- a/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx +++ b/projects/app/src/pageComponents/app/detail/Logs/LogTable.tsx @@ -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(); + 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 ] ); diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx index 305b4890c..27900b45b 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx @@ -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: ( diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/FileSelect.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/FileSelect.tsx new file mode 100644 index 000000000..1326d5a75 --- /dev/null +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/FileSelect.tsx @@ -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 ( + + + + + 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') + } + /> + + + {/* Render */} + {values.length > 0 && ( + <> + + + {values.map((url, index) => { + const fileIcon = getFileIcon(url, 'common/link'); + return ( + + + + + {url} + + {/* Status icon */} + handleDeleteUrl(index)} + hoverColor="red.600" + hoverBg="red.50" + /> + + + ); + })} + + + )} + + ); +}; + +export default React.memo(FileSelectRender); diff --git a/projects/app/src/pages/api/core/app/exportChatLogs.ts b/projects/app/src/pages/api/core/app/exportChatLogs.ts index d75518fe3..6f1b92c33 100644 --- a/projects/app/src/pages/api/core/app/exportChatLogs.ts +++ b/projects/app/src/pages/api/core/app/exportChatLogs.ts @@ -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; logKeys: AppLogKeysEnum[]; - locale?: keyof I18nName; }; async function handler(req: ApiRequestProps, res: NextApiResponse) { @@ -51,7 +50,6 @@ async function handler(req: ApiRequestProps, res: NextAp sources, tmbIds, chatSearch, - locale = 'en', title, sourcesMap, logKeys = [] @@ -61,6 +59,7 @@ async function handler(req: ApiRequestProps, res: NextAp throw new Error('缺少参数'); } + const locale = getLocale(req); const timezoneCode = getTimezoneCodeFromStr(dateStart); const { teamId, tmbId, app } = await authApp({ diff --git a/projects/app/src/pages/api/core/app/getChatLogs.ts b/projects/app/src/pages/api/core/app/getChatLogs.ts index 45047dbe2..1eecbd211 100644 --- a/projects/app/src/pages/api/core/app/getChatLogs.ts +++ b/projects/app/src/pages/api/core/app/getChatLogs.ts @@ -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, _res: NextApiResponse ): Promise> { - 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,