From 8369e59b4d3e0310fac0515cc3968a4b1ea643e8 Mon Sep 17 00:00:00 2001 From: crazywoola <100913391+crazywoola@users.noreply.github.com> Date: Thu, 6 Mar 2025 17:38:50 +0800 Subject: [PATCH 01/17] Feat/14573 support more types in form (#15093) --- .../date-picker/index.tsx | 2 +- .../time-picker/index.tsx | 2 +- .../components/base/markdown-blocks/form.tsx | 110 ++++++++++++++++-- web/app/styles/markdown.scss | 7 +- 4 files changed, 107 insertions(+), 14 deletions(-) diff --git a/web/app/components/base/date-and-time-picker/date-picker/index.tsx b/web/app/components/base/date-and-time-picker/date-picker/index.tsx index 67dbec3b90..82f6500f1d 100644 --- a/web/app/components/base/date-and-time-picker/date-picker/index.tsx +++ b/web/app/components/base/date-and-time-picker/date-picker/index.tsx @@ -234,7 +234,7 @@ const DatePicker = ({ )} - +
{/* Header */} {view === ViewType.date ? ( diff --git a/web/app/components/base/date-and-time-picker/time-picker/index.tsx b/web/app/components/base/date-and-time-picker/time-picker/index.tsx index 1fecda00b7..9a15888789 100644 --- a/web/app/components/base/date-and-time-picker/time-picker/index.tsx +++ b/web/app/components/base/date-and-time-picker/time-picker/index.tsx @@ -142,7 +142,7 @@ const TimePicker = ({
)} - +
{/* Header */}
diff --git a/web/app/components/base/markdown-blocks/form.tsx b/web/app/components/base/markdown-blocks/form.tsx index 7ce3e82b1d..89b9d76dc8 100644 --- a/web/app/components/base/markdown-blocks/form.tsx +++ b/web/app/components/base/markdown-blocks/form.tsx @@ -2,6 +2,10 @@ import React, { useEffect, useState } from 'react' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' +import DatePicker from '@/app/components/base/date-and-time-picker/date-picker' +import TimePicker from '@/app/components/base/date-and-time-picker/time-picker' +import Checkbox from '@/app/components/base/checkbox' +import Select from '@/app/components/base/select' import { useChatContext } from '@/app/components/base/chat/chat/context' enum DATA_FORMAT { @@ -19,18 +23,13 @@ enum SUPPORTED_TYPES { PASSWORD = 'password', EMAIL = 'email', NUMBER = 'number', + DATE = 'date', + TIME = 'time', + DATETIME = 'datetime', + CHECKBOX = 'checkbox', + SELECT = 'select', } const MarkdownForm = ({ node }: any) => { - // const supportedTypes = ['text', 'password', 'email', 'number'] - //
- // - // - // - // - // - // - // - //
const { onSend } = useChatContext() const [formValues, setFormValues] = useState<{ [key: string]: any }>({}) @@ -90,6 +89,97 @@ const MarkdownForm = ({ node }: any) => { ) } if (child.tagName === SUPPORTED_TAGS.INPUT && Object.values(SUPPORTED_TYPES).includes(child.properties.type)) { + if (child.properties.type === SUPPORTED_TYPES.DATE || child.properties.type === SUPPORTED_TYPES.DATETIME) { + return ( + { + setFormValues(prevValues => ({ + ...prevValues, + [child.properties.name]: date, + })) + }} + onClear={() => { + setFormValues(prevValues => ({ + ...prevValues, + [child.properties.name]: undefined, + })) + }} + /> + ) + } + if (child.properties.type === SUPPORTED_TYPES.TIME) { + return ( + { + setFormValues(prevValues => ({ + ...prevValues, + [child.properties.name]: time, + })) + }} + onClear={() => { + setFormValues(prevValues => ({ + ...prevValues, + [child.properties.name]: undefined, + })) + }} + /> + ) + } + if (child.properties.type === SUPPORTED_TYPES.CHECKBOX) { + return ( +
+ { + setFormValues(prevValues => ({ + ...prevValues, + [child.properties.name]: !prevValues[child.properties.name], + })) + }} + /> + {child.properties.dataTip || child.properties['data-tip'] || ''} +
+ ) + } + if (child.properties.type === SUPPORTED_TYPES.SELECT) { + return ( + :first-child { margin-top: 0; } @@ -1035,4 +1038,4 @@ .markdown-body .react-syntax-highlighter-line-number { color: var(--color-text-quaternary); -} \ No newline at end of file +} From d57470660070b09abaa8fe507bb072ac90150225 Mon Sep 17 00:00:00 2001 From: w4-jinhyeonkim <131935801+w4-jinhyeonkim@users.noreply.github.com> Date: Thu, 6 Mar 2025 18:40:37 +0900 Subject: [PATCH 02/17] Update ko-KR/plugin.ts (#15103) --- web/i18n/ko-KR/plugin.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/i18n/ko-KR/plugin.ts b/web/i18n/ko-KR/plugin.ts index 5cdd427d28..06445f3fb7 100644 --- a/web/i18n/ko-KR/plugin.ts +++ b/web/i18n/ko-KR/plugin.ts @@ -37,11 +37,11 @@ const translation = { }, operation: { detail: '세부 정보', - install: '설치하다', + install: '설치', viewDetail: '자세히보기', info: '플러그인 정보', update: '업데이트', - remove: '제거하다', + remove: '제거', checkUpdate: '업데이트 확인', }, toolSelector: { @@ -178,7 +178,7 @@ const translation = { pluginsResult: '{{num}} 결과', discover: '발견하다', moreFrom: 'Marketplace에서 더 보기', - sortBy: '블랙 시티', + sortBy: '정렬', and: '그리고', }, task: { From d84fa4d15498f70828153323b80d7e088b7712df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Thu, 6 Mar 2025 18:21:40 +0800 Subject: [PATCH 03/17] fix: with file conversation second chat raise error (#15097) --- web/app/components/base/chat/chat/utils.ts | 28 ++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/web/app/components/base/chat/chat/utils.ts b/web/app/components/base/chat/chat/utils.ts index 34d00afe33..69bc680777 100644 --- a/web/app/components/base/chat/chat/utils.ts +++ b/web/app/components/base/chat/chat/utils.ts @@ -17,15 +17,35 @@ export const processOpeningStatement = (openingStatement: string, inputs: Record }) } +export const processInputFileFromServer = (fileItem: Record) => { + return { + type: fileItem.type, + transfer_method: fileItem.transfer_method, + url: fileItem.remote_url, + upload_file_id: fileItem.related_id, + } +} + export const getProcessedInputs = (inputs: Record, inputsForm: InputForm[]) => { const processedInputs = { ...inputs } inputsForm.forEach((item) => { - if (item.type === InputVarType.multiFiles && inputs[item.variable]) - processedInputs[item.variable] = getProcessedFiles(inputs[item.variable]) + const inputValue = inputs[item.variable] + if (!inputValue) + return - if (item.type === InputVarType.singleFile && inputs[item.variable]) - processedInputs[item.variable] = getProcessedFiles([inputs[item.variable]])[0] + if (item.type === InputVarType.singleFile) { + if ('transfer_method' in inputValue) + processedInputs[item.variable] = processInputFileFromServer(inputValue) + else + processedInputs[item.variable] = getProcessedFiles([inputValue])[0] + } + else if (item.type === InputVarType.multiFiles) { + if ('transfer_method' in inputValue[0]) + processedInputs[item.variable] = inputValue.map(processInputFileFromServer) + else + processedInputs[item.variable] = getProcessedFiles(inputValue) + } }) return processedInputs From cd7cb19aee6b5ae91d6a8563b976a480202b00a8 Mon Sep 17 00:00:00 2001 From: Jimmiaxie Date: Thu, 6 Mar 2025 18:55:25 +0800 Subject: [PATCH 04/17] hotfix: Fixed tags not updating in real time in the label management of apps #15113 (#15110) --- web/app/components/base/tag-management/selector.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/app/components/base/tag-management/selector.tsx b/web/app/components/base/tag-management/selector.tsx index 01e9eb162f..4fdb2c2d8d 100644 --- a/web/app/components/base/tag-management/selector.tsx +++ b/web/app/components/base/tag-management/selector.tsx @@ -16,7 +16,7 @@ import Checkbox from '@/app/components/base/checkbox' import { bindTag, createTag, fetchTagList, unBindTag } from '@/service/tag' import { ToastContext } from '@/app/components/base/toast' -interface TagSelectorProps { +type TagSelectorProps = { targetID: string isPopover?: boolean position?: 'bl' | 'br' @@ -215,6 +215,7 @@ const TagSelector: FC = ({ }) => { const { t } = useTranslation() + const tagList = useTagStore(s => s.tagList) const setTagList = useTagStore(s => s.setTagList) const getTagList = async () => { @@ -224,9 +225,9 @@ const TagSelector: FC = ({ const triggerContent = useMemo(() => { if (selectedTags?.length) - return selectedTags.map(tag => tag.name).join(', ') + return selectedTags.filter(selectedTag => tagList.find(tag => tag.id === selectedTag.id)).map(tag => tag.name).join(', ') return '' - }, [selectedTags]) + }, [selectedTags, tagList]) const Trigger = () => { return ( From 9c1db7dca7e5a0232c3fa0a4e6333d63100bff89 Mon Sep 17 00:00:00 2001 From: engchina <12236799+engchina@users.noreply.github.com> Date: Thu, 6 Mar 2025 18:58:51 +0800 Subject: [PATCH 05/17] modify oracle lexer name Fixes #15106 (#15108) Co-authored-by: engchina --- api/core/rag/datasource/vdb/oracle/oraclevector.py | 2 +- docker/startupscripts/init_user.script | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/rag/datasource/vdb/oracle/oraclevector.py b/api/core/rag/datasource/vdb/oracle/oraclevector.py index 8262c219b4..e5ca4492c6 100644 --- a/api/core/rag/datasource/vdb/oracle/oraclevector.py +++ b/api/core/rag/datasource/vdb/oracle/oraclevector.py @@ -61,7 +61,7 @@ CREATE TABLE IF NOT EXISTS {table_name} ( SQL_CREATE_INDEX = """ CREATE INDEX IF NOT EXISTS idx_docs_{table_name} ON {table_name}(text) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS -('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER multilingual_lexer') +('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER world_lexer') """ diff --git a/docker/startupscripts/init_user.script b/docker/startupscripts/init_user.script index 55e8510d2f..0c5bff1ef6 100755 --- a/docker/startupscripts/init_user.script +++ b/docker/startupscripts/init_user.script @@ -5,6 +5,6 @@ create user dify identified by dify DEFAULT TABLESPACE users quota unlimited on grant DB_DEVELOPER_ROLE to dify; BEGIN -CTX_DDL.CREATE_PREFERENCE('dify.multilingual_lexer','CHINESE_VGRAM_LEXER'); +CTX_DDL.CREATE_PREFERENCE('dify.world_lexer','WORLD_LEXER'); END; / From 2a6e522a87d19225d8694b43292c51da1d3c68a5 Mon Sep 17 00:00:00 2001 From: Jimmiaxie Date: Thu, 6 Mar 2025 20:14:39 +0800 Subject: [PATCH 06/17] Fixed incorrect use of key in the page /plugins?category=discover #15126 (#15127) --- .../plugins/marketplace/list/list-with-collection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/plugins/marketplace/list/list-with-collection.tsx b/web/app/components/plugins/marketplace/list/list-with-collection.tsx index aed84d77dd..daaf2d9647 100644 --- a/web/app/components/plugins/marketplace/list/list-with-collection.tsx +++ b/web/app/components/plugins/marketplace/list/list-with-collection.tsx @@ -65,7 +65,7 @@ const ListWithCollection = ({ return ( Date: Thu, 6 Mar 2025 22:32:38 +0800 Subject: [PATCH 07/17] fix parent-child retrival count (#15119) --- .../index_tool_callback_handler.py | 32 ++++++++++++----- api/core/rag/retrieval/dataset_retrieval.py | 34 ++++++++++++++----- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/api/core/callback_handler/index_tool_callback_handler.py b/api/core/callback_handler/index_tool_callback_handler.py index 8f8aaa93d6..64c734f626 100644 --- a/api/core/callback_handler/index_tool_callback_handler.py +++ b/api/core/callback_handler/index_tool_callback_handler.py @@ -1,9 +1,11 @@ from core.app.apps.base_app_queue_manager import AppQueueManager, PublishFrom from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.queue_entities import QueueRetrieverResourcesEvent +from core.rag.index_processor.constant.index_type import IndexType from core.rag.models.document import Document from extensions.ext_database import db -from models.dataset import DatasetQuery, DocumentSegment +from models.dataset import ChildChunk, DatasetQuery, DocumentSegment +from models.dataset import Document as DatasetDocument from models.model import DatasetRetrieverResource @@ -41,15 +43,29 @@ class DatasetIndexToolCallbackHandler: """Handle tool end.""" for document in documents: if document.metadata is not None: - query = db.session.query(DocumentSegment).filter( - DocumentSegment.index_node_id == document.metadata["doc_id"] - ) + dataset_document = DatasetDocument.query.filter( + DatasetDocument.id == document.metadata["document_id"] + ).first() + if dataset_document.doc_form == IndexType.PARENT_CHILD_INDEX: + child_chunk = ChildChunk.query.filter( + ChildChunk.index_node_id == document.metadata["doc_id"], + ChildChunk.dataset_id == dataset_document.dataset_id, + ChildChunk.document_id == dataset_document.id, + ).first() + if child_chunk: + segment = DocumentSegment.query.filter(DocumentSegment.id == child_chunk.segment_id).update( + {DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False + ) + else: + query = db.session.query(DocumentSegment).filter( + DocumentSegment.index_node_id == document.metadata["doc_id"] + ) - if "dataset_id" in document.metadata: - query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) + if "dataset_id" in document.metadata: + query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) - # add hit count to document segment - query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) + # add hit count to document segment + query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) db.session.commit() diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py index ac868a2250..c5ac63e853 100644 --- a/api/core/rag/retrieval/dataset_retrieval.py +++ b/api/core/rag/retrieval/dataset_retrieval.py @@ -21,6 +21,7 @@ from core.rag.data_post_processor.data_post_processor import DataPostProcessor from core.rag.datasource.keyword.jieba.jieba_keyword_table_handler import JiebaKeywordTableHandler from core.rag.datasource.retrieval_service import RetrievalService from core.rag.entities.context_entities import DocumentContext +from core.rag.index_processor.constant.index_type import IndexType from core.rag.models.document import Document from core.rag.rerank.rerank_type import RerankMode from core.rag.retrieval.retrieval_methods import RetrievalMethod @@ -28,7 +29,7 @@ from core.rag.retrieval.router.multi_dataset_function_call_router import Functio from core.rag.retrieval.router.multi_dataset_react_route import ReactMultiDatasetRouter from core.tools.utils.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool from extensions.ext_database import db -from models.dataset import Dataset, DatasetQuery, DocumentSegment +from models.dataset import ChildChunk, Dataset, DatasetQuery, DocumentSegment from models.dataset import Document as DatasetDocument from services.external_knowledge_service import ExternalDatasetService @@ -429,16 +430,31 @@ class DatasetRetrieval: dify_documents = [document for document in documents if document.provider == "dify"] for document in dify_documents: if document.metadata is not None: - query = db.session.query(DocumentSegment).filter( - DocumentSegment.index_node_id == document.metadata["doc_id"] - ) + dataset_document = DatasetDocument.query.filter( + DatasetDocument.id == document.metadata["document_id"] + ).first() + if dataset_document.doc_form == IndexType.PARENT_CHILD_INDEX: + child_chunk = ChildChunk.query.filter( + ChildChunk.index_node_id == document.metadata["doc_id"], + ChildChunk.dataset_id == dataset_document.dataset_id, + ChildChunk.document_id == dataset_document.id, + ).first() + if child_chunk: + segment = DocumentSegment.query.filter(DocumentSegment.id == child_chunk.segment_id).update( + {DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False + ) + db.session.commit() + else: + query = db.session.query(DocumentSegment).filter( + DocumentSegment.index_node_id == document.metadata["doc_id"] + ) - # if 'dataset_id' in document.metadata: - if "dataset_id" in document.metadata: - query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) + # if 'dataset_id' in document.metadata: + if "dataset_id" in document.metadata: + query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) - # add hit count to document segment - query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) + # add hit count to document segment + query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) db.session.commit() From 9437a1a8443bbd2883d3392b20434fa6106549d3 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 6 Mar 2025 22:36:05 +0800 Subject: [PATCH 08/17] docs: add comprehensive Traditional Chinese contribution guide (#14816) Signed-off-by: Bo-Yi Wu --- CONTRIBUTING_TW.md | 153 +++++++++++++++++++++++++++ README_TW.md | 258 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 411 insertions(+) create mode 100644 CONTRIBUTING_TW.md create mode 100644 README_TW.md diff --git a/CONTRIBUTING_TW.md b/CONTRIBUTING_TW.md new file mode 100644 index 0000000000..678ea1150a --- /dev/null +++ b/CONTRIBUTING_TW.md @@ -0,0 +1,153 @@ +# 貢獻指南 + +您想為 Dify 做出貢獻 - 這太棒了,我們迫不及待地想看看您的成果。作為一家人力和資金有限的初創公司,我們有宏大的抱負,希望設計出最直觀的工作流程來構建和管理 LLM 應用程式。來自社群的任何幫助都非常珍貴,真的。 + +鑑於我們的現狀,我們需要靈活且快速地發展,但同時也希望確保像您這樣的貢獻者能夠獲得盡可能順暢的貢獻體驗。我們編寫了這份貢獻指南,目的是幫助您熟悉代碼庫以及我們如何與貢獻者合作,讓您可以更快地進入有趣的部分。 + +這份指南,就像 Dify 本身一樣,是不斷發展的。如果有時它落後於實際項目,我們非常感謝您的理解,也歡迎任何改進的反饋。 + +關於授權,請花一分鐘閱讀我們簡短的[授權和貢獻者協議](./LICENSE)。社群也遵守[行為準則](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。 + +## 在開始之前 + +[尋找](https://github.com/langgenius/dify/issues?q=is:issue+is:open)現有的 issue,或[創建](https://github.com/langgenius/dify/issues/new/choose)一個新的。我們將 issues 分為 2 種類型: + +### 功能請求 + +- 如果您要開啟新的功能請求,我們希望您能解釋所提議的功能要達成什麼目標,並且盡可能包含更多的相關背景資訊。[@perzeusss](https://github.com/perzeuss) 已經製作了一個實用的[功能請求輔助工具](https://udify.app/chat/MK2kVSnw1gakVwMX),能幫助您草擬您的需求。歡迎試用。 + +- 如果您想從現有問題中選擇一個來處理,只需在其下方留言表示即可。 + + 相關方向的團隊成員會加入討論。如果一切順利,他們會同意您開始編寫代碼。我們要求您在得到許可前先不要開始處理該功能,以免我們提出變更時您的工作成果被浪費。 + + 根據所提議功能的領域不同,您可能會與不同的團隊成員討論。以下是目前每位團隊成員所負責的領域概述: + + | 成員 | 負責領域 | + | --------------------------------------------------------------------------------------- | ------------------------------ | + | [@yeuoly](https://github.com/Yeuoly) | 設計 Agents 架構 | + | [@jyong](https://github.com/JohnJyong) | RAG 管道設計 | + | [@GarfieldDai](https://github.com/GarfieldDai) | 建構工作流程編排 | + | [@iamjoel](https://github.com/iamjoel) & [@zxhlyh](https://github.com/zxhlyh) | 打造易用的前端界面 | + | [@guchenhe](https://github.com/guchenhe) & [@crazywoola](https://github.com/crazywoola) | 開發者體驗,各類問題的聯絡窗口 | + | [@takatost](https://github.com/takatost) | 整體產品方向與架構 | + + 我們如何排定優先順序: + + | 功能類型 | 優先級 | + | ------------------------------------------------------------------------------------------------------- | -------- | + | 被團隊成員標記為高優先級的功能 | 高優先級 | + | 來自我們[社群回饋版](https://github.com/langgenius/dify/discussions/categories/feedbacks)的熱門功能請求 | 中優先級 | + | 非核心功能和次要增強 | 低優先級 | + | 有價值但非急迫的功能 | 未來功能 | + +### 其他事項 (例如錯誤回報、效能優化、錯字更正) + +- 可以直接開始編寫程式碼。 + + 我們如何排定優先順序: + + | 問題類型 | 優先級 | + | ----------------------------------------------------- | -------- | + | 核心功能的錯誤 (無法登入、應用程式無法運行、安全漏洞) | 重要 | + | 非關鍵性錯誤、效能提升 | 中優先級 | + | 小修正 (錯字、令人困惑但仍可運作的使用者界面) | 低優先級 | + +## 安裝 + +以下是設置 Dify 開發環境的步驟: + +### 1. 分叉此存儲庫 + +### 2. 複製代碼庫 + +從您的終端機複製分叉的代碼庫: + +```shell +git clone git@github.com:/dify.git +``` + +- [Docker](https://www.docker.com/) +- [Docker Compose](https://docs.docker.com/compose/install/) +- [Node.js v18.x (LTS)](http://nodejs.org) +- [pnpm](https://pnpm.io/) +- [Python](https://www.python.org/) version 3.11.x or 3.12.x + +### 4. 安裝 + +Dify 由後端和前端組成。透過 `cd api/` 導航至後端目錄,然後按照[後端 README](api/README.md)進行安裝。在另一個終端機視窗中,透過 `cd web/` 導航至前端目錄,然後按照[前端 README](web/README.md)進行安裝。 + +查閱[安裝常見問題](https://docs.dify.ai/learn-more/faq/install-faq)了解常見問題和故障排除步驟的列表。 + +### 5. 在瀏覽器中訪問 Dify + +要驗證您的設置,請在瀏覽器中訪問 [http://localhost:3000](http://localhost:3000)(預設值,或您自行設定的 URL 和埠號)。現在您應該能看到 Dify 已啟動並運行。 + +## 開發 + +如果您要添加模型提供者,請參考[此指南](https://github.com/langgenius/dify/blob/main/api/core/model_runtime/README.md)。 + +如果您要為 Agent 或工作流程添加工具提供者,請參考[此指南](./api/core/tools/README.md)。 + +為了幫助您快速找到您的貢獻適合的位置,以下是 Dify 後端和前端的簡要註解大綱: + +### 後端 + +Dify 的後端使用 Python 的 [Flask](https://flask.palletsprojects.com/en/3.0.x/) 框架編寫。它使用 [SQLAlchemy](https://www.sqlalchemy.org/) 作為 ORM 工具,使用 [Celery](https://docs.celeryq.dev/en/stable/getting-started/introduction.html) 進行任務佇列處理。授權邏輯則透過 Flask-login 實現。 + +```text +[api/] +├── constants // 整個專案中使用的常數與設定值 +├── controllers // API 路由定義與請求處理邏輯 +├── core // 核心應用服務、模型整合與工具實現 +├── docker // Docker 容器化相關設定檔案 +├── events // 事件處理與流程管理機制 +├── extensions // 與第三方框架或平台的整合擴充功能 +├── fields // 資料序列化與結構定義欄位 +├── libs // 可重複使用的共用程式庫與輔助工具 +├── migrations // 資料庫結構變更與遷移腳本 +├── models // 資料庫模型與資料結構定義 +├── services // 核心業務邏輯與功能實現 +├── storage // 私鑰與敏感資訊儲存機制 +├── tasks // 非同步任務與背景作業處理器 +└── tests +``` + +### 前端 + +網站基於 [Next.js](https://nextjs.org/) 的 Typescript 樣板,並使用 [Tailwind CSS](https://tailwindcss.com/) 進行樣式設計。[React-i18next](https://react.i18next.com/) 用於國際化。 + +```text +[web/] +├── app // 頁面佈局與介面元件 +│ ├── (commonLayout) // 應用程式共用佈局結構 +│ ├── (shareLayout) // Token 會話專用共享佈局 +│ ├── activate // 帳號啟用頁面 +│ ├── components // 頁面與佈局共用元件 +│ ├── install // 系統安裝頁面 +│ ├── signin // 使用者登入頁面 +│ └── styles // 全域共用樣式定義 +├── assets // 靜態資源檔案庫 +├── bin // 建構流程執行腳本 +├── config // 系統可調整設定與選項 +├── context // 應用程式狀態共享上下文 +├── dictionaries // 多語系翻譯詞彙庫 +├── docker // Docker 容器設定檔 +├── hooks // 可重複使用的 React Hooks +├── i18n // 國際化與本地化設定 +├── models // 資料結構與 API 回應模型 +├── public // 靜態資源與網站圖標 +├── service // API 操作介面定義 +├── test // 測試用例與測試框架 +├── types // TypeScript 型別定義 +└── utils // 共用輔助功能函式庫 +``` + +## 提交您的 PR + +最後,是時候向我們的存儲庫開啟拉取請求(PR)了。對於主要功能,我們會先將它們合併到 `deploy/dev` 分支進行測試,然後才會進入 `main` 分支。如果您遇到合併衝突或不知道如何開啟拉取請求等問題,請查看 [GitHub 的拉取請求教學](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests)。 + +就是這樣!一旦您的 PR 被合併,您將作為貢獻者出現在我們的 [README](https://github.com/langgenius/dify/blob/main/README.md) 中。 + +## 獲取幫助 + +如果您在貢獻過程中遇到困難或有迫切的問題,只需通過相關的 GitHub issue 向我們提問,或加入我們的 [Discord](https://discord.gg/8Tpq4AcN9c) 進行快速交流。 diff --git a/README_TW.md b/README_TW.md new file mode 100644 index 0000000000..4bfc81a25e --- /dev/null +++ b/README_TW.md @@ -0,0 +1,258 @@ +![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) + +

+ 📌 介紹 Dify 工作流程檔案上傳功能:重現 Google NotebookLM Podcast +

+ +

+ Dify 雲端服務 · + 自行託管 · + 說明文件 · + 企業諮詢 +

+ +

+ + Static Badge + + Static Badge + + chat on Discord + + join Reddit + + follow on X(Twitter) + + follow on LinkedIn + + Docker Pulls + + Commits last month + + Issues closed + + Discussion posts +

+ +

+ README in English + 繁體中文文件 + 简体中文版自述文件 + 日本語のREADME + README en Español + README en Français + README tlhIngan Hol + README in Korean + README بالعربية + Türkçe README + README Tiếng Việt + README in Deutsch +

+ +Dify 是一個開源的 LLM 應用程式開發平台。其直觀的界面結合了智能代理工作流程、RAG 管道、代理功能、模型管理、可觀察性功能等,讓您能夠快速從原型進展到生產環境。 + +## 快速開始 + +> 安裝 Dify 之前,請確保您的機器符合以下最低系統要求: +> +> - CPU >= 2 核心 +> - 記憶體 >= 4 GiB + +
+ +啟動 Dify 伺服器最簡單的方式是透過 [docker compose](docker/docker-compose.yaml)。在使用以下命令運行 Dify 之前,請確保您的機器已安裝 [Docker](https://docs.docker.com/get-docker/) 和 [Docker Compose](https://docs.docker.com/compose/install/): + +```bash +cd dify +cd docker +cp .env.example .env +docker compose up -d +``` + +運行後,您可以在瀏覽器中通過 [http://localhost/install](http://localhost/install) 訪問 Dify 儀表板並開始初始化過程。 + +### 尋求幫助 + +如果您在設置 Dify 時遇到問題,請參考我們的 [常見問題](https://docs.dify.ai/getting-started/install-self-hosted/faqs)。如果仍有疑問,請聯絡 [社區和我們](#community--contact)。 + +> 如果您想為 Dify 做出貢獻或進行額外開發,請參考我們的 [從原始碼部署指南](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code) + +## 核心功能 + +**1. 工作流程**: +在視覺化畫布上建立和測試強大的 AI 工作流程,利用以下所有功能及更多。 + +https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa + +**2. 全面的模型支援**: +無縫整合來自數十個推理提供商和自託管解決方案的數百個專有/開源 LLM,涵蓋 GPT、Mistral、Llama3 和任何與 OpenAI API 兼容的模型。您可以在[此處](https://docs.dify.ai/getting-started/readme/model-providers)找到支援的模型提供商完整列表。 + +![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3) + +**3. 提示詞 IDE**: +直觀的界面,用於編寫提示詞、比較模型性能,以及為聊天型應用程式添加文字轉語音等額外功能。 + +**4. RAG 管道**: +廣泛的 RAG 功能,涵蓋從文件擷取到檢索的全部流程,內建支援從 PDF、PPT 和其他常見文件格式提取文本。 + +**5. 代理功能**: +您可以基於 LLM 函數調用或 ReAct 定義代理,並為代理添加預構建或自定義工具。Dify 為 AI 代理提供 50 多種內建工具,如 Google 搜尋、DALL·E、Stable Diffusion 和 WolframAlpha。 + +**6. LLMOps**: +監控並分析應用程式日誌和長期效能。您可以根據生產數據和標註持續改進提示詞、數據集和模型。 + +**7. 後端即服務**: +Dify 的所有功能都提供相應的 API,因此您可以輕鬆地將 Dify 整合到您自己的業務邏輯中。 + +## 功能比較 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
功能Dify.AILangChainFlowiseOpenAI Assistants API
程式設計方法API + 應用導向Python 代碼應用導向API 導向
支援的 LLM 模型豐富多樣豐富多樣豐富多樣僅限 OpenAI
RAG 引擎
代理功能
工作流程
可觀察性
企業級功能 (SSO/存取控制)
本地部署
+ +## 使用 Dify + +- **雲端服務
** + 我們提供 [Dify Cloud](https://dify.ai) 服務,任何人都可以零配置嘗試。它提供與自部署版本相同的所有功能,並在沙盒計劃中包含 200 次免費 GPT-4 調用。 + +- **自託管 Dify 社區版
** + 使用這份[快速指南](#快速開始)在您的環境中快速運行 Dify。 + 使用我們的[文檔](https://docs.dify.ai)獲取更多參考和深入指導。 + +- **企業/組織版 Dify
** + 我們提供額外的企業中心功能。[通過這個聊天機器人記錄您的問題](https://udify.app/chat/22L1zSxg6yW1cWQg)或[發送電子郵件給我們](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry)討論企業需求。
+ > 對於使用 AWS 的初創企業和小型企業,請查看 [AWS Marketplace 上的 Dify Premium](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6),並一鍵部署到您自己的 AWS VPC。這是一個經濟實惠的 AMI 產品,可選擇使用自定義徽標和品牌創建應用。 + +## 保持領先 + +在 GitHub 上為 Dify 加星,即時獲取新版本通知。 + +![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4) + +## 進階設定 + +如果您需要自定義配置,請參考我們的 [.env.example](docker/.env.example) 文件中的註釋,並在您的 `.env` 文件中更新相應的值。此外,根據您特定的部署環境和需求,您可能需要調整 `docker-compose.yaml` 文件本身,例如更改映像版本、端口映射或卷掛載。進行任何更改後,請重新運行 `docker-compose up -d`。您可以在[這裡](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用環境變數的完整列表。 + +如果您想配置高可用性設置,社區貢獻的 [Helm Charts](https://helm.sh/) 和 YAML 文件允許在 Kubernetes 上部署 Dify。 + +- [由 @LeoQuote 提供的 Helm Chart](https://github.com/douban/charts/tree/master/charts/dify) +- [由 @BorisPolonsky 提供的 Helm Chart](https://github.com/BorisPolonsky/dify-helm) +- [由 @Winson-030 提供的 YAML 文件](https://github.com/Winson-030/dify-kubernetes) + +### 使用 Terraform 進行部署 + +使用 [terraform](https://www.terraform.io/) 一鍵部署 Dify 到雲端平台 + +### Azure 全球 + +- [由 @nikawang 提供的 Azure Terraform](https://github.com/nikawang/dify-azure-terraform) + +### Google Cloud + +- [由 @sotazum 提供的 Google Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform) + +### 使用 AWS CDK 進行部署 + +使用 [CDK](https://aws.amazon.com/cdk/) 部署 Dify 到 AWS + +### AWS + +- [由 @KevinZhao 提供的 AWS CDK](https://github.com/aws-samples/solution-for-deploying-dify-on-aws) + +## 貢獻 + +對於想要貢獻程式碼的開發者,請參閱我們的[貢獻指南](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)。 +同時,也請考慮透過在社群媒體和各種活動與會議上分享 Dify 來支持我們。 + +> 我們正在尋找貢獻者協助將 Dify 翻譯成中文和英文以外的語言。如果您有興趣幫忙,請查看 [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) 獲取更多資訊,並在我們的 [Discord 社群伺服器](https://discord.gg/8Tpq4AcN9c) 的 `global-users` 頻道留言給我們。 + +## 社群與聯絡方式 + +- [Github Discussion](https://github.com/langgenius/dify/discussions):最適合分享反饋和提問。 +- [GitHub Issues](https://github.com/langgenius/dify/issues):最適合報告使用 Dify.AI 時遇到的問題和提出功能建議。請參閱我們的[貢獻指南](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)。 +- [Discord](https://discord.gg/FngNHpbcY7):最適合分享您的應用程式並與社群互動。 +- [X(Twitter)](https://twitter.com/dify_ai):最適合分享您的應用程式並與社群互動。 + +**貢獻者** + + + + + +## 星星歷史 + +[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date) + +## 安全揭露 + +為保護您的隱私,請避免在 GitHub 上發布安全性問題。請將您的問題發送至 security@dify.ai,我們將為您提供更詳細的答覆。 + +## 授權條款 + +本代碼庫採用 [Dify 開源授權](LICENSE),這基本上是 Apache 2.0 授權加上一些額外限制條款。 From b7583e95a5cf226b7450c50fc6b93be9573c298b Mon Sep 17 00:00:00 2001 From: Kalo Chin <91766386+fdb02983rhy@users.noreply.github.com> Date: Thu, 6 Mar 2025 23:36:59 +0900 Subject: [PATCH 09/17] fix: adjust scroll detection threshold in chat component (#14640) --- web/app/components/base/chat/chat/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/base/chat/chat/index.tsx b/web/app/components/base/chat/chat/index.tsx index f0e7d6626f..3745e03653 100644 --- a/web/app/components/base/chat/chat/index.tsx +++ b/web/app/components/base/chat/chat/index.tsx @@ -186,7 +186,7 @@ const Chat: FC = ({ if (chatContainer) { const setUserScrolled = () => { if (chatContainer) - userScrolledRef.current = chatContainer.scrollHeight - chatContainer.scrollTop >= chatContainer.clientHeight + 300 + userScrolledRef.current = chatContainer.scrollHeight - chatContainer.scrollTop > chatContainer.clientHeight } chatContainer.addEventListener('scroll', setUserScrolled) return () => chatContainer.removeEventListener('scroll', setUserScrolled) From 65da9425dff5b11db28ae18bef1a29143bb4fc05 Mon Sep 17 00:00:00 2001 From: Yeuoly <45712896+Yeuoly@users.noreply.github.com> Date: Fri, 7 Mar 2025 00:41:56 +0800 Subject: [PATCH 10/17] Fix: only retrieval plugin-compatible providers when provider_name starts with `langgenius` (#15133) --- api/core/entities/provider_configuration.py | 34 ++++++++++++++------- api/core/plugin/entities/plugin.py | 3 ++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/api/core/entities/provider_configuration.py b/api/core/entities/provider_configuration.py index b1a155fea8..4b8214019c 100644 --- a/api/core/entities/provider_configuration.py +++ b/api/core/entities/provider_configuration.py @@ -187,18 +187,30 @@ class ProviderConfiguration(BaseModel): :return: """ # get provider - provider_record = ( - db.session.query(Provider) - .filter( - Provider.tenant_id == self.tenant_id, - Provider.provider_type == ProviderType.CUSTOM.value, - or_( - Provider.provider_name == ModelProviderID(self.provider.provider).plugin_name, - Provider.provider_name == self.provider.provider, - ), + model_provider_id = ModelProviderID(self.provider.provider) + if model_provider_id.is_langgenius(): + provider_record = ( + db.session.query(Provider) + .filter( + Provider.tenant_id == self.tenant_id, + Provider.provider_type == ProviderType.CUSTOM.value, + or_( + Provider.provider_name == model_provider_id.provider_name, + Provider.provider_name == self.provider.provider, + ), + ) + .first() + ) + else: + provider_record = ( + db.session.query(Provider) + .filter( + Provider.tenant_id == self.tenant_id, + Provider.provider_type == ProviderType.CUSTOM.value, + Provider.provider_name == self.provider.provider, + ) + .first() ) - .first() - ) # Get provider credential secret variables provider_credential_secret_variables = self.extract_secret_variables( diff --git a/api/core/plugin/entities/plugin.py b/api/core/plugin/entities/plugin.py index c61e848a42..ad39b972bf 100644 --- a/api/core/plugin/entities/plugin.py +++ b/api/core/plugin/entities/plugin.py @@ -164,6 +164,9 @@ class GenericProviderID: self.organization, self.plugin_name, self.provider_name = value.split("/") self.is_hardcoded = is_hardcoded + def is_langgenius(self) -> bool: + return self.organization == "langgenius" + @property def plugin_id(self) -> str: return f"{self.organization}/{self.plugin_name}" From 69746f2f0bbc6316b9475192b5bada7cc152d35c Mon Sep 17 00:00:00 2001 From: "Moch. Ainun Najib" Date: Fri, 7 Mar 2025 09:55:08 +0700 Subject: [PATCH 11/17] add: allowed_domains marketplace.dify.ai (#15139) --- docker/ssrf_proxy/squid.conf.template | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/ssrf_proxy/squid.conf.template b/docker/ssrf_proxy/squid.conf.template index 676fe7379c..7e85e84a6f 100644 --- a/docker/ssrf_proxy/squid.conf.template +++ b/docker/ssrf_proxy/squid.conf.template @@ -19,6 +19,8 @@ acl Safe_ports port 488 # gss-http acl Safe_ports port 591 # filemaker acl Safe_ports port 777 # multiling http acl CONNECT method CONNECT +acl allowed_domains dstdomain .marketplace.dify.ai +http_access allow allowed_domains http_access deny !Safe_ports http_access deny CONNECT !SSL_ports http_access allow localhost manager From f588ccff720d40224abc75ca28325bba3e96db0c Mon Sep 17 00:00:00 2001 From: KVOJJJin Date: Fri, 7 Mar 2025 11:56:20 +0800 Subject: [PATCH 12/17] Feat: settings dark mode (#15184) --- .../components/billing/billing-page/index.tsx | 18 +- web/app/components/billing/plan/index.tsx | 127 ++++------- web/app/components/billing/pricing/index.tsx | 1 - .../components/billing/progress-bar/index.tsx | 8 +- .../components/billing/upgrade-btn/index.tsx | 27 +-- .../billing/usage-info/apps-info.tsx | 6 +- .../components/billing/usage-info/index.tsx | 55 ++--- .../billing/usage-info/vector-space-info.tsx | 6 +- .../components/custom/custom-page/index.tsx | 44 ++-- .../custom/custom-web-app-brand/index.tsx | 214 ++++++++++++------ .../datasets/create/website/base/field.tsx | 2 +- .../api-based-extension-page/empty.tsx | 18 +- .../api-based-extension-page/index.tsx | 10 +- .../api-based-extension-page/item.tsx | 25 +- .../api-based-extension-page/modal.tsx | 4 +- .../data-source-notion/operate/index.tsx | 42 ++-- .../config-firecrawl-modal.tsx | 16 +- .../config-jina-reader-modal.tsx | 16 +- .../data-source-website/index.tsx | 4 +- .../data-source-page/panel/config-item.tsx | 4 +- .../data-source-page/panel/index.tsx | 20 +- .../header/account-setting/index.tsx | 12 +- .../account-setting/members-page/index.tsx | 38 ++-- .../members-page/invite-modal/index.tsx | 17 +- .../invite-modal/role-selector.tsx | 40 ++-- .../invited-modal/index.module.css | 2 +- .../members-page/invited-modal/index.tsx | 12 +- .../invited-modal/invitation-link.tsx | 12 +- .../members-page/operation/index.module.css | 3 - .../members-page/operation/index.tsx | 54 ++--- web/i18n/en-US/billing.ts | 8 +- web/i18n/en-US/custom.ts | 2 + web/i18n/zh-Hans/billing.ts | 8 +- web/i18n/zh-Hans/custom.ts | 2 + 34 files changed, 450 insertions(+), 427 deletions(-) delete mode 100644 web/app/components/header/account-setting/members-page/operation/index.module.css diff --git a/web/app/components/billing/billing-page/index.tsx b/web/app/components/billing/billing-page/index.tsx index 41068d516e..d892811e5b 100644 --- a/web/app/components/billing/billing-page/index.tsx +++ b/web/app/components/billing/billing-page/index.tsx @@ -4,10 +4,10 @@ import React from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import { - RiExternalLinkLine, + RiArrowRightUpLine, } from '@remixicon/react' import PlanComp from '../plan' -import { ReceiptList } from '../../base/icons/src/vender/line/financeAndECommerce' +import Divider from '@/app/components/base/divider' import { fetchBillingUrl } from '@/service/billing' import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' @@ -25,13 +25,13 @@ const Billing: FC = () => {
{enableBilling && isCurrentWorkspaceManager && billingUrl && ( - -
- -
{t('billing.viewBilling')}
-
- -
+ <> + + + {t('billing.viewBilling')} + + + )}
) diff --git a/web/app/components/billing/plan/index.tsx b/web/app/components/billing/plan/index.tsx index 63493eefa7..034c999b9c 100644 --- a/web/app/components/billing/plan/index.tsx +++ b/web/app/components/billing/plan/index.tsx @@ -2,36 +2,21 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' +import { + RiBook2Line, + RiBox3Line, + RiFileEditLine, + RiGroup3Line, + RiGroupLine, + RiSquareLine, +} from '@remixicon/react' import { Plan, SelfHostedPlan } from '../type' import VectorSpaceInfo from '../usage-info/vector-space-info' import AppsInfo from '../usage-info/apps-info' import UpgradeBtn from '../upgrade-btn' -import { User01 } from '../../base/icons/src/vender/line/users' -import { MessageFastPlus } from '../../base/icons/src/vender/line/communication' -import { FileUpload } from '../../base/icons/src/vender/line/files' -import cn from '@/utils/classnames' import { useProviderContext } from '@/context/provider-context' import UsageInfo from '@/app/components/billing/usage-info' -const typeStyle = { - [Plan.sandbox]: { - textClassNames: 'text-gray-900', - bg: 'linear-gradient(113deg, rgba(255, 255, 255, 0.51) 3.51%, rgba(255, 255, 255, 0.00) 111.71%), #EAECF0', - }, - [Plan.professional]: { - textClassNames: 'text-[#026AA2]', - bg: 'linear-gradient(113deg, rgba(255, 255, 255, 0.51) 3.51%, rgba(255, 255, 255, 0.00) 111.71%), #E0F2FE', - }, - [Plan.team]: { - textClassNames: 'text-[#3538CD]', - bg: 'linear-gradient(113deg, rgba(255, 255, 255, 0.51) 3.51%, rgba(255, 255, 255, 0.00) 111.71%), #E0EAFF', - }, - [SelfHostedPlan.enterprise]: { - textClassNames: 'text-[#DC6803]', - bg: 'linear-gradient(113deg, rgba(255, 255, 255, 0.51) 3.51%, rgba(255, 255, 255, 0.00) 111.71%), #FFEED3', - }, -} - type Props = { loc: string } @@ -50,74 +35,62 @@ const PlanComp: FC = ({ total, } = plan - const isInHeader = loc === 'header' - return ( -
-
-
-
- {t('billing.currentPlan')} -
-
- {t(`billing.plans.${type}.name`)} -
-
- {(!isInHeader || (isInHeader && type !== Plan.sandbox)) && ( - +
+
+ {plan.type === Plan.sandbox && ( + )} + {plan.type === Plan.professional && ( + + )} + {plan.type === Plan.team && ( + + )} + {(plan.type as any) === SelfHostedPlan.enterprise && ( + + )} +
+
+
+
{t(`billing.plans.${type}.name`)}
+
{t('billing.currentPlan')}
+
+
{t(`billing.plans.${type}.for`)}
+
+ {(plan.type as any) !== SelfHostedPlan.enterprise && ( + + )} +
- {/* Plan detail */} -
- +
+ - - - - {isInHeader && type === Plan.sandbox && ( - - )} + + +
) diff --git a/web/app/components/billing/pricing/index.tsx b/web/app/components/billing/pricing/index.tsx index 28f5ce7122..f8b7242d5e 100644 --- a/web/app/components/billing/pricing/index.tsx +++ b/web/app/components/billing/pricing/index.tsx @@ -60,7 +60,6 @@ const Pricing: FC = ({
{ return ( -
+
diff --git a/web/app/components/billing/upgrade-btn/index.tsx b/web/app/components/billing/upgrade-btn/index.tsx index f080e6bbc4..49b45a8aa0 100644 --- a/web/app/components/billing/upgrade-btn/index.tsx +++ b/web/app/components/billing/upgrade-btn/index.tsx @@ -3,8 +3,8 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' import PremiumBadge from '../../base/premium-badge' +import Button from '@/app/components/base/button' import { SparklesSoft } from '@/app/components/base/icons/src/public/common' -import cn from '@/utils/classnames' import { useModalContext } from '@/context/modal-context' type Props = { @@ -17,23 +17,7 @@ type Props = { loc?: string } -const PlainBtn = ({ className, onClick }: { className?: string; onClick: () => void }) => { - const { t } = useTranslation() - - return ( -
-
- {t('billing.upgradeBtn.plain')} -
-
- ) -} - const UpgradeBtn: FC = ({ - className, isPlain = false, isShort = false, onClick: _onClick, @@ -56,8 +40,13 @@ const UpgradeBtn: FC = ({ } } - if (isPlain) - return + if (isPlain) { + return ( + + ) + } return ( = ({ return ( = ({ const percent = usage / total * 100 const color = (() => { if (percent < LOW) - return '#155EEF' + return 'bg-components-progress-bar-progress-solid' if (percent < MIDDLE) - return '#F79009' + return 'bg-components-progress-warning-progress' - return '#F04438' + return 'bg-components-progress-error-progress' })() return ( -
-
-
- -
{name}
- {tooltip && ( - - {tooltip} -
- } - /> - )} -
-
-
{usage}{unit}
-
/
-
{total === NUM_INFINITE ? t('billing.plansCommon.unlimited') : `${total}${unit}`}
-
+
+ +
+
{name}
+ {tooltip && ( + + {tooltip} +
+ } + /> + )}
-
- +
+ {usage} +
/
+
{total === NUM_INFINITE ? t('billing.plansCommon.unlimited') : `${total}${unit}`}
+
) } diff --git a/web/app/components/billing/usage-info/vector-space-info.tsx b/web/app/components/billing/usage-info/vector-space-info.tsx index bb434c27a2..e578280681 100644 --- a/web/app/components/billing/usage-info/vector-space-info.tsx +++ b/web/app/components/billing/usage-info/vector-space-info.tsx @@ -1,8 +1,10 @@ 'use client' import type { FC } from 'react' import React from 'react' +import { + RiHardDrive3Line, +} from '@remixicon/react' import { useTranslation } from 'react-i18next' -import { ArtificialBrain } from '../../base/icons/src/vender/line/development' import UsageInfo from '../usage-info' import { useProviderContext } from '@/context/provider-context' @@ -22,7 +24,7 @@ const VectorSpaceInfo: FC = ({ return ( { const { t } = useTranslation() const { plan, enableBilling } = useProviderContext() - + const { setShowPricingModal } = useModalContext() const showBillingTip = enableBilling && plan.type === Plan.sandbox const showContact = enableBilling && (plan.type === Plan.professional || plan.type === Plan.team) return (
- { - showBillingTip && ( - -
-
-
{t('custom.upgradeTip.prefix')}
-
{t('custom.upgradeTip.suffix')}
-
- -
-
- ) - } - - { - showContact && ( -
- {t('custom.customize.prefix')} - {t('custom.customize.contactUs')} - {t('custom.customize.suffix')} + {showBillingTip && ( +
+
+
{t('custom.upgradeTip.title')}
+
{t('custom.upgradeTip.des')}
- ) - } +
setShowPricingModal()}>{t('billing.upgradeBtn.encourageShort')}
+
+ )} + + {showContact && ( +
+ {t('custom.customize.prefix')} + {t('custom.customize.contactUs')} + {t('custom.customize.suffix')} +
+ )}
) } diff --git a/web/app/components/custom/custom-web-app-brand/index.tsx b/web/app/components/custom/custom-web-app-brand/index.tsx index 0804f2f425..84967bff78 100644 --- a/web/app/components/custom/custom-web-app-brand/index.tsx +++ b/web/app/components/custom/custom-web-app-brand/index.tsx @@ -2,22 +2,28 @@ import type { ChangeEvent } from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { + RiEditBoxLine, + RiEqualizer2Line, + RiExchange2Fill, + RiImageAddLine, + RiLayoutLeft2Line, RiLoader2Line, + RiPlayLargeLine, } from '@remixicon/react' -import s from './style.module.css' import LogoSite from '@/app/components/base/logo/logo-site' import Switch from '@/app/components/base/switch' import Button from '@/app/components/base/button' -import { MessageDotsCircle } from '@/app/components/base/icons/src/vender/solid/communication' -import { ImagePlus } from '@/app/components/base/icons/src/vender/line/images' +import Divider from '@/app/components/base/divider' import { useProviderContext } from '@/context/provider-context' import { Plan } from '@/app/components/billing/type' import { imageUpload } from '@/app/components/base/image-uploader/utils' import { useToastContext } from '@/app/components/base/toast' +import { BubbleTextMod } from '@/app/components/base/icons/src/vender/solid/communication' import { updateCurrentWorkspace, } from '@/service/common' import { useAppContext } from '@/context/app-context' +import cn from '@/utils/classnames' const ALLOW_FILE_EXTENSIONS = ['svg', 'png'] @@ -107,32 +113,7 @@ const CustomWebAppBrand = () => { return (
-
{t('custom.webapp.title')}
-
-
-
-
- -
-
-
-
-
- { - !webappBrandRemoved && ( -
- POWERED BY - { - webappLogo - ? logo - : - } -
- ) - } -
-
-
+
{t('custom.webapp.removeBrand')} { onChange={handleSwitch} />
-
+
-
{t('custom.webapp.changeLogo')}
-
{t('custom.webapp.changeLogoTip')}
+
{t('custom.webapp.changeLogo')}
+
{t('custom.webapp.changeLogoTip')}
+ {(uploadDisabled || (!webappLogo && !webappBrandRemoved)) && ( + <> + +
+ + )} { !uploading && ( ) @@ -192,6 +177,13 @@ const CustomWebAppBrand = () => { { fileId && ( <> + - ) } -
-
- { - uploadProgress === -1 && ( -
{t('custom.uploadedFail')}
- ) - } + {uploadProgress === -1 && ( +
{t('custom.uploadedFail')}
+ )} +
+
{t('appOverview.overview.appInfo.preview')}
+ +
+
+ {/* chat card */} +
+
+
+
+ +
+
Chatflow App
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ {!webappBrandRemoved && ( + <> +
POWERED BY
+ {webappLogo + ? logo + : + } + + )} +
+
+
+
+
+
+
Hello! How can I assist you today?
+ +
+
Talk to Dify
+
+
+
+ {/* workflow card */} +
+
+
+
+ +
+
Workflow App
+
+ +
+
+
+
RUN ONCE
+
RUN BATCH
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ {!webappBrandRemoved && ( + <> +
POWERED BY
+ {webappLogo + ? logo + : + } + + )} +
+
+
) } diff --git a/web/app/components/datasets/create/website/base/field.tsx b/web/app/components/datasets/create/website/base/field.tsx index 5b5ca90c5d..79cf87497f 100644 --- a/web/app/components/datasets/create/website/base/field.tsx +++ b/web/app/components/datasets/create/website/base/field.tsx @@ -31,7 +31,7 @@ const Field: FC = ({ return (
-
{label}
+
{label}
{isRequired && *} {tooltip && ( { const { t } = useTranslation() return ( -
-
- +
+
+
-
{t('common.apiBasedExtension.title')}
+
{t('common.apiBasedExtension.title')}
- {t('common.apiBasedExtension.link')} +
) diff --git a/web/app/components/header/account-setting/api-based-extension-page/index.tsx b/web/app/components/header/account-setting/api-based-extension-page/index.tsx index 4a0a54079b..832296f77b 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/index.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/index.tsx @@ -5,6 +5,7 @@ import { } from '@remixicon/react' import Item from './item' import Empty from './empty' +import Button from '@/app/components/base/button' import { useModalContext } from '@/context/modal-context' import { fetchApiBasedExtensionList } from '@/service/common' @@ -41,13 +42,14 @@ const ApiBasedExtensionPage = () => { )) ) } -
- + {t('common.apiBasedExtension.add')} -
+
) } diff --git a/web/app/components/header/account-setting/api-based-extension-page/item.tsx b/web/app/components/header/account-setting/api-based-extension-page/item.tsx index 6d716c638e..a218a23160 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/item.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/item.tsx @@ -3,8 +3,9 @@ import { useState } from 'react' import { useTranslation } from 'react-i18next' import { RiDeleteBinLine, + RiEditLine, } from '@remixicon/react' -import { Edit02 } from '@/app/components/base/icons/src/vender/line/general' +import Button from '@/app/components/base/button' import type { ApiBasedExtension } from '@/models/common' import { useModalContext } from '@/context/modal-context' import { deleteApiBasedExtension } from '@/service/common' @@ -36,25 +37,25 @@ const Item: FC = ({ } return ( -
+
-
{data.name}
-
{data.api_endpoint}
+
{data.name}
+
{data.api_endpoint}
-
- + {t('common.operation.edit')} -
-
+
+ + {t('common.operation.delete')} +
{ showDeleteConfirm diff --git a/web/app/components/header/account-setting/api-based-extension-page/modal.tsx b/web/app/components/header/account-setting/api-based-extension-page/modal.tsx index ebe5386388..c890be75c6 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/modal.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/modal.tsx @@ -101,9 +101,9 @@ const ApiBasedExtensionModal: FC = ({ - + {t('common.apiBasedExtension.link')}
diff --git a/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx index c6c8b9ec39..1e82d80a55 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx @@ -11,6 +11,7 @@ import { import { Menu, Transition } from '@headlessui/react' import { syncDataSourceNotion, updateDataSourceNotionAction } from '@/service/common' import Toast from '@/app/components/base/toast' +import cn from '@/utils/classnames' type OperateProps = { payload: { @@ -23,13 +24,6 @@ export default function Operate({ payload, onAuthAgain, }: OperateProps) { - const itemClassName = ` - flex px-3 py-2 hover:bg-gray-50 text-sm text-gray-700 - cursor-pointer - ` - const itemIconClassName = ` - mr-2 mt-[2px] w-4 h-4 text-gray-500 - ` const { t } = useTranslation() const { mutate } = useSWRConfig() @@ -54,8 +48,8 @@ export default function Operate({ { ({ open }) => ( <> - - + + - +
- +
-
{t('common.dataSource.notion.changeAuthorizedPages')}
-
+
{t('common.dataSource.notion.changeAuthorizedPages')}
+
{payload.total} {t('common.dataSource.notion.pagesAuthorized')}
-
- -
{t('common.dataSource.notion.sync')}
+
+ +
{t('common.dataSource.notion.sync')}
-
-
- -
{t('common.dataSource.notion.remove')}
+
+
+ +
{t('common.dataSource.notion.remove')}
diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx index a4a8b9b637..17148ce648 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx @@ -89,11 +89,11 @@ const ConfigFirecrawlModal: FC = ({ return ( -
-
+
+
-
{t(`${I18N_PREFIX}.configFirecrawl`)}
+
{t(`${I18N_PREFIX}.configFirecrawl`)}
@@ -114,7 +114,7 @@ const ConfigFirecrawlModal: FC = ({ />
- + {t(`${I18N_PREFIX}.getApiKeyLinkText`)} @@ -138,12 +138,12 @@ const ConfigFirecrawlModal: FC = ({
-
-
- +
+
+ {t('common.modelProvider.encrypted.front')} diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx index c6d6ad0256..4ddc348a84 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx @@ -75,11 +75,11 @@ const ConfigJinaReaderModal: FC = ({ return ( -
-
+
+
-
-
- +
+
+ {t('common.modelProvider.encrypted.front')} diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx index 628510c5dd..d87fd4396e 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx @@ -85,10 +85,10 @@ const DataSourceWebsite: FC = ({ provider }) => { logo: ({ className }: { className: string }) => ( item.provider === DataSourceProvider.fireCrawl ? ( -
🔥
+
🔥
) : ( -
+
) diff --git a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx index b7fd8193e2..5606297afc 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx @@ -52,7 +52,7 @@ const ConfigItem: FC = ({ ? : } -
+
{ payload.isActive ? t(isNotion ? 'common.dataSource.notion.connected' : 'common.dataSource.website.active') @@ -70,7 +70,7 @@ const ConfigItem: FC = ({ { isWebsite && !readOnly && ( -
+
) diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx index 8d2ec0a8ca..0cd2d26029 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -8,12 +8,13 @@ import ConfigItem from './config-item' import s from './style.module.css' import { DataSourceType } from './types' +import Button from '@/app/components/base/button' import { DataSourceProvider } from '@/models/common' import cn from '@/utils/classnames' type Props = { type: DataSourceType - provider: DataSourceProvider + provider?: DataSourceProvider isConfigured: boolean onConfigure: () => void readOnly: boolean @@ -43,13 +44,13 @@ const Panel: FC = ({ return (
-
+
{t(`common.dataSource.${type}.title`)}
{isWebsite && ( -
- {t('common.dataSource.website.with')} { provider === DataSourceProvider.fireCrawl ? '🔥 Firecrawl' : 'Jina Reader'} +
+ {t('common.dataSource.website.with')} { provider === DataSourceProvider.fireCrawl ? '🔥 Firecrawl' : 'Jina Reader'}
)}
@@ -66,16 +67,13 @@ const Panel: FC = ({ { isConfigured ? ( -
{t('common.dataSource.configure')} -
+ ) : ( <> diff --git a/web/app/components/header/account-setting/index.tsx b/web/app/components/header/account-setting/index.tsx index b3409c226a..7c7f0baa98 100644 --- a/web/app/components/header/account-setting/index.tsx +++ b/web/app/components/header/account-setting/index.tsx @@ -194,12 +194,12 @@ export default function AccountSetting({
-
{activeItem?.name}
- { - activeItem?.description && ( -
{activeItem?.description}
- ) - } +
+ {activeItem?.name} + {activeItem?.description && ( +
{activeItem?.description}
+ )} +
{activeItem?.key === 'provider' && (
{ @@ -35,7 +37,13 @@ const MembersPage = () => { const { locale } = useContext(I18n) const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager, systemFeatures } = useAppContext() - const { data, mutate } = useSWR({ url: '/workspaces/current/members' }, fetchMembers) + const { data, mutate } = useSWR( + { + url: '/workspaces/current/members', + params: {}, + }, + fetchMembers, + ) const [inviteModalVisible, setInviteModalVisible] = useState(false) const [invitationResults, setInvitationResults] = useState([]) const [invitedModalVisible, setInvitedModalVisible] = useState(false) @@ -47,17 +55,17 @@ const MembersPage = () => { return ( <>
-
- -
-
{currentWorkspace?.name}
+
+ +
+
{currentWorkspace?.name}
{enableBilling && ( -
+
{isNotUnlimitedMemberPlan ? (
{t('billing.plansCommon.member')}{locale !== LanguagesSupported[1] && accounts.length > 1 && 's'}
-
{accounts.length}
+
{accounts.length}
/
{plan.total.teamMembers === NUM_INFINITE ? t('billing.plansCommon.unlimited') : plan.total.teamMembers}
@@ -75,14 +83,10 @@ const MembersPage = () => { {isMemberFull && ( )} -
(isCurrentWorkspaceManager && !isMemberFull) && setInviteModalVisible(true)}> - +
+
@@ -99,18 +103,18 @@ const MembersPage = () => {
{account.name} - {account.status === 'pending' && {t('common.members.pending')}} + {account.status === 'pending' && {t('common.members.pending')}} {userProfile.email === account.email && {t('common.members.you')}}
{account.email}
-
{dayjs(Number((account.last_active_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}
+
{dayjs(Number((account.last_active_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}
{ ((isCurrentWorkspaceOwner && account.role !== 'owner') || (isCurrentWorkspaceManager && !['owner', 'admin'].includes(account.role))) ? - :
{RoleMap[account.role] || RoleMap.normal}
+ :
{RoleMap[account.role] || RoleMap.normal}
}
diff --git a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx index 197e3ee867..b0bd638475 100644 --- a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx +++ b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx @@ -1,7 +1,7 @@ 'use client' import { useCallback, useState } from 'react' import { useContext } from 'use-context-selector' -import { XMarkIcon } from '@heroicons/react/24/outline' +import { RiCloseLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' import { ReactMultiEmail } from 'react-multi-email' import { RiErrorWarningFill } from '@remixicon/react' @@ -59,10 +59,10 @@ const InviteModal = ({
{ }} className={cn(s.modal)}>
-
{t('common.members.inviteTeamMember')}
- +
{t('common.members.inviteTeamMember')}
+
-
{t('common.members.inviteTeamMemberTip')}
+
{t('common.members.inviteTeamMemberTip')}
{!isEmailSetup && (
@@ -80,19 +80,18 @@ const InviteModal = ({ )}
-
{t('common.members.email')}
+
{t('common.members.email')}
-
+
{email}
removeEmail(index)}> × diff --git a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx index d3bbc60cae..3fceb41937 100644 --- a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx +++ b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx @@ -34,53 +34,53 @@ const RoleSelector = ({ value, onChange }: RoleSelectorProps) => { onClick={() => setOpen(v => !v)} className='block' > -
-
{t('common.members.invitedAsRole', { role: t(`common.members.${toHump(value)}`) })}
- +
+
{t('common.members.invitedAsRole', { role: t(`common.members.${toHump(value)}`) })}
+
-
+
-
{ +
{ onChange('normal') setOpen(false) }}>
-
{t('common.members.normal')}
-
{t('common.members.normalTip')}
- {value === 'normal' && } +
{t('common.members.normal')}
+
{t('common.members.normalTip')}
+ {value === 'normal' && }
-
{ +
{ onChange('editor') setOpen(false) }}>
-
{t('common.members.editor')}
-
{t('common.members.editorTip')}
- {value === 'editor' && } +
{t('common.members.editor')}
+
{t('common.members.editorTip')}
+ {value === 'editor' && }
-
{ +
{ onChange('admin') setOpen(false) }}>
-
{t('common.members.admin')}
-
{t('common.members.adminTip')}
- {value === 'admin' && } +
{t('common.members.admin')}
+
{t('common.members.adminTip')}
+ {value === 'admin' && }
{datasetOperatorEnabled && ( -
{ +
{ onChange('dataset_operator') setOpen(false) }}>
-
{t('common.members.datasetOperator')}
-
{t('common.members.datasetOperatorTip')}
- {value === 'dataset_operator' && } +
{t('common.members.datasetOperator')}
+
{t('common.members.datasetOperatorTip')}
+ {value === 'dataset_operator' && }
)} diff --git a/web/app/components/header/account-setting/members-page/invited-modal/index.module.css b/web/app/components/header/account-setting/members-page/invited-modal/index.module.css index 4ab9eca574..506b6a0c89 100644 --- a/web/app/components/header/account-setting/members-page/invited-modal/index.module.css +++ b/web/app/components/header/account-setting/members-page/invited-modal/index.module.css @@ -1,7 +1,7 @@ .modal { padding: 32px !important; width: 480px !important; - background: linear-gradient(180deg, rgba(3, 152, 85, 0.05) 0%, rgba(3, 152, 85, 0) 22.44%), #F9FAFB !important; + /* background: linear-gradient(180deg, rgba(3, 152, 85, 0.05) 0%, rgba(3, 152, 85, 0) 22.44%), #F9FAFB !important; */ } .copyIcon { diff --git a/web/app/components/header/account-setting/members-page/invited-modal/index.tsx b/web/app/components/header/account-setting/members-page/invited-modal/index.tsx index fc64d46b06..e4078d10a9 100644 --- a/web/app/components/header/account-setting/members-page/invited-modal/index.tsx +++ b/web/app/components/header/account-setting/members-page/invited-modal/index.tsx @@ -33,25 +33,25 @@ const InvitedModal = ({
-
{t('common.members.invitationSent')}
+
{t('common.members.invitationSent')}
{!IS_CE_EDITION && ( -
{t('common.members.invitationSentTip')}
+
{t('common.members.invitationSentTip')}
)} {IS_CE_EDITION && ( <> -
{t('common.members.invitationSentTip')}
+
{t('common.members.invitationSentTip')}
{ !!successInvitationResults.length && <> -
{t('common.members.invitationLink')}
+
{t('common.members.invitationLink')}
{successInvitationResults.map(item => )} @@ -59,7 +59,7 @@ const InvitedModal = ({ { !!failedInvitationResults.length && <> -
{t('common.members.failedInvitationEmails')}
+
{t('common.members.failedInvitationEmails')}
{ failedInvitationResults.map(item => diff --git a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx index fc0ffdd740..86c2a4426e 100644 --- a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx +++ b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx @@ -35,21 +35,21 @@ const InvitationLink = ({ }, [isCopied]) return ( -
-
-
+
+
+
{value.url}
-
+
-
-
+
+
diff --git a/web/app/components/header/account-setting/members-page/operation/index.module.css b/web/app/components/header/account-setting/members-page/operation/index.module.css deleted file mode 100644 index 0189b046a3..0000000000 --- a/web/app/components/header/account-setting/members-page/operation/index.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.popup { - box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03); -} \ No newline at end of file diff --git a/web/app/components/header/account-setting/members-page/operation/index.tsx b/web/app/components/header/account-setting/members-page/operation/index.tsx index 82867ec522..1f15888dd6 100644 --- a/web/app/components/header/account-setting/members-page/operation/index.tsx +++ b/web/app/components/header/account-setting/members-page/operation/index.tsx @@ -4,26 +4,12 @@ import { Fragment, useMemo } from 'react' import { useContext } from 'use-context-selector' import { Menu, Transition } from '@headlessui/react' import { CheckIcon, ChevronDownIcon } from '@heroicons/react/24/outline' -import s from './index.module.css' import { useProviderContext } from '@/context/provider-context' import cn from '@/utils/classnames' import type { Member } from '@/models/common' import { deleteMemberOrCancelInvitation, updateMemberRole } from '@/service/common' import { ToastContext } from '@/app/components/base/toast' -const itemClassName = ` - flex px-3 py-2 cursor-pointer hover:bg-gray-50 rounded-lg -` -const itemIconClassName = ` - w-4 h-4 mt-[2px] mr-1 text-primary-600 -` -const itemTitleClassName = ` - leading-[20px] text-sm text-gray-700 whitespace-nowrap -` -const itemDescClassName = ` - leading-[18px] text-xs text-gray-500 whitespace-nowrap -` - type IOperationProps = { member: Member operatorRole: string @@ -90,15 +76,9 @@ const Operation = ({ { ({ open }) => ( <> - + {RoleMap[member.role] || RoleMap.normal} - + -
+
{ roleList.map(role => ( -
handleUpdateMemberRole(role)}> +
handleUpdateMemberRole(role)}> { role === member.role - ? - :
+ ? + :
}
-
{t(`common.members.${toHump(role)}`)}
-
{t(`common.members.${toHump(role)}Tip`)}
+
{t(`common.members.${toHump(role)}`)}
+
{t(`common.members.${toHump(role)}Tip`)}
@@ -138,12 +112,12 @@ const Operation = ({ }
-
-
-
+
+
+
-
{t('common.members.removeFromTeam')}
-
{t('common.members.removeFromTeamTip')}
+
{t('common.members.removeFromTeam')}
+
{t('common.members.removeFromTeamTip')}
diff --git a/web/i18n/en-US/billing.ts b/web/i18n/en-US/billing.ts index c39e9c7744..98ee8ac3b6 100644 --- a/web/i18n/en-US/billing.ts +++ b/web/i18n/en-US/billing.ts @@ -10,7 +10,7 @@ const translation = { }, teamMembers: 'Team Members', upgradeBtn: { - plain: 'Upgrade Plan', + plain: 'View Plan', encourage: 'Upgrade Now', encourageShort: 'Upgrade', }, @@ -101,18 +101,22 @@ const translation = { plans: { sandbox: { name: 'Sandbox', + for: 'Free Trial of Core Capabilities', description: 'Free Trial of Core Capabilities', }, professional: { name: 'Professional', + for: 'For Independent Developers/Small Teams', description: 'For Independent Developers/Small Teams', }, team: { name: 'Team', + for: 'For Medium-sized Teams', description: 'For Medium-sized Teams', }, community: { name: 'Community', + for: 'For Individual Users, Small Teams, or Non-commercial Projects', description: 'For Individual Users, Small Teams, or Non-commercial Projects', price: 'Free', btnText: 'Get Started with Community', @@ -125,6 +129,7 @@ const translation = { }, premium: { name: 'Premium', + for: 'For Mid-sized Organizations and Teams', description: 'For Mid-sized Organizations and Teams', price: 'Scalable', priceTip: 'Based on Cloud Marketplace', @@ -140,6 +145,7 @@ const translation = { }, enterprise: { name: 'Enterprise', + for: 'For large-sized Teams', description: 'For Enterprise Require Organization-wide Security, Compliance, Scalability, Control and More Advanced Features', price: 'Custom', priceTip: 'Annual Billing Only', diff --git a/web/i18n/en-US/custom.ts b/web/i18n/en-US/custom.ts index 2d931a8da2..408d4c55e4 100644 --- a/web/i18n/en-US/custom.ts +++ b/web/i18n/en-US/custom.ts @@ -1,6 +1,8 @@ const translation = { custom: 'Customization', upgradeTip: { + title: 'Upgrade your plan', + des: 'Upgrade your plan to customize your brand', prefix: 'Upgrade your plan to', suffix: 'customize your brand.', }, diff --git a/web/i18n/zh-Hans/billing.ts b/web/i18n/zh-Hans/billing.ts index 957f99bed4..a6c676a2a8 100644 --- a/web/i18n/zh-Hans/billing.ts +++ b/web/i18n/zh-Hans/billing.ts @@ -9,7 +9,7 @@ const translation = { vectorSpaceTooltip: '采用高质量索引模式的文档会消耗知识数据存储资源。当知识数据存储达到限制时,将不会上传新文档。', }, upgradeBtn: { - plain: '升级套餐', + plain: '查看套餐', encourage: '立即升级', encourageShort: '升级', }, @@ -100,18 +100,22 @@ const translation = { plans: { sandbox: { name: 'Sandbox', + for: '核心能力的免费试用', description: '核心功能免费试用', }, professional: { name: 'Professional', + for: '适合独立开发者或小团队', description: '对于独立开发者/小团队', }, team: { name: 'Team', + for: '适合中等规模的团队', description: '对于中型团队', }, community: { name: 'Community', + for: '适用于个人用户、小型团队或非商业项目', description: '适用于个人用户、小型团队或非商业项目', price: '免费', btnText: '开始使用', @@ -124,6 +128,7 @@ const translation = { }, premium: { name: 'Premium', + for: '对于中型组织和团队', description: '对于中型组织和团队', price: '可扩展', priceTip: '基于云市场', @@ -139,6 +144,7 @@ const translation = { }, enterprise: { name: 'Enterprise', + for: '适合大人员规模的团队', description: '对于需要组织范围内的安全性、合规性、可扩展性、控制和更高级功能的企业', price: '定制', priceTip: '仅按年计费', diff --git a/web/i18n/zh-Hans/custom.ts b/web/i18n/zh-Hans/custom.ts index f8813831a3..4bec191a60 100644 --- a/web/i18n/zh-Hans/custom.ts +++ b/web/i18n/zh-Hans/custom.ts @@ -1,6 +1,8 @@ const translation = { custom: '定制', upgradeTip: { + title: '升级您的计划', + des: '升级您的计划来定制您的品牌。', prefix: '升级您的计划以', suffix: '定制您的品牌。', }, From 99dc8c7871487d9b8c0669caafb7b93a70bf1956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Fri, 7 Mar 2025 12:12:06 +0800 Subject: [PATCH 13/17] fix: http node request detect text/xml as file (#15174) --- api/core/workflow/nodes/http_request/entities.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/core/workflow/nodes/http_request/entities.py b/api/core/workflow/nodes/http_request/entities.py index 5764ce725e..1a23171108 100644 --- a/api/core/workflow/nodes/http_request/entities.py +++ b/api/core/workflow/nodes/http_request/entities.py @@ -120,6 +120,10 @@ class Response: if disp_type == "attachment" or filename is not None: return True + # For 'text/' types, only 'csv' should be downloaded as file + if content_type.startswith("text/") and "csv" not in content_type: + return False + # For application types, try to detect if it's a text-based format if content_type.startswith("application/"): # Common text-based application types From c6209d76eb80ccb25b361b499bfbe307deb776a7 Mon Sep 17 00:00:00 2001 From: Marc Klingen Date: Fri, 7 Mar 2025 05:15:38 +0100 Subject: [PATCH 14/17] chore: update langfuse description (#15136) --- web/i18n/en-US/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/i18n/en-US/app.ts b/web/i18n/en-US/app.ts index 27279130b1..ef883c0123 100644 --- a/web/i18n/en-US/app.ts +++ b/web/i18n/en-US/app.ts @@ -155,7 +155,7 @@ const translation = { }, langfuse: { title: 'Langfuse', - description: 'Traces, evals, prompt management and metrics to debug and improve your LLM application.', + description: 'Open-source LLM observability, evaluation, prompt management and metrics to debug and improve your LLM application.', }, opik: { title: 'Opik', From 53eb56bb1e395199b99b81c2d9b78895bafff8e4 Mon Sep 17 00:00:00 2001 From: mr-chenguang <37072324+lcgash@users.noreply.github.com> Date: Fri, 7 Mar 2025 12:15:52 +0800 Subject: [PATCH 15/17] Fix: psycopg2.errors.StringDataRightTruncation value too long for type character varying(40) (#15179) --- ...315-5511c782ee4c_extend_provider_column.py | 64 +++++++++++++++++++ api/models/tools.py | 6 +- 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 api/migrations/versions/2025_03_07_0315-5511c782ee4c_extend_provider_column.py diff --git a/api/migrations/versions/2025_03_07_0315-5511c782ee4c_extend_provider_column.py b/api/migrations/versions/2025_03_07_0315-5511c782ee4c_extend_provider_column.py new file mode 100644 index 0000000000..0dc15ffd78 --- /dev/null +++ b/api/migrations/versions/2025_03_07_0315-5511c782ee4c_extend_provider_column.py @@ -0,0 +1,64 @@ +"""extend provider column + +Revision ID: 5511c782ee4c +Revises: 4413929e1ec2 +Create Date: 2025-03-07 03:15:05.364804 + +""" +from alembic import op +import models as models +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '5511c782ee4c' +down_revision = '4413929e1ec2' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('tool_api_providers', schema=None) as batch_op: + batch_op.alter_column('name', + existing_type=sa.VARCHAR(length=40), + type_=sa.String(length=255), + existing_nullable=False) + + with op.batch_alter_table('tool_model_invokes', schema=None) as batch_op: + batch_op.alter_column('provider', + existing_type=sa.VARCHAR(length=40), + type_=sa.String(length=255), + existing_nullable=False) + + with op.batch_alter_table('tool_workflow_providers', schema=None) as batch_op: + batch_op.alter_column('name', + existing_type=sa.VARCHAR(length=40), + type_=sa.String(length=255), + existing_nullable=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + with op.batch_alter_table('tool_workflow_providers', schema=None) as batch_op: + batch_op.alter_column('name', + existing_type=sa.String(length=255), + type_=sa.VARCHAR(length=40), + existing_nullable=False) + + with op.batch_alter_table('tool_model_invokes', schema=None) as batch_op: + batch_op.alter_column('provider', + existing_type=sa.String(length=255), + type_=sa.VARCHAR(length=40), + existing_nullable=False) + + with op.batch_alter_table('tool_api_providers', schema=None) as batch_op: + batch_op.alter_column('name', + existing_type=sa.String(length=255), + type_=sa.VARCHAR(length=40), + existing_nullable=False) + + # ### end Alembic commands ### diff --git a/api/models/tools.py b/api/models/tools.py index b1e321ed85..b941e4ee0f 100644 --- a/api/models/tools.py +++ b/api/models/tools.py @@ -64,7 +64,7 @@ class ApiToolProvider(Base): id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()")) # name of the api provider - name = db.Column(db.String(40), nullable=False) + name = db.Column(db.String(255), nullable=False) # icon icon = db.Column(db.String(255), nullable=False) # original schema @@ -143,7 +143,7 @@ class WorkflowToolProvider(Base): id: Mapped[str] = mapped_column(StringUUID, server_default=db.text("uuid_generate_v4()")) # name of the workflow provider - name: Mapped[str] = mapped_column(db.String(40), nullable=False) + name: Mapped[str] = mapped_column(db.String(255), nullable=False) # label of the workflow provider label: Mapped[str] = mapped_column(db.String(255), nullable=False, server_default="") # icon @@ -205,7 +205,7 @@ class ToolModelInvoke(Base): # tenant id tenant_id = db.Column(StringUUID, nullable=False) # provider - provider = db.Column(db.String(40), nullable=False) + provider = db.Column(db.String(255), nullable=False) # type tool_type = db.Column(db.String(40), nullable=False) # tool name From ff10a4603f4464746395d8b610550a21fec85b4f Mon Sep 17 00:00:00 2001 From: Likename Haojie Date: Fri, 7 Mar 2025 14:06:14 +0800 Subject: [PATCH 16/17] bugfix:cant correct display latex (#14910) --- web/app/components/base/markdown.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/app/components/base/markdown.tsx b/web/app/components/base/markdown.tsx index f324b67f1f..90d6225484 100644 --- a/web/app/components/base/markdown.tsx +++ b/web/app/components/base/markdown.tsx @@ -68,6 +68,7 @@ const preprocessLaTeX = (content: string) => { return flow([ (str: string) => str.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`), + (str: string) => str.replace(/\\\[(.*?)\\\]/gs, (_, equation) => `$$${equation}$$`), (str: string) => str.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`), (str: string) => str.replace(/(^|[^\\])\$(.+?)\$/g, (_, prefix, equation) => `${prefix}$${equation}$`), ])(content) From 55fb4a83f031514b4e2fdc4ccd28da3b0927b4c2 Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 7 Mar 2025 14:52:29 +0800 Subject: [PATCH 17/17] fix: update the link of contact sales in billing page --- web/app/components/billing/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/billing/config.ts b/web/app/components/billing/config.ts index 257b9d80fd..52651259ef 100644 --- a/web/app/components/billing/config.ts +++ b/web/app/components/billing/config.ts @@ -6,7 +6,7 @@ export const NUM_INFINITE = 99999999 export const contractSales = 'contractSales' export const unAvailable = 'unAvailable' -export const contactSalesUrl = 'https://vikgc6bnu1s.typeform.com/to/mowuXTQH' +export const contactSalesUrl = 'https://vikgc6bnu1s.typeform.com/dify-business' export const getStartedWithCommunityUrl = 'https://github.com/langgenius/dify' export const getWithPremiumUrl = 'https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6'