merge main

pull/21398/head
zxhlyh 11 months ago
commit 2670557258

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introducing Dify Workflow File Upload: Recreate Google NotebookLM Podcast</a> 📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introducing Dify Workflow File Upload: Recreate Google NotebookLM Podcast</a>
@ -87,8 +87,6 @@ Please refer to our [FAQ](https://docs.dify.ai/getting-started/install-self-host
**1. Workflow**: **1. Workflow**:
Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond. Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Comprehensive model support**: **2. Comprehensive model support**:
Seamless integration with hundreds of proprietary / open-source LLMs from dozens of inference providers and self-hosted solutions, covering GPT, Mistral, Llama3, and any OpenAI API-compatible models. A full list of supported model providers can be found [here](https://docs.dify.ai/getting-started/readme/model-providers). Seamless integration with hundreds of proprietary / open-source LLMs from dozens of inference providers and self-hosted solutions, covering GPT, Mistral, Llama3, and any OpenAI API-compatible models. A full list of supported model providers can be found [here](https://docs.dify.ai/getting-started/readme/model-providers).

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> · <a href="https://cloud.dify.ai">Dify Cloud</a> ·
@ -54,8 +54,6 @@
**1. سير العمل**: قم ببناء واختبار سير عمل الذكاء الاصطناعي القوي على قماش بصري، مستفيدًا من جميع الميزات التالية وأكثر. **1. سير العمل**: قم ببناء واختبار سير عمل الذكاء الاصطناعي القوي على قماش بصري، مستفيدًا من جميع الميزات التالية وأكثر.
<https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa>
**2. الدعم الشامل للنماذج**: تكامل سلس مع مئات من LLMs الخاصة / مفتوحة المصدر من عشرات من موفري التحليل والحلول المستضافة ذاتيًا، مما يغطي GPT و Mistral و Llama3 وأي نماذج متوافقة مع واجهة OpenAI API. يمكن العثور على قائمة كاملة بمزودي النموذج المدعومين [هنا](https://docs.dify.ai/getting-started/readme/model-providers). **2. الدعم الشامل للنماذج**: تكامل سلس مع مئات من LLMs الخاصة / مفتوحة المصدر من عشرات من موفري التحليل والحلول المستضافة ذاتيًا، مما يغطي 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) ![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">ডিফাই ওয়ার্কফ্লো ফাইল আপলোড পরিচিতি: গুগল নোটবুক-এলএম পডকাস্ট পুনর্নির্মাণ</a> 📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">ডিফাই ওয়ার্কফ্লো ফাইল আপলোড পরিচিতি: গুগল নোটবুক-এলএম পডকাস্ট পুনর্নির্মাণ</a>
@ -84,8 +84,6 @@ docker compose up -d
**১. ওয়ার্কফ্লো**: **১. ওয়ার্কফ্লো**:
ভিজ্যুয়াল ক্যানভাসে AI ওয়ার্কফ্লো তৈরি এবং পরীক্ষা করুন, নিম্নলিখিত সব ফিচার এবং তার বাইরেও আরও অনেক কিছু ব্যবহার করে। ভিজ্যুয়াল ক্যানভাসে AI ওয়ার্কফ্লো তৈরি এবং পরীক্ষা করুন, নিম্নলিখিত সব ফিচার এবং তার বাইরেও আরও অনেক কিছু ব্যবহার করে।
<https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa>
**২. মডেল সাপোর্ট**: **২. মডেল সাপোর্ট**:
GPT, Mistral, Llama3, এবং যেকোনো OpenAI API-সামঞ্জস্যপূর্ণ মডেলসহ, কয়েক ডজন ইনফারেন্স প্রদানকারী এবং সেল্ফ-হোস্টেড সমাধান থেকে শুরু করে প্রোপ্রাইটরি/ওপেন-সোর্স LLM-এর সাথে সহজে ইন্টিগ্রেশন। সমর্থিত মডেল প্রদানকারীদের একটি সম্পূর্ণ তালিকা পাওয়া যাবে [এখানে](https://docs.dify.ai/getting-started/readme/model-providers)। GPT, Mistral, Llama3, এবং যেকোনো OpenAI API-সামঞ্জস্যপূর্ণ মডেলসহ, কয়েক ডজন ইনফারেন্স প্রদানকারী এবং সেল্ফ-হোস্টেড সমাধান থেকে শুরু করে প্রোপ্রাইটরি/ওপেন-সোর্স LLM-এর সাথে সহজে ইন্টিগ্রেশন। সমর্থিত মডেল প্রদানকারীদের একটি সম্পূর্ণ তালিকা পাওয়া যাবে [এখানে](https://docs.dify.ai/getting-started/readme/model-providers)।

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<div align="center"> <div align="center">
<a href="https://cloud.dify.ai">Dify 云服务</a> · <a href="https://cloud.dify.ai">Dify 云服务</a> ·
@ -61,11 +61,6 @@ Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI
**1. 工作流**: **1. 工作流**:
在画布上构建和测试功能强大的 AI 工作流程,利用以下所有功能以及更多功能。 在画布上构建和测试功能强大的 AI 工作流程,利用以下所有功能以及更多功能。
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. 全面的模型支持**: **2. 全面的模型支持**:
与数百种专有/开源 LLMs 以及数十种推理提供商和自托管解决方案无缝集成,涵盖 GPT、Mistral、Llama3 以及任何与 OpenAI API 兼容的模型。完整的支持模型提供商列表可在[此处](https://docs.dify.ai/getting-started/readme/model-providers)找到。 与数百种专有/开源 LLMs 以及数十种推理提供商和自托管解决方案无缝集成,涵盖 GPT、Mistral、Llama3 以及任何与 OpenAI API 兼容的模型。完整的支持模型提供商列表可在[此处](https://docs.dify.ai/getting-started/readme/model-providers)找到。

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Einführung in Dify Workflow File Upload: Google NotebookLM Podcast nachbilden</a> 📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Einführung in Dify Workflow File Upload: Google NotebookLM Podcast nachbilden</a>
@ -83,11 +83,6 @@ Bitte beachten Sie unsere [FAQ](https://docs.dify.ai/getting-started/install-sel
**1. Workflow**: **1. Workflow**:
Erstellen und testen Sie leistungsstarke KI-Workflows auf einer visuellen Oberfläche, wobei Sie alle der folgenden Funktionen und darüber hinaus nutzen können. Erstellen und testen Sie leistungsstarke KI-Workflows auf einer visuellen Oberfläche, wobei Sie alle der folgenden Funktionen und darüber hinaus nutzen können.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Umfassende Modellunterstützung**: **2. Umfassende Modellunterstützung**:
Nahtlose Integration mit Hunderten von proprietären und Open-Source-LLMs von Dutzenden Inferenzanbietern und selbstgehosteten Lösungen, die GPT, Mistral, Llama3 und alle mit der OpenAI API kompatiblen Modelle abdecken. Eine vollständige Liste der unterstützten Modellanbieter finden Sie [hier](https://docs.dify.ai/getting-started/readme/model-providers). Nahtlose Integration mit Hunderten von proprietären und Open-Source-LLMs von Dutzenden Inferenzanbietern und selbstgehosteten Lösungen, die GPT, Mistral, Llama3 und alle mit der OpenAI API kompatiblen Modelle abdecken. Eine vollständige Liste der unterstützten Modellanbieter finden Sie [hier](https://docs.dify.ai/getting-started/readme/model-providers).

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> · <a href="https://cloud.dify.ai">Dify Cloud</a> ·
@ -59,11 +59,6 @@ Dify es una plataforma de desarrollo de aplicaciones de LLM de código abierto.
**1. Flujo de trabajo**: **1. Flujo de trabajo**:
Construye y prueba potentes flujos de trabajo de IA en un lienzo visual, aprovechando todas las siguientes características y más. Construye y prueba potentes flujos de trabajo de IA en un lienzo visual, aprovechando todas las siguientes características y más.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Soporte de modelos completo**: **2. Soporte de modelos completo**:
Integración perfecta con cientos de LLMs propietarios / de código abierto de docenas de proveedores de inferencia y soluciones auto-alojadas, que cubren GPT, Mistral, Llama3 y cualquier modelo compatible con la API de OpenAI. Se puede encontrar una lista completa de proveedores de modelos admitidos [aquí](https://docs.dify.ai/getting-started/readme/model-providers). Integración perfecta con cientos de LLMs propietarios / de código abierto de docenas de proveedores de inferencia y soluciones auto-alojadas, que cubren GPT, Mistral, Llama3 y cualquier modelo compatible con la API de OpenAI. Se puede encontrar una lista completa de proveedores de modelos admitidos [aquí](https://docs.dify.ai/getting-started/readme/model-providers).

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> · <a href="https://cloud.dify.ai">Dify Cloud</a> ·
@ -59,11 +59,6 @@ Dify est une plateforme de développement d'applications LLM open source. Son in
**1. Flux de travail** : **1. Flux de travail** :
Construisez et testez des flux de travail d'IA puissants sur un canevas visuel, en utilisant toutes les fonctionnalités suivantes et plus encore. Construisez et testez des flux de travail d'IA puissants sur un canevas visuel, en utilisant toutes les fonctionnalités suivantes et plus encore.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Prise en charge complète des modèles** : **2. Prise en charge complète des modèles** :
Intégration transparente avec des centaines de LLM propriétaires / open source provenant de dizaines de fournisseurs d'inférence et de solutions auto-hébergées, couvrant GPT, Mistral, Llama3, et tous les modèles compatibles avec l'API OpenAI. Une liste complète des fournisseurs de modèles pris en charge se trouve [ici](https://docs.dify.ai/getting-started/readme/model-providers). Intégration transparente avec des centaines de LLM propriétaires / open source provenant de dizaines de fournisseurs d'inférence et de solutions auto-hébergées, couvrant GPT, Mistral, Llama3, et tous les modèles compatibles avec l'API OpenAI. Une liste complète des fournisseurs de modèles pris en charge se trouve [ici](https://docs.dify.ai/getting-started/readme/model-providers).

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> · <a href="https://cloud.dify.ai">Dify Cloud</a> ·
@ -60,11 +60,6 @@ DifyはオープンソースのLLMアプリケーション開発プラットフ
**1. ワークフロー**: **1. ワークフロー**:
強力なAIワークフローをビジュアルキャンバス上で構築し、テストできます。すべての機能、および以下の機能を使用できます。 強力なAIワークフローをビジュアルキャンバス上で構築し、テストできます。すべての機能、および以下の機能を使用できます。
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. 総合的なモデルサポート**: **2. 総合的なモデルサポート**:
数百ものプロプライエタリ/オープンソースのLLMと、数十もの推論プロバイダーおよびセルフホスティングソリューションとのシームレスな統合を提供します。GPT、Mistral、Llama3、OpenAI APIと互換性のあるすべてのモデルを統合されています。サポートされているモデルプロバイダーの完全なリストは[こちら](https://docs.dify.ai/getting-started/readme/model-providers)をご覧ください。 数百ものプロプライエタリ/オープンソースのLLMと、数十もの推論プロバイダーおよびセルフホスティングソリューションとのシームレスな統合を提供します。GPT、Mistral、Llama3、OpenAI APIと互換性のあるすべてのモデルを統合されています。サポートされているモデルプロバイダーの完全なリストは[こちら](https://docs.dify.ai/getting-started/readme/model-providers)をご覧ください。

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> · <a href="https://cloud.dify.ai">Dify Cloud</a> ·
@ -59,11 +59,6 @@ Dify is an open-source LLM app development platform. Its intuitive interface com
**1. Workflow**: **1. Workflow**:
Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond. Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Comprehensive model support**: **2. Comprehensive model support**:
Seamless integration with hundreds of proprietary / open-source LLMs from dozens of inference providers and self-hosted solutions, covering GPT, Mistral, Llama3, and any OpenAI API-compatible models. A full list of supported model providers can be found [here](https://docs.dify.ai/getting-started/readme/model-providers). Seamless integration with hundreds of proprietary / open-source LLMs from dozens of inference providers and self-hosted solutions, covering GPT, Mistral, Llama3, and any OpenAI API-compatible models. A full list of supported model providers can be found [here](https://docs.dify.ai/getting-started/readme/model-providers).

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify 클라우드</a> · <a href="https://cloud.dify.ai">Dify 클라우드</a> ·
@ -54,11 +54,6 @@
**1. 워크플로우**: **1. 워크플로우**:
다음 기능들을 비롯한 다양한 기능을 활용하여 시각적 캔버스에서 강력한 AI 워크플로우를 구축하고 테스트하세요. 다음 기능들을 비롯한 다양한 기능을 활용하여 시각적 캔버스에서 강력한 AI 워크플로우를 구축하고 테스트하세요.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. 포괄적인 모델 지원:**: **2. 포괄적인 모델 지원:**:
수십 개의 추론 제공업체와 자체 호스팅 솔루션에서 제공하는 수백 개의 독점 및 오픈 소스 LLM과 원활하게 통합되며, GPT, Mistral, Llama3 및 모든 OpenAI API 호환 모델을 포함합니다. 지원되는 모델 제공업체의 전체 목록은 [여기](https://docs.dify.ai/getting-started/readme/model-providers)에서 확인할 수 있습니다. 수십 개의 추론 제공업체와 자체 호스팅 솔루션에서 제공하는 수백 개의 독점 및 오픈 소스 LLM과 원활하게 통합되며, GPT, Mistral, Llama3 및 모든 OpenAI API 호환 모델을 포함합니다. 지원되는 모델 제공업체의 전체 목록은 [여기](https://docs.dify.ai/getting-started/readme/model-providers)에서 확인할 수 있습니다.

@ -1,5 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introduzindo o Dify Workflow com Upload de Arquivo: Recrie o Podcast Google NotebookLM</a> 📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introduzindo o Dify Workflow com Upload de Arquivo: Recrie o Podcast Google NotebookLM</a>
</p> </p>
@ -59,11 +58,6 @@ Dify é uma plataforma de desenvolvimento de aplicativos LLM de código aberto.
**1. Workflow**: **1. Workflow**:
Construa e teste workflows poderosos de IA em uma interface visual, aproveitando todos os recursos a seguir e muito mais. Construa e teste workflows poderosos de IA em uma interface visual, aproveitando todos os recursos a seguir e muito mais.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Suporte abrangente a modelos**: **2. Suporte abrangente a modelos**:
Integração perfeita com centenas de LLMs proprietários e de código aberto de diversas provedoras e soluções auto-hospedadas, abrangendo GPT, Mistral, Llama3 e qualquer modelo compatível com a API da OpenAI. A lista completa de provedores suportados pode ser encontrada [aqui](https://docs.dify.ai/getting-started/readme/model-providers). Integração perfeita com centenas de LLMs proprietários e de código aberto de diversas provedoras e soluções auto-hospedadas, abrangendo GPT, Mistral, Llama3 e qualquer modelo compatível com a API da OpenAI. A lista completa de provedores suportados pode ser encontrada [aqui](https://docs.dify.ai/getting-started/readme/model-providers).

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Predstavljamo nalaganje datotek Dify Workflow: znova ustvarite Google NotebookLM Podcast</a> 📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Predstavljamo nalaganje datotek Dify Workflow: znova ustvarite Google NotebookLM Podcast</a>
@ -81,11 +81,6 @@ Prosimo, glejte naša pogosta vprašanja [FAQ](https://docs.dify.ai/getting-star
**1. Potek dela**: **1. Potek dela**:
Zgradite in preizkusite zmogljive poteke dela AI na vizualnem platnu, pri čemer izkoristite vse naslednje funkcije in več. Zgradite in preizkusite zmogljive poteke dela AI na vizualnem platnu, pri čemer izkoristite vse naslednje funkcije in več.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Celovita podpora za modele**: **2. Celovita podpora za modele**:
Brezhibna integracija s stotinami lastniških/odprtokodnih LLM-jev ducatov ponudnikov sklepanja in samostojnih rešitev, ki pokrivajo GPT, Mistral, Llama3 in vse modele, združljive z API-jem OpenAI. Celoten seznam podprtih ponudnikov modelov najdete [tukaj](https://docs.dify.ai/getting-started/readme/model-providers). Brezhibna integracija s stotinami lastniških/odprtokodnih LLM-jev ducatov ponudnikov sklepanja in samostojnih rešitev, ki pokrivajo GPT, Mistral, Llama3 in vse modele, združljive z API-jem OpenAI. Celoten seznam podprtih ponudnikov modelov najdete [tukaj](https://docs.dify.ai/getting-started/readme/model-providers).

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Bulut</a> · <a href="https://cloud.dify.ai">Dify Bulut</a> ·
@ -55,11 +55,6 @@ Dify, açık kaynaklı bir LLM uygulama geliştirme platformudur. Sezgisel aray
**1. Workflow**: **1. Workflow**:
Görsel bir arayüz üzerinde güçlü AI iş akışları oluşturun ve test edin, aşağıdaki tüm özellikleri ve daha fazlasını kullanarak. Görsel bir arayüz üzerinde güçlü AI iş akışları oluşturun ve test edin, aşağıdaki tüm özellikleri ve daha fazlasını kullanarak.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Kapsamlı model desteği**: **2. Kapsamlı model desteği**:
Çok sayıda çıkarım sağlayıcısı ve kendi kendine barındırılan çözümlerden yüzlerce özel / açık kaynaklı LLM ile sorunsuz entegrasyon sağlar. GPT, Mistral, Llama3 ve OpenAI API uyumlu tüm modelleri kapsar. Desteklenen model sağlayıcılarının tam listesine [buradan](https://docs.dify.ai/getting-started/readme/model-providers) ulaşabilirsiniz. Çok sayıda çıkarım sağlayıcısı ve kendi kendine barındırılan çözümlerden yüzlerce özel / açık kaynaklı LLM ile sorunsuz entegrasyon sağlar. GPT, Mistral, Llama3 ve OpenAI API uyumlu tüm modelleri kapsar. Desteklenen model sağlayıcılarının tam listesine [buradan](https://docs.dify.ai/getting-started/readme/model-providers) ulaşabilirsiniz.

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">介紹 Dify 工作流程檔案上傳功能:重現 Google NotebookLM Podcast</a> 📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">介紹 Dify 工作流程檔案上傳功能:重現 Google NotebookLM Podcast</a>
@ -86,8 +86,6 @@ docker compose up -d
**1. 工作流程** **1. 工作流程**
在視覺化畫布上建立和測試強大的 AI 工作流程,利用以下所有功能及更多。 在視覺化畫布上建立和測試強大的 AI 工作流程,利用以下所有功能及更多。
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. 全面的模型支援** **2. 全面的模型支援**
無縫整合來自數十個推理提供商和自託管解決方案的數百個專有/開源 LLM涵蓋 GPT、Mistral、Llama3 和任何與 OpenAI API 兼容的模型。您可以在[此處](https://docs.dify.ai/getting-started/readme/model-providers)找到支援的模型提供商完整列表。 無縫整合來自數十個推理提供商和自託管解決方案的數百個專有/開源 LLM涵蓋 GPT、Mistral、Llama3 和任何與 OpenAI API 兼容的模型。您可以在[此處](https://docs.dify.ai/getting-started/readme/model-providers)找到支援的模型提供商完整列表。

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> · <a href="https://cloud.dify.ai">Dify Cloud</a> ·
@ -55,11 +55,6 @@ Dify là một nền tảng phát triển ứng dụng LLM mã nguồn mở. Gia
**1. Quy trình làm việc**: **1. Quy trình làm việc**:
Xây dựng và kiểm tra các quy trình làm việc AI mạnh mẽ trên một canvas trực quan, tận dụng tất cả các tính năng sau đây và hơn thế nữa. Xây dựng và kiểm tra các quy trình làm việc AI mạnh mẽ trên một canvas trực quan, tận dụng tất cả các tính năng sau đây và hơn thế nữa.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Hỗ trợ mô hình toàn diện**: **2. Hỗ trợ mô hình toàn diện**:
Tích hợp liền mạch với hàng trăm mô hình LLM độc quyền / mã nguồn mở từ hàng chục nhà cung cấp suy luận và giải pháp tự lưu trữ, bao gồm GPT, Mistral, Llama3, và bất kỳ mô hình tương thích API OpenAI nào. Danh sách đầy đủ các nhà cung cấp mô hình được hỗ trợ có thể được tìm thấy [tại đây](https://docs.dify.ai/getting-started/readme/model-providers). Tích hợp liền mạch với hàng trăm mô hình LLM độc quyền / mã nguồn mở từ hàng chục nhà cung cấp suy luận và giải pháp tự lưu trữ, bao gồm GPT, Mistral, Llama3, và bất kỳ mô hình tương thích API OpenAI nào. Danh sách đầy đủ các nhà cung cấp mô hình được hỗ trợ có thể được tìm thấy [tại đây](https://docs.dify.ai/getting-started/readme/model-providers).

@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings):
CURRENT_VERSION: str = Field( CURRENT_VERSION: str = Field(
description="Dify version", description="Dify version",
default="1.3.1", default="1.4.0",
) )
COMMIT_SHA: str = Field( COMMIT_SHA: str = Field(

@ -40,7 +40,7 @@ IMPORT_INFO_REDIS_KEY_PREFIX = "app_import_info:"
CHECK_DEPENDENCIES_REDIS_KEY_PREFIX = "app_check_dependencies:" CHECK_DEPENDENCIES_REDIS_KEY_PREFIX = "app_check_dependencies:"
IMPORT_INFO_REDIS_EXPIRY = 10 * 60 # 10 minutes IMPORT_INFO_REDIS_EXPIRY = 10 * 60 # 10 minutes
DSL_MAX_SIZE = 10 * 1024 * 1024 # 10MB DSL_MAX_SIZE = 10 * 1024 * 1024 # 10MB
CURRENT_DSL_VERSION = "0.2.0" CURRENT_DSL_VERSION = "0.3.0"
class ImportMode(StrEnum): class ImportMode(StrEnum):

@ -69,7 +69,7 @@
<div class="email-container"> <div class="email-container">
<!-- Header --> <!-- Header -->
<div class="header"> <div class="header">
<img src="https://img.mailinblue.com/6365111/images/content_library/original/64cb67ca60532312c211dc72.png" alt="Dify Logo"> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo">
</div> </div>
<!-- Content --> <!-- Content -->

@ -99,7 +99,7 @@
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<!-- Optional: Add a logo or a header image here --> <!-- Optional: Add a logo or a header image here -->
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo" /> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
</div> </div>
<p class="title">Dify.AI Account Deletion and Verification</p> <p class="title">Dify.AI Account Deletion and Verification</p>
<p class="typography">We received a request to delete your Dify account. To ensure the security of your account and <p class="typography">We received a request to delete your Dify account. To ensure the security of your account and

@ -88,7 +88,7 @@
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<!-- Optional: Add a logo or a header image here --> <!-- Optional: Add a logo or a header image here -->
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo" /> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
</div> </div>
<p class="title">Your Dify.AI Account Has Been Successfully Deleted</p> <p class="title">Your Dify.AI Account Has Been Successfully Deleted</p>
<p class="typography">We're writing to confirm that your Dify.AI account has been successfully deleted as per your request. Your <p class="typography">We're writing to confirm that your Dify.AI account has been successfully deleted as per your request. Your

@ -61,7 +61,7 @@
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<!-- Optional: Add a logo or a header image here --> <!-- Optional: Add a logo or a header image here -->
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo" /> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
</div> </div>
<p class="title">Your login code for Dify</p> <p class="title">Your login code for Dify</p>
<p class="description">Copy and paste this code, this code will only be valid for the next 5 minutes.</p> <p class="description">Copy and paste this code, this code will only be valid for the next 5 minutes.</p>

@ -61,7 +61,7 @@
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<!-- Optional: Add a logo or a header image here --> <!-- Optional: Add a logo or a header image here -->
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo" /> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
</div> </div>
<p class="title">Dify 的登录验证码</p> <p class="title">Dify 的登录验证码</p>
<p class="description">复制并粘贴此验证码,注意验证码仅在接下来的 5 分钟内有效。</p> <p class="description">复制并粘贴此验证码,注意验证码仅在接下来的 5 分钟内有效。</p>

@ -54,7 +54,7 @@
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<!-- Optional: Add a logo or a header image here --> <!-- Optional: Add a logo or a header image here -->
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo"> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo">
</div> </div>
<div class="content"> <div class="content">
<p>Dear {{ to }},</p> <p>Dear {{ to }},</p>

@ -54,7 +54,7 @@
<body> <body>
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo"> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo">
</div> </div>
<div class="content"> <div class="content">
<p>尊敬的 {{ to }}</p> <p>尊敬的 {{ to }}</p>

@ -61,7 +61,7 @@
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<!-- Optional: Add a logo or a header image here --> <!-- Optional: Add a logo or a header image here -->
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo" /> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
</div> </div>
<p class="title">Set your Dify password</p> <p class="title">Set your Dify password</p>
<p class="description">Copy and paste this code, this code will only be valid for the next 5 minutes.</p> <p class="description">Copy and paste this code, this code will only be valid for the next 5 minutes.</p>

@ -61,7 +61,7 @@
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<!-- Optional: Add a logo or a header image here --> <!-- Optional: Add a logo or a header image here -->
<img src="https://cloud.dify.ai/logo/logo-site.png" alt="Dify Logo" /> <img src="https://assets.dify.ai/images/logo.png" alt="Dify Logo" />
</div> </div>
<p class="title">设置您的 Dify 账户密码</p> <p class="title">设置您的 Dify 账户密码</p>
<p class="description">复制并粘贴此验证码,注意验证码仅在接下来的 5 分钟内有效。</p> <p class="description">复制并粘贴此验证码,注意验证码仅在接下来的 5 分钟内有效。</p>

@ -478,7 +478,7 @@ ANALYTICDB_PORT=5432
ANALYTICDB_MIN_CONNECTION=1 ANALYTICDB_MIN_CONNECTION=1
ANALYTICDB_MAX_CONNECTION=5 ANALYTICDB_MAX_CONNECTION=5
# TiDB vector configurations, only available when VECTOR_STORE is `tidb` # TiDB vector configurations, only available when VECTOR_STORE is `tidb_vector`
TIDB_VECTOR_HOST=tidb TIDB_VECTOR_HOST=tidb
TIDB_VECTOR_PORT=4000 TIDB_VECTOR_PORT=4000
TIDB_VECTOR_USER= TIDB_VECTOR_USER=

@ -2,7 +2,7 @@ x-shared-env: &shared-api-worker-env
services: services:
# API service # API service
api: api:
image: langgenius/dify-api:1.3.1 image: langgenius/dify-api:1.4.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -31,7 +31,7 @@ services:
# worker service # worker service
# The Celery worker for processing the queue. # The Celery worker for processing the queue.
worker: worker:
image: langgenius/dify-api:1.3.1 image: langgenius/dify-api:1.4.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -57,7 +57,7 @@ services:
# Frontend web application. # Frontend web application.
web: web:
image: langgenius/dify-web:1.3.1 image: langgenius/dify-web:1.4.0
restart: always restart: always
environment: environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-} CONSOLE_API_URL: ${CONSOLE_API_URL:-}
@ -118,7 +118,7 @@ services:
# The DifySandbox # The DifySandbox
sandbox: sandbox:
image: langgenius/dify-sandbox:0.2.11 image: langgenius/dify-sandbox:0.2.12
restart: always restart: always
environment: environment:
# The DifySandbox configurations # The DifySandbox configurations
@ -142,7 +142,7 @@ services:
# plugin daemon # plugin daemon
plugin_daemon: plugin_daemon:
image: langgenius/dify-plugin-daemon:0.0.9-local image: langgenius/dify-plugin-daemon:0.0.10-local
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.

@ -45,7 +45,7 @@ services:
# The DifySandbox # The DifySandbox
sandbox: sandbox:
image: langgenius/dify-sandbox:0.2.11 image: langgenius/dify-sandbox:0.2.12
restart: always restart: always
env_file: env_file:
- ./middleware.env - ./middleware.env
@ -71,7 +71,7 @@ services:
# plugin daemon # plugin daemon
plugin_daemon: plugin_daemon:
image: langgenius/dify-plugin-daemon:0.0.9-local image: langgenius/dify-plugin-daemon:0.0.10-local
restart: always restart: always
env_file: env_file:
- ./middleware.env - ./middleware.env

@ -492,7 +492,7 @@ x-shared-env: &shared-api-worker-env
services: services:
# API service # API service
api: api:
image: langgenius/dify-api:1.3.1 image: langgenius/dify-api:1.4.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -521,7 +521,7 @@ services:
# worker service # worker service
# The Celery worker for processing the queue. # The Celery worker for processing the queue.
worker: worker:
image: langgenius/dify-api:1.3.1 image: langgenius/dify-api:1.4.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -547,7 +547,7 @@ services:
# Frontend web application. # Frontend web application.
web: web:
image: langgenius/dify-web:1.3.1 image: langgenius/dify-web:1.4.0
restart: always restart: always
environment: environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-} CONSOLE_API_URL: ${CONSOLE_API_URL:-}
@ -608,7 +608,7 @@ services:
# The DifySandbox # The DifySandbox
sandbox: sandbox:
image: langgenius/dify-sandbox:0.2.11 image: langgenius/dify-sandbox:0.2.12
restart: always restart: always
environment: environment:
# The DifySandbox configurations # The DifySandbox configurations
@ -632,7 +632,7 @@ services:
# plugin daemon # plugin daemon
plugin_daemon: plugin_daemon:
image: langgenius/dify-plugin-daemon:0.0.9-local image: langgenius/dify-plugin-daemon:0.0.10-local
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

@ -12,6 +12,7 @@ import { DataSourceType } from '@/models/datasets'
import FileIcon from '@/app/components/base/file-icon' import FileIcon from '@/app/components/base/file-icon'
import { Folder } from '@/app/components/base/icons/src/vender/solid/files' import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel' import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import Drawer from '@/app/components/base/drawer' import Drawer from '@/app/components/base/drawer'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Badge from '@/app/components/base/badge' import Badge from '@/app/components/base/badge'
@ -47,56 +48,66 @@ const Item: FC<ItemProps> = ({
const [isDeleting, setIsDeleting] = useState(false) const [isDeleting, setIsDeleting] = useState(false)
return ( return (
<div className={cn('group relative mb-1 flex w-full items-center rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg py-2 pl-2.5 pr-3 shadow-xs last-of-type:mb-0 hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm', isDeleting && 'border-state-destructive-border hover:bg-state-destructive-hover')}> <div className={cn(
{ 'group relative mb-1 flex h-10 w-full cursor-pointer items-center justify-between rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2 last-of-type:mb-0 hover:bg-components-panel-on-panel-item-bg-hover',
config.data_source_type === DataSourceType.FILE && ( isDeleting && 'border-state-destructive-border hover:bg-state-destructive-hover',
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#E0EAFF] bg-[#F5F8FF]'> )}>
<Folder className='h-4 w-4 text-[#444CE7]' /> <div className='flex w-0 grow items-center space-x-1.5'>
</div> {
) config.data_source_type === DataSourceType.FILE && (
} <div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#E0EAFF] bg-[#F5F8FF]'>
{ <Folder className='h-4 w-4 text-[#444CE7]' />
config.data_source_type === DataSourceType.NOTION && ( </div>
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#EAECF5]'> )
<FileIcon type='notion' className='h-4 w-4' /> }
</div> {
) config.data_source_type === DataSourceType.NOTION && (
} <div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#EAECF5]'>
{ <FileIcon type='notion' className='h-4 w-4' />
config.data_source_type === DataSourceType.WEB && ( </div>
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-blue-100 bg-[#F5FAFF]'> )
<Globe06 className='h-4 w-4 text-blue-600' /> }
</div>
)
}
<div className='grow'>
<div className='flex h-[18px] items-center'>
<div className='grow truncate text-[13px] font-medium text-text-secondary' title={config.name}>{config.name}</div>
{config.provider === 'external'
? <Badge text={t('dataset.externalTag') as string} />
: <Badge
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
/>}
</div>
</div >
<div className='absolute bottom-0 right-0 top-0 hidden w-[124px] items-center justify-end rounded-lg bg-gradient-to-r from-white/50 to-white to-50% pr-2 group-hover:flex'>
{ {
editable && <div config.data_source_type === DataSourceType.WEB && (
className='mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-black/5' <div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-blue-100 bg-[#F5FAFF]'>
onClick={() => setShowSettingsModal(true)} <Globe06 className='h-4 w-4 text-blue-600' />
</div>
)
}
<div className='system-sm-medium w-0 grow truncate text-text-secondary' title={config.name}>{config.name}</div>
</div>
<div className='ml-2 hidden shrink-0 items-center space-x-1 group-hover:flex'>
{
editable && <ActionButton
onClick={(e) => {
e.stopPropagation()
setShowSettingsModal(true)
}}
> >
<RiEditLine className='h-4 w-4 text-text-tertiary' /> <RiEditLine className='h-4 w-4 shrink-0 text-text-tertiary' />
</div> </ActionButton>
} }
<div <ActionButton
className='flex h-6 w-6 cursor-pointer items-center justify-center text-text-tertiary hover:text-text-destructive'
onClick={() => onRemove(config.id)} onClick={() => onRemove(config.id)}
onMouseOver={() => setIsDeleting(true)} state={isDeleting ? ActionButtonState.Destructive : ActionButtonState.Default}
onMouseEnter={() => setIsDeleting(true)}
onMouseLeave={() => setIsDeleting(false)} onMouseLeave={() => setIsDeleting(false)}
> >
<RiDeleteBinLine className='h-4 w-4' /> <RiDeleteBinLine className={cn('h-4 w-4 shrink-0 text-text-tertiary', isDeleting && 'text-text-destructive')} />
</div> </ActionButton>
</div> </div>
{
config.indexing_technique && <Badge
className='shrink-0 group-hover:hidden'
text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
/>
}
{
config.provider === 'external' && <Badge
className='shrink-0 group-hover:hidden'
text={t('dataset.externalTag') as string}
/>
}
<Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'> <Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
<SettingsModal <SettingsModal
currentDataset={config} currentDataset={config}

@ -47,14 +47,14 @@ const Empty = () => {
))} ))}
</div> </div>
{/* mask */} {/* mask */}
<div className='absolute z-20 h-full w-full bg-gradient-to-b from-background-gradient-mask-transparent to-white' /> <div className='absolute z-20 h-full w-full bg-gradient-to-b from-components-panel-bg-transparent to-components-panel-bg' />
<div className='relative z-30 flex h-full items-center justify-center'> <div className='relative z-30 flex h-full items-center justify-center'>
<div className='flex flex-col items-center gap-y-3'> <div className='flex flex-col items-center gap-y-3'>
<div className='relative -z-10 flex h-[52px] w-[52px] items-center justify-center rounded-xl <div className='relative -z-10 flex h-[52px] w-[52px] items-center justify-center rounded-xl
border-[1px] border-dashed border-divider-deep bg-components-card-bg shadow-xl shadow-shadow-shadow-5'> border-[1px] border-dashed border-divider-deep bg-components-card-bg shadow-xl shadow-shadow-shadow-5'>
<Group className='h-5 w-5 text-text-tertiary' /> <Group className='h-5 w-5 text-text-tertiary' />
<Line className='absolute -right-[1px] top-1/2 -translate-y-1/2' /> <Line className='absolute right-[-1px] top-1/2 -translate-y-1/2' />
<Line className='absolute -left-[1px] top-1/2 -translate-y-1/2' /> <Line className='absolute left-[-1px] top-1/2 -translate-y-1/2' />
<Line className='absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2 rotate-90' /> <Line className='absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2 rotate-90' />
<Line className='absolute left-1/2 top-full -translate-x-1/2 -translate-y-1/2 rotate-90' /> <Line className='absolute left-1/2 top-full -translate-x-1/2 -translate-y-1/2 rotate-90' />
</div> </div>

@ -140,16 +140,6 @@ const WorkflowChecklist = ({
</div> </div>
) )
} }
{
node.varErrorMessage?.map((errorMessage: string) => (
<div className='rounded-b-lg bg-gray-25 px-3 py-2'>
<div className='flex text-xs leading-[18px] text-gray-500'>
<AlertTriangle className='mr-2 mt-[3px] h-3 w-3 text-[#F79009]' />
{errorMessage}
</div>
</div>
))
}
</div> </div>
</div> </div>
)) ))

@ -15,7 +15,6 @@ import { useStore } from '../store'
import { import {
getToolCheckParams, getToolCheckParams,
getValidTreeNodes, getValidTreeNodes,
transformStartNodeVariables,
} from '../utils' } from '../utils'
import { import {
CUSTOM_NODE, CUSTOM_NODE,
@ -46,9 +45,6 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
const { data: strategyProviders } = useStrategyProviders() const { data: strategyProviders } = useStrategyProviders()
const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail) const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail)
const chatVarList = useStore(s => s.conversationVariables)
const environmentVariables = useStore(s => s.environmentVariables)
const getCheckData = useCallback((data: CommonNodeType<{}>) => { const getCheckData = useCallback((data: CommonNodeType<{}>) => {
let checkData = data let checkData = data
if (data.type === BlockEnum.KnowledgeRetrieval) { if (data.type === BlockEnum.KnowledgeRetrieval) {
@ -68,10 +64,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
const needWarningNodes = useMemo(() => { const needWarningNodes = useMemo(() => {
const list = [] const list = []
const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true) const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges)
const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables)
const errMessageMap = new Map()
for (let i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
const node = nodes[i] const node = nodes[i]
@ -117,32 +110,8 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
toolIcon, toolIcon,
unConnected: !validNodes.find(n => n.id === node.id), unConnected: !validNodes.find(n => n.id === node.id),
errorMessage, errorMessage,
varErrorMessage: [],
}) })
} }
errMessageMap.set(node.id, list[list.length - 1])
if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) {
const { errorMessage: varErrorMessages } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t)
if (varErrorMessages?.length) {
const errMessage = errMessageMap.get(node.id)
if (errMessage) {
errMessage.varErrorMessage = varErrorMessages
}
else {
list.push({
id: node.id,
type: node.data.type,
title: node.data.title,
toolIcon,
unConnected: !validNodes.find(n => n.id === node.id),
errorMessage: '',
varErrorMessage: varErrorMessages,
})
errMessageMap.set(node.id, list[list.length - 1])
}
}
}
} }
} }
@ -164,13 +133,8 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
}) })
} }
for (let i = 0; i < validNodes.length; i++) {
const node = validNodes[i]
delete node._parentOutputVarMap
}
return list return list
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData, chatVarList, environmentVariables]) }, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders, getCheckData])
return needWarningNodes return needWarningNodes
} }
@ -189,9 +153,6 @@ export const useChecklistBeforePublish = () => {
const updateDatasetsDetail = useDatasetsDetailStore(s => s.updateDatasetsDetail) const updateDatasetsDetail = useDatasetsDetailStore(s => s.updateDatasetsDetail)
const updateTime = useRef(0) const updateTime = useRef(0)
const chatVarList = useStore(s => s.conversationVariables)
const environmentVariables = useStore(s => s.environmentVariables)
const getCheckData = useCallback((data: CommonNodeType<{}>, datasets: DataSet[]) => { const getCheckData = useCallback((data: CommonNodeType<{}>, datasets: DataSet[]) => {
let checkData = data let checkData = data
if (data.type === BlockEnum.KnowledgeRetrieval) { if (data.type === BlockEnum.KnowledgeRetrieval) {
@ -222,15 +183,12 @@ export const useChecklistBeforePublish = () => {
const { const {
validNodes, validNodes,
maxDepth, maxDepth,
} = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges, true) } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges)
if (maxDepth > MAX_TREE_DEPTH) { if (maxDepth > MAX_TREE_DEPTH) {
notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) }) notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) })
return false return false
} }
const allVariablesMap = transformStartNodeVariables(chatVarList, environmentVariables)
// Before publish, we need to fetch datasets detail, in case of the settings of datasets have been changed // Before publish, we need to fetch datasets detail, in case of the settings of datasets have been changed
const knowledgeRetrievalNodes = nodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval) const knowledgeRetrievalNodes = nodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval)
const allDatasetIds = knowledgeRetrievalNodes.reduce<string[]>((acc, node) => { const allDatasetIds = knowledgeRetrievalNodes.reduce<string[]>((acc, node) => {
@ -281,14 +239,6 @@ export const useChecklistBeforePublish = () => {
notify({ type: 'error', message: `[${node.data.title}] ${t('workflow.common.needConnectTip')}` }) notify({ type: 'error', message: `[${node.data.title}] ${t('workflow.common.needConnectTip')}` })
return false return false
} }
if (nodesExtraData[node.data.type as BlockEnum].checkVarValid) {
const { errorMessage: varErrorMessage } = nodesExtraData[node.data.type as BlockEnum].checkVarValid(node.data, { ...allVariablesMap, ...node._parentOutputVarMap }, t)
if (varErrorMessage?.length) {
notify({ type: 'error', message: `[${node.data.title}] ${varErrorMessage[0]}` })
return false
}
}
} }
if (isChatMode && !nodes.find(node => node.data.type === BlockEnum.Answer)) { if (isChatMode && !nodes.find(node => node.data.type === BlockEnum.Answer)) {
@ -302,7 +252,7 @@ export const useChecklistBeforePublish = () => {
} }
return true return true
}, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, chatVarList, environmentVariables]) }, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData])
return { return {
handleCheckBeforePublish, handleCheckBeforePublish,

@ -164,7 +164,7 @@ const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: Val
return res return res
} }
export const formatItem = ( const formatItem = (
item: any, item: any,
isChatMode: boolean, isChatMode: boolean,
filterVar: (payload: Var, selector: ValueSelector) => boolean, filterVar: (payload: Var, selector: ValueSelector) => boolean,

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByText } from '../../utils/workflow'
import type { AnswerNodeType } from './types' import type { AnswerNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -25,19 +24,6 @@ const nodeDefault: NodeDefault<AnswerNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: AnswerNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const answer_warnings = getNotExistVariablesByText(payload.answer || '', varMap)
if (answer_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.answer.answer')} ${t('workflow.common.referenceVar')}${answer_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...answer_warnings],
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import { type AssignerNodeType, WriteMode } from './types' import { type AssignerNodeType, WriteMode } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -46,24 +45,6 @@ const nodeDefault: NodeDefault<AssignerNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: AssignerNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const items = payload.items ?? []
const variables_warnings = getNotExistVariablesByArray(items.map(item => item.variable_selector ?? []) ?? [], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.assigner.assignedVariable')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
const value_warnings = getNotExistVariablesByArray(items.map(item => item.value ?? []) ?? [], varMap)
if (value_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.assigner.setVariable')} ${t('workflow.common.referenceVar')}${value_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...variables_warnings, ...value_warnings],
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import { CodeLanguage, type CodeNodeType } from './types' import { CodeLanguage, type CodeNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -35,20 +34,7 @@ const nodeDefault: NodeDefault<CodeNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: CodeNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const variables_selector = payload.variables.map(v => v.value_selector)
const variables_selector_warnings = getNotExistVariablesByArray(variables_selector, varMap)
if (variables_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.code.inputVars')} ${t('workflow.common.referenceVar')}${variables_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variables_selector_warnings,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { DocExtractorNodeType } from './types' import type { DocExtractorNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -30,19 +29,6 @@ const nodeDefault: NodeDefault<DocExtractorNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: DocExtractorNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const variables_warnings = getNotExistVariablesByArray([payload.variable_selector], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.docExtractor.inputVar')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variables_warnings,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import { AuthorizationType, BodyType, Method } from './types' import { AuthorizationType, BodyType, Method } from './types'
import type { BodyPayload, HttpNodeType } from './types' import type { BodyPayload, HttpNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -45,8 +44,8 @@ const nodeDefault: NodeDefault<HttpNodeType> = {
errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.api') }) errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.api') })
if (!errorMessages if (!errorMessages
&& payload.body.type === BodyType.binary && payload.body.type === BodyType.binary
&& ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0) && ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0)
) )
errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.binaryFileVariable') }) errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.binaryFileVariable') })
@ -55,53 +54,6 @@ const nodeDefault: NodeDefault<HttpNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: HttpNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const url_warnings = getNotExistVariablesByText(payload.url, varMap)
if (url_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.api')} ${t('workflow.common.referenceVar')}${url_warnings.join('、')}${t('workflow.common.noExist')}`)
const headers_warnings = getNotExistVariablesByText(payload.headers, varMap)
if (headers_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.headers')} ${t('workflow.common.referenceVar')}${headers_warnings.join('、')}${t('workflow.common.noExist')}`)
const params_warnings = getNotExistVariablesByText(payload.params, varMap)
if (params_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.params')} ${t('workflow.common.referenceVar')}${params_warnings.join('、')}${t('workflow.common.noExist')}`)
const body_warnings: string[] = []
if ([BodyType.binary].includes(payload.body.type)) {
const body_data = payload.body.data as BodyPayload
body_data.forEach((item) => {
const key_warnings = getNotExistVariablesByText(item.key || '', varMap)
if (key_warnings.length)
body_warnings.push(...key_warnings)
const warnings = getNotExistVariablesByArray([item.file || []], varMap)
if (warnings.length)
body_warnings.push(...warnings)
})
}
else {
const body_data = payload.body.data as BodyPayload
body_data.forEach((item) => {
const key_warnings = getNotExistVariablesByText(item.key || '', varMap)
if (key_warnings.length)
body_warnings.push(...key_warnings)
const value_warnings = getNotExistVariablesByText(item.value || '', varMap)
if (value_warnings.length)
body_warnings.push(...value_warnings)
})
}
if (body_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.http.body')} ${t('workflow.common.referenceVar')}${body_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...url_warnings, ...headers_warnings, ...params_warnings, ...body_warnings],
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,6 +1,4 @@
import type { Var } from '../../types'
import type { NodeDefault } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import { type IfElseNodeType, LogicalOperator } from './types' import { type IfElseNodeType, LogicalOperator } from './types'
import { isEmptyRelatedOperator } from './utils' import { isEmptyRelatedOperator } from './utils'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -76,41 +74,6 @@ const nodeDefault: NodeDefault<IfElseNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: IfElseNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const condition_variable_selector_warnings: string[] = []
const condition_value_warnings: string[] = []
payload.cases.forEach((caseItem) => {
caseItem.conditions.forEach((condition) => {
if (!condition.variable_selector)
return
const selector_warnings = getNotExistVariablesByArray([condition.variable_selector], varMap)
if (selector_warnings.length)
condition_variable_selector_warnings.push(...selector_warnings)
const value_warnings = Array.isArray(condition.value) ? getNotExistVariablesByArray([condition.value], varMap) : getNotExistVariablesByText(condition.value, varMap)
if (value_warnings.length)
condition_value_warnings.push(...value_warnings)
condition.sub_variable_condition?.conditions.forEach((subCondition) => {
const sub_variable_value_warnings = Array.isArray(subCondition.value) ? getNotExistVariablesByArray([subCondition.value], varMap) : getNotExistVariablesByText(subCondition.value, varMap)
if (sub_variable_value_warnings.length)
condition_value_warnings.push(...sub_variable_value_warnings)
})
})
})
if (condition_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.ifElse.condition')} ${t('workflow.common.referenceVar')}${condition_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
if (condition_value_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.ifElse.enterValue')} ${t('workflow.common.referenceVar')}${condition_value_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: condition_variable_selector_warnings,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,6 +1,5 @@
import { BlockEnum, ErrorHandleMode } from '../../types' import { BlockEnum, ErrorHandleMode } from '../../types'
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { IterationNodeType } from './types' import type { IterationNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
@ -49,19 +48,6 @@ const nodeDefault: NodeDefault<IterationNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: IterationNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const iterator_selector_warnings = getNotExistVariablesByArray([payload.iterator_selector], varMap)
if (iterator_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.iteration.input')} ${t('workflow.common.referenceVar')}${iterator_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: iterator_selector_warnings,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -55,7 +55,7 @@ const DatasetItem: FC<Props> = ({
}, [onRemove]) }, [onRemove])
return ( return (
<div className={`group/dataset-item flex h-10 cursor-pointer items-center justify-between rounded-xl <div className={`group/dataset-item flex h-10 cursor-pointer items-center justify-between rounded-lg
border-[0.5px] border-components-panel-border-subtle px-2 border-[0.5px] border-components-panel-border-subtle px-2
${isDeleteHovered ${isDeleteHovered
? 'border-state-destructive-border bg-state-destructive-hover' ? 'border-state-destructive-border bg-state-destructive-hover'

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { KnowledgeRetrievalNodeType } from './types' import type { KnowledgeRetrievalNodeType } from './types'
import { checkoutRerankModelConfigedInRetrievalSettings } from './utils' import { checkoutRerankModelConfigedInRetrievalSettings } from './utils'
import { DATASET_DEFAULT } from '@/config' import { DATASET_DEFAULT } from '@/config'
@ -48,19 +47,6 @@ const nodeDefault: NodeDefault<KnowledgeRetrievalNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: KnowledgeRetrievalNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const query_variable_selector_warnings = getNotExistVariablesByArray([payload.query_variable_selector], varMap)
if (query_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.knowledgeRetrieval.queryVariable')} ${t('workflow.common.referenceVar')}${query_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...query_variable_selector_warnings],
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,6 +1,5 @@
import { BlockEnum, VarType } from '../../types' import { BlockEnum, VarType } from '../../types'
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import { comparisonOperatorNotRequireValue } from '../if-else/utils' import { comparisonOperatorNotRequireValue } from '../if-else/utils'
import { type ListFilterNodeType, OrderBy } from './types' import { type ListFilterNodeType, OrderBy } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -58,18 +57,6 @@ const nodeDefault: NodeDefault<ListFilterNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: ListFilterNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const variable_warnings = getNotExistVariablesByArray([payload.variable], varMap)
if (variable_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.listFilter.inputVar')} ${t('workflow.common.referenceVar')}${variable_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variable_warnings,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,7 +1,5 @@
import type { Var } from '../../types' import { BlockEnum, EditionType } from '../../types'
import { BlockEnum, EditionType, VarType } from '../../types'
import { type NodeDefault, type PromptItem, PromptRole } from '../../types' import { type NodeDefault, type PromptItem, PromptRole } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import type { LLMNodeType } from './types' import type { LLMNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -83,37 +81,6 @@ const nodeDefault: NodeDefault<LLMNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: LLMNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const prompt_templates_warnings: string[] = []
if (payload.context?.enabled && payload.context?.variable_selector?.length) {
const context_variable_selector_warnings = getNotExistVariablesByArray([payload.context.variable_selector], varMap)
if (context_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.context')} ${t('workflow.common.referenceVar')}${context_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
}
const prompt_templates = Array.isArray(payload.prompt_template) ? payload.prompt_template : [payload.prompt_template] as PromptItem[]
prompt_templates.forEach((v) => {
prompt_templates_warnings.push(...getNotExistVariablesByText(v.text, { context: { variable: 'context', type: VarType.string }, ...varMap }))
})
if (prompt_templates_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.prompt')} ${t('workflow.common.referenceVar')}${prompt_templates_warnings.join('、')}${t('workflow.common.noExist')}`)
const memory_query_prompt_template_warnings = getNotExistVariablesByText(payload.memory?.query_prompt_template || '', varMap)
if (memory_query_prompt_template_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.common.memories.title')}USER ${t('workflow.common.referenceVar')}${memory_query_prompt_template_warnings.join('、')}${t('workflow.common.noExist')}`)
if (payload.vision?.enabled && payload.vision?.configs?.variable_selector?.length) {
const vision_variable_selector_warnings = getNotExistVariablesByArray([payload.vision.configs.variable_selector], varMap)
if (vision_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
}
return {
isValid: true,
warning_vars: [...prompt_templates_warnings, ...memory_query_prompt_template_warnings],
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import { type ParameterExtractorNodeType, ReasoningModeType } from './types' import { type ParameterExtractorNodeType, ReasoningModeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -62,30 +61,6 @@ const nodeDefault: NodeDefault<ParameterExtractorNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: ParameterExtractorNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const variables_warnings = getNotExistVariablesByArray([payload.query], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.parameterExtractor.inputVar')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
let vision_variable_warnings: string[] = []
if (payload.vision?.configs?.variable_selector?.length) {
vision_variable_warnings = getNotExistVariablesByArray([payload.vision.configs.variable_selector], varMap)
if (vision_variable_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_warnings.join('、')}${t('workflow.common.noExist')}`)
}
const instruction_warnings = getNotExistVariablesByText(payload.instruction, varMap)
if (instruction_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.parameterExtractor.instruction')} ${t('workflow.common.referenceVar')}${instruction_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...variables_warnings, ...vision_variable_warnings, ...instruction_warnings],
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
import type { QuestionClassifierNodeType } from './types' import type { QuestionClassifierNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -69,30 +68,6 @@ const nodeDefault: NodeDefault<QuestionClassifierNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: QuestionClassifierNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const query_variable_selector_warnings = getNotExistVariablesByArray([payload.query_variable_selector], varMap)
if (query_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.questionClassifiers.inputVars')} ${t('workflow.common.referenceVar')}${query_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
let vision_variable_selector_warnings: string[] = []
if (payload.vision?.configs?.variable_selector?.length) {
vision_variable_selector_warnings = getNotExistVariablesByArray([payload.vision?.configs?.variable_selector], varMap)
if (vision_variable_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.llm.vision')} ${t('workflow.common.referenceVar')}${vision_variable_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
}
const instruction_warnings: string[] = getNotExistVariablesByText(payload.instruction, varMap)
if (instruction_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.questionClassifiers.advancedSetting')}-${t('workflow.nodes.questionClassifiers.instruction')} ${t('workflow.common.referenceVar')}${instruction_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: [...query_variable_selector_warnings, ...vision_variable_selector_warnings, ...instruction_warnings],
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,5 +1,4 @@
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { TemplateTransformNodeType } from './types' import type { TemplateTransformNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -33,19 +32,6 @@ const nodeDefault: NodeDefault<TemplateTransformNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: TemplateTransformNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const variables_selector = payload.variables.map(v => v.value_selector)
const variables_selector_warnings = getNotExistVariablesByArray(variables_selector, varMap)
if (variables_selector_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.templateTransform.inputVars')} ${t('workflow.common.referenceVar')}${variables_selector_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,9 +1,8 @@
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
import type { NodeDefault, Var } from '../../types' import type { NodeDefault } from '../../types'
import type { ToolNodeType } from './types' import type { ToolNodeType } from './types'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { getNotExistVariablesByArray, getNotExistVariablesByText } from '../../utils/workflow'
const i18nPrefix = 'workflow.errorMsg' const i18nPrefix = 'workflow.errorMsg'
@ -60,35 +59,6 @@ const nodeDefault: NodeDefault<ToolNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: ToolNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr = []
const tool_parametersMap = payload.tool_parameters
const tool_parameters_array = Object.values(tool_parametersMap)
const tool_parameters_warnings: string[] = []
tool_parameters_array?.forEach((item) => {
if (!item.value)
return
if (Array.isArray(item.value)) {
const warnings = getNotExistVariablesByArray([item.value], varMap)
if (warnings.length)
tool_parameters_warnings.push(...warnings)
return
}
if (typeof item.value === 'string') {
const warnings = getNotExistVariablesByText(item.value, varMap)
if (warnings.length)
tool_parameters_warnings.push(...warnings)
}
})
if (tool_parameters_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.tool.inputVars')} ${t('workflow.common.referenceVar')}${tool_parameters_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: tool_parameters_warnings,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -1,6 +1,4 @@
import type { Var } from '../../types'
import { type NodeDefault, VarType } from '../../types' import { type NodeDefault, VarType } from '../../types'
import { getNotExistVariablesByArray } from '../../utils/workflow'
import type { VariableAssignerNodeType } from './types' import type { VariableAssignerNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils' import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types'
@ -53,18 +51,6 @@ const nodeDefault: NodeDefault<VariableAssignerNodeType> = {
errorMessage: errorMessages, errorMessage: errorMessages,
} }
}, },
checkVarValid(payload: VariableAssignerNodeType, varMap: Record<string, Var>, t: any) {
const errorMessageArr: string[] = []
const variables_warnings = getNotExistVariablesByArray(payload.variables ?? [], varMap)
if (variables_warnings.length)
errorMessageArr.push(`${t('workflow.nodes.variableAssigner.title')} ${t('workflow.common.referenceVar')}${variables_warnings.join('、')}${t('workflow.common.noExist')}`)
return {
isValid: true,
warning_vars: variables_warnings,
errorMessage: errorMessageArr,
}
},
} }
export default nodeDefault export default nodeDefault

@ -303,7 +303,6 @@ export type NodeDefault<T = {}> = {
} }
defaultValue: Partial<T> defaultValue: Partial<T>
checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean; errorMessage?: string } checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean; errorMessage?: string }
checkVarValid?: (payload: T, varMap: Record<string, Var>, t: any,) => { isValid: boolean; errorMessage?: string[] }
} }
export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => void export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => void

@ -5,18 +5,6 @@ import type {
} from '@/app/components/workflow/types' } from '@/app/components/workflow/types'
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
jest.mock('ky', () => ({
__esModule: true,
default: {
create: jest.fn(),
},
}))
jest.mock('lodash-es/groupBy', () => ({
__esModule: true,
default: jest.fn(),
}))
describe('preprocessNodesAndEdges', () => { describe('preprocessNodesAndEdges', () => {
it('process nodes without iteration node or loop node should return origin nodes and edges.', () => { it('process nodes without iteration node or loop node should return origin nodes and edges.', () => {
const nodes = [ const nodes = [

@ -10,20 +10,14 @@ import {
uniqBy, uniqBy,
} from 'lodash-es' } from 'lodash-es'
import type { import type {
ConversationVariable,
Edge, Edge,
EnvironmentVariable,
Node, Node,
Var,
} from '../types' } from '../types'
import { import {
BlockEnum, BlockEnum,
} from '../types' } from '../types'
import type { IterationNodeType } from '../nodes/iteration/types' import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types' import type { LoopNodeType } from '../nodes/loop/types'
import { VAR_REGEX_TEXT } from '@/config'
import { formatItem } from '../nodes/_base/components/variable/utils'
import type { StructuredOutput } from '../nodes/llm/types'
export const canRunBySingle = (nodeType: BlockEnum) => { export const canRunBySingle = (nodeType: BlockEnum) => {
return nodeType === BlockEnum.LLM return nodeType === BlockEnum.LLM
@ -92,17 +86,7 @@ export const getNodesConnectedSourceOrTargetHandleIdsMap = (changes: ConnectedSo
return nodesConnectedSourceOrTargetHandleIdsMap return nodesConnectedSourceOrTargetHandleIdsMap
} }
function getParentOutputVarMap(item: Var, path: string, varMap: Record<string, Var>) { export const getValidTreeNodes = (nodes: Node[], edges: Edge[]) => {
if (!item.children || (Array.isArray(item.children) && !item.children.length) || ((item.children as StructuredOutput).schema))
return
(item.children as Var[]).forEach((child) => {
const newPath = `${path}.${child.variable}`
varMap[newPath] = child
getParentOutputVarMap(child, newPath, varMap)
})
}
export const getValidTreeNodes = (nodes: Node[], edges: Edge[], isCollectVar?: boolean) => {
const startNode = nodes.find(node => node.data.type === BlockEnum.Start) const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
if (!startNode) { if (!startNode) {
@ -125,19 +109,6 @@ export const getValidTreeNodes = (nodes: Node[], edges: Edge[], isCollectVar?: b
outgoers.forEach((outgoer) => { outgoers.forEach((outgoer) => {
list.push(outgoer) list.push(outgoer)
if (isCollectVar) {
const nodeObj = formatItem(root, false, () => true)
const varMap = {} as Record<string, Var>
nodeObj.vars.forEach((item) => {
if (item.variable.startsWith('sys.'))
return
const newPath = `${nodeObj.nodeId}.${item.variable}`
varMap[newPath] = item
getParentOutputVarMap(item, newPath, varMap)
})
outgoer._parentOutputVarMap = { ...(root._parentOutputVarMap ?? {}), ...varMap }
}
if (outgoer.data.type === BlockEnum.Iteration) if (outgoer.data.type === BlockEnum.Iteration)
list.push(...nodes.filter(node => node.parentId === outgoer.id)) list.push(...nodes.filter(node => node.parentId === outgoer.id))
if (outgoer.data.type === BlockEnum.Loop) if (outgoer.data.type === BlockEnum.Loop)
@ -356,48 +327,3 @@ export const getParallelInfo = (nodes: Node[], edges: Edge[], parentNodeId?: str
export const hasErrorHandleNode = (nodeType?: BlockEnum) => { export const hasErrorHandleNode = (nodeType?: BlockEnum) => {
return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code
} }
export const transformStartNodeVariables = (chatVarList: ConversationVariable[], environmentVariables: EnvironmentVariable[]) => {
const variablesMap: Record<string, ConversationVariable | EnvironmentVariable> = {}
chatVarList.forEach((variable) => {
variablesMap[`conversation.${variable.name}`] = variable
})
environmentVariables.forEach((variable) => {
variablesMap[`env.${variable.name}`] = variable
})
return variablesMap
}
export const getNotExistVariablesByText = (text: string, varMap: Record<string, Var>) => {
const var_warnings: string[] = []
text?.replace(VAR_REGEX_TEXT, (str, id_name) => {
if (id_name.startsWith('sys.'))
return str
if (varMap[id_name])
return str
const arr = id_name.split('.')
arr.shift()
var_warnings.push(arr.join('.'))
return str
})
return var_warnings
}
export const getNotExistVariablesByArray = (array: string[][], varMap: Record<string, Var>) => {
if (!array.length)
return []
const var_warnings: string[] = []
array.forEach((item) => {
if (!item.length)
return
if (['sys'].includes(item[0]))
return
const var_warning = varMap[item.join('.')]
if (var_warning)
return
const arr = [...item]
arr.shift()
var_warnings.push(arr.join('.'))
})
return var_warnings
}

@ -282,8 +282,6 @@ Thought: {{agent_scratchpad}}
export const VAR_REGEX = /\{\{(#[a-zA-Z0-9_-]{1,50}(\.[a-zA-Z_]\w{0,29}){1,10}#)\}\}/gi export const VAR_REGEX = /\{\{(#[a-zA-Z0-9_-]{1,50}(\.[a-zA-Z_]\w{0,29}){1,10}#)\}\}/gi
export const VAR_REGEX_TEXT = /\{\{#([a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*)#\}\}/gi
export const resetReg = () => VAR_REGEX.lastIndex = 0 export const resetReg = () => VAR_REGEX.lastIndex = 0
export let textGenerationTimeoutMs = 60000 export let textGenerationTimeoutMs = 60000

@ -113,8 +113,6 @@ const translation = {
addFailureBranch: 'Add Fail Branch', addFailureBranch: 'Add Fail Branch',
loadMore: 'Load More', loadMore: 'Load More',
noHistory: 'No History', noHistory: 'No History',
referenceVar: 'Reference Variable',
noExist: 'No such variable',
}, },
env: { env: {
envPanelTitle: 'Environment Variables', envPanelTitle: 'Environment Variables',
@ -601,7 +599,6 @@ const translation = {
selectVariable: 'Select variable...', selectVariable: 'Select variable...',
addSubVariable: 'Sub Variable', addSubVariable: 'Sub Variable',
select: 'Select', select: 'Select',
condition: 'Condition',
}, },
variableAssigner: { variableAssigner: {
title: 'Assign variables', title: 'Assign variables',

@ -113,8 +113,6 @@ const translation = {
addFailureBranch: '失敗ブランチを追加', addFailureBranch: '失敗ブランチを追加',
loadMore: 'さらに読み込む', loadMore: 'さらに読み込む',
noHistory: '履歴がありません', noHistory: '履歴がありません',
referenceVar: '参照変数',
noExist: '存在しません',
}, },
env: { env: {
envPanelTitle: '環境変数', envPanelTitle: '環境変数',
@ -600,7 +598,6 @@ const translation = {
}, },
select: '選ぶ', select: '選ぶ',
addSubVariable: 'サブ変数', addSubVariable: 'サブ変数',
condition: '条件',
}, },
variableAssigner: { variableAssigner: {
title: '変数を代入する', title: '変数を代入する',

@ -113,8 +113,6 @@ const translation = {
openInExplore: '在“探索”中打开', openInExplore: '在“探索”中打开',
loadMore: '加载更多', loadMore: '加载更多',
noHistory: '没有历史版本', noHistory: '没有历史版本',
referenceVar: '引用变量',
noExist: '不存在',
}, },
env: { env: {
envPanelTitle: '环境变量', envPanelTitle: '环境变量',
@ -602,7 +600,6 @@ const translation = {
selectVariable: '选择变量', selectVariable: '选择变量',
addSubVariable: '添加子变量', addSubVariable: '添加子变量',
select: '选择', select: '选择',
condition: '条件',
}, },
variableAssigner: { variableAssigner: {
title: '变量赋值', title: '变量赋值',

@ -1,6 +1,6 @@
{ {
"name": "dify-web", "name": "dify-web",
"version": "1.3.1", "version": "1.4.0",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=v22.11.0" "node": ">=v22.11.0"

Loading…
Cancel
Save