| @@ -1,6 +1,6 @@ | |||
| #!/bin/bash | |||
| npm add -g pnpm@10.13.1 | |||
| npm add -g pnpm@10.15.0 | |||
| cd web && pnpm install | |||
| pipx install uv | |||
| @@ -107,74 +107,6 @@ Monitor and analyze application logs and performance over time. You could contin | |||
| **7. Backend-as-a-Service**: | |||
| All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic. | |||
| ## Feature Comparison | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Feature</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Programming Approach</td> | |||
| <td align="center">API + App-oriented</td> | |||
| <td align="center">Python Code</td> | |||
| <td align="center">App-oriented</td> | |||
| <td align="center">API-oriented</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Supported LLMs</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">OpenAI-only</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG Engine</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agent</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Workflow</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Observability</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Enterprise Feature (SSO/Access control)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Local Deployment</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Using Dify | |||
| - **Cloud </br>** | |||
| @@ -68,74 +68,6 @@ | |||
| **7.الواجهة الخلفية (Backend) كخدمة**: تأتي جميع عروض Dify مع APIs مطابقة، حتى يمكنك دمج Dify بسهولة في منطق أعمالك الخاص. | |||
| ## مقارنة الميزات | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">الميزة</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">نهج البرمجة</td> | |||
| <td align="center">موجّه لـ تطبيق + واجهة برمجة تطبيق (API)</td> | |||
| <td align="center">برمجة Python</td> | |||
| <td align="center">موجه لتطبيق</td> | |||
| <td align="center">واجهة برمجة تطبيق (API)</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">LLMs المدعومة</td> | |||
| <td align="center">تنوع غني</td> | |||
| <td align="center">تنوع غني</td> | |||
| <td align="center">تنوع غني</td> | |||
| <td align="center">فقط OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">محرك RAG</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">الوكيل</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">سير العمل</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">الملاحظة</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">ميزات الشركات (SSO / مراقبة الوصول)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">نشر محلي</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## استخدام Dify | |||
| - **سحابة </br>** | |||
| @@ -106,74 +106,6 @@ LLM ফাংশন কলিং বা ReAct উপর ভিত্তি ক | |||
| **7. ব্যাকএন্ড-অ্যাজ-এ-সার্ভিস**: | |||
| ডিফাই-এর সমস্ত অফার সংশ্লিষ্ট API-সহ আছে, যাতে আপনি অনায়াসে ডিফাইকে আপনার নিজস্ব বিজনেস লজিকে ইন্টেগ্রেট করতে পারেন। | |||
| ## বৈশিষ্ট্য তুলনা | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">বৈশিষ্ট্য</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">প্রোগ্রামিং পদ্ধতি</td> | |||
| <td align="center">API + App-oriented</td> | |||
| <td align="center">Python Code</td> | |||
| <td align="center">App-oriented</td> | |||
| <td align="center">API-oriented</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">সাপোর্টেড LLMs</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">OpenAI-only</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG ইঞ্জিন</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">এজেন্ট</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">ওয়ার্কফ্লো</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">অবজার্ভেবল</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">এন্টারপ্রাইজ ফিচার (SSO/Access control)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">লোকাল ডেপ্লয়মেন্ট</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## ডিফাই-এর ব্যবহার | |||
| - **ক্লাউড </br>** | |||
| @@ -80,74 +80,6 @@ Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI | |||
| **7. 后端即服务**: | |||
| 所有 Dify 的功能都带有相应的 API,因此您可以轻松地将 Dify 集成到自己的业务逻辑中。 | |||
| ## 功能比较 | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">功能</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistant API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">编程方法</td> | |||
| <td align="center">API + 应用程序导向</td> | |||
| <td align="center">Python 代码</td> | |||
| <td align="center">应用程序导向</td> | |||
| <td align="center">API 导向</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">支持的 LLMs</td> | |||
| <td align="center">丰富多样</td> | |||
| <td align="center">丰富多样</td> | |||
| <td align="center">丰富多样</td> | |||
| <td align="center">仅限 OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG 引擎</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agent</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">工作流</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">可观测性</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">企业功能(SSO/访问控制)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">本地部署</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## 使用 Dify | |||
| - **云 </br>** | |||
| @@ -106,74 +106,6 @@ Sie können Agenten basierend auf LLM Function Calling oder ReAct definieren und | |||
| **7. Backend-as-a-Service**: | |||
| Alle Dify-Angebote kommen mit entsprechenden APIs, sodass Sie Dify mühelos in Ihre eigene Geschäftslogik integrieren können. | |||
| ## Vergleich der Merkmale | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Feature</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Programming Approach</td> | |||
| <td align="center">API + App-oriented</td> | |||
| <td align="center">Python Code</td> | |||
| <td align="center">App-oriented</td> | |||
| <td align="center">API-oriented</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Supported LLMs</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">OpenAI-only</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG Engine</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agent</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Workflow</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Observability</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Enterprise Feature (SSO/Access control)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Local Deployment</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Dify verwenden | |||
| - **Cloud </br>** | |||
| @@ -79,74 +79,6 @@ Supervisa y analiza registros de aplicaciones y rendimiento a lo largo del tiemp | |||
| **7. Backend como servicio**: | |||
| Todas las ofertas de Dify vienen con APIs correspondientes, por lo que podrías integrar Dify sin esfuerzo en tu propia lógica empresarial. | |||
| ## Comparación de características | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Característica</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">API de Asistentes de OpenAI</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Enfoque de programación</td> | |||
| <td align="center">API + orientado a la aplicación</td> | |||
| <td align="center">Código Python</td> | |||
| <td align="center">Orientado a la aplicación</td> | |||
| <td align="center">Orientado a la API</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">LLMs admitidos</td> | |||
| <td align="center">Gran variedad</td> | |||
| <td align="center">Gran variedad</td> | |||
| <td align="center">Gran variedad</td> | |||
| <td align="center">Solo OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Motor RAG</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agente</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Flujo de trabajo</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Observabilidad</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Característica empresarial (SSO/Control de acceso)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Implementación local</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Usando Dify | |||
| - **Nube </br>** | |||
| @@ -79,74 +79,6 @@ Surveillez et analysez les journaux d'application et les performances au fil du | |||
| **7. Backend-as-a-Service** : | |||
| Toutes les offres de Dify sont accompagnées d'API correspondantes, vous permettant d'intégrer facilement Dify dans votre propre logique métier. | |||
| ## Comparaison des fonctionnalités | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Fonctionnalité</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Approche de programmation</td> | |||
| <td align="center">API + Application</td> | |||
| <td align="center">Code Python</td> | |||
| <td align="center">Application</td> | |||
| <td align="center">API</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">LLMs pris en charge</td> | |||
| <td align="center">Grande variété</td> | |||
| <td align="center">Grande variété</td> | |||
| <td align="center">Grande variété</td> | |||
| <td align="center">Uniquement OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Moteur RAG</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agent</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Flux de travail</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Observabilité</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Fonctionnalité d'entreprise (SSO/Contrôle d'accès)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Déploiement local</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Utiliser Dify | |||
| - **Cloud </br>** | |||
| @@ -80,74 +80,6 @@ LLM Function CallingやReActに基づくエージェントの定義が可能で | |||
| **7. Backend-as-a-Service**: | |||
| すべての機能はAPIを提供されており、Difyを自分のビジネスロジックに簡単に統合できます。 | |||
| ## 機能比較 | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">機能</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">プログラミングアプローチ</td> | |||
| <td align="center">API + アプリ指向</td> | |||
| <td align="center">Pythonコード</td> | |||
| <td align="center">アプリ指向</td> | |||
| <td align="center">API指向</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">サポートされているLLM</td> | |||
| <td align="center">バラエティ豊か</td> | |||
| <td align="center">バラエティ豊か</td> | |||
| <td align="center">バラエティ豊か</td> | |||
| <td align="center">OpenAIのみ</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAGエンジン</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">エージェント</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">ワークフロー</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">観測性</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">エンタープライズ機能(SSO/アクセス制御)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">ローカル展開</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Difyの使用方法 | |||
| - **クラウド </br>** | |||
| @@ -79,74 +79,6 @@ Monitor and analyze application logs and performance over time. You could contin | |||
| **7. Backend-as-a-Service**: | |||
| All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic. | |||
| ## Feature Comparison | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Feature</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Programming Approach</td> | |||
| <td align="center">API + App-oriented</td> | |||
| <td align="center">Python Code</td> | |||
| <td align="center">App-oriented</td> | |||
| <td align="center">API-oriented</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Supported LLMs</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">Rich Variety</td> | |||
| <td align="center">OpenAI-only</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG Engine</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agent</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Workflow</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Observability</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Enterprise Feature (SSO/Access control)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Local Deployment</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Using Dify | |||
| - **Cloud </br>** | |||
| @@ -73,74 +73,6 @@ LLM 함수 호출 또는 ReAct를 기반으로 에이전트를 정의하고 에 | |||
| **7. Backend-as-a-Service**: | |||
| Dify의 모든 제품에는 해당 API가 함께 제공되므로 Dify를 자신의 비즈니스 로직에 쉽게 통합할 수 있습니다. | |||
| ## 기능 비교 | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">기능</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">프로그래밍 접근 방식</td> | |||
| <td align="center">API + 앱 중심</td> | |||
| <td align="center">Python 코드</td> | |||
| <td align="center">앱 중심</td> | |||
| <td align="center">API 중심</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">지원되는 LLMs</td> | |||
| <td align="center">다양한 종류</td> | |||
| <td align="center">다양한 종류</td> | |||
| <td align="center">다양한 종류</td> | |||
| <td align="center">OpenAI 전용</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG 엔진</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">에이전트</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">워크플로우</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">가시성</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">기업용 기능 (SSO/접근 제어)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">로컬 배포</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Dify 사용하기 | |||
| - **클라우드 </br>** | |||
| @@ -79,74 +79,6 @@ Monitore e analise os registros e o desempenho do aplicativo ao longo do tempo. | |||
| **7. Backend como Serviço**: | |||
| Todas os recursos do Dify vêm com APIs correspondentes, permitindo que você integre o Dify sem esforço na lógica de negócios da sua empresa. | |||
| ## Comparação de recursos | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Recurso</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Abordagem de Programação</td> | |||
| <td align="center">Orientada a API + Aplicativo</td> | |||
| <td align="center">Código Python</td> | |||
| <td align="center">Orientada a Aplicativo</td> | |||
| <td align="center">Orientada a API</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">LLMs Suportados</td> | |||
| <td align="center">Variedade Rica</td> | |||
| <td align="center">Variedade Rica</td> | |||
| <td align="center">Variedade Rica</td> | |||
| <td align="center">Apenas OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG Engine</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agente</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Workflow</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Observabilidade</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Recursos Empresariais (SSO/Controle de Acesso)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Implantação Local</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Usando o Dify | |||
| - **Nuvem </br>** | |||
| @@ -103,74 +103,6 @@ Spremljajte in analizirajte dnevnike aplikacij in učinkovitost skozi čas. Pozi | |||
| **7. Backend-as-a-Service**: | |||
| AVse ponudbe Difyja so opremljene z ustreznimi API-ji, tako da lahko Dify brez težav integrirate v svojo poslovno logiko. | |||
| ## Primerjava Funkcij | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Funkcija</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Programski pristop</td> | |||
| <td align="center">API + usmerjeno v aplikacije</td> | |||
| <td align="center">Python koda</td> | |||
| <td align="center">Usmerjeno v aplikacije</td> | |||
| <td align="center">Usmerjeno v API</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Podprti LLM-ji</td> | |||
| <td align="center">Bogata izbira</td> | |||
| <td align="center">Bogata izbira</td> | |||
| <td align="center">Bogata izbira</td> | |||
| <td align="center">Samo OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG pogon</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agent</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Potek dela</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Spremljanje</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Funkcija za podjetja (SSO/nadzor dostopa)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Lokalna namestitev</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Uporaba Dify | |||
| - **Cloud </br>** | |||
| @@ -74,74 +74,6 @@ Uygulama loglarını ve performans metriklerini zaman içinde izleme ve analiz e | |||
| **7. Hizmet Olarak Backend**: | |||
| Dify'ın tüm özellikleri ilgili API'lerle birlikte gelir, böylece Dify'ı kendi iş mantığınıza kolayca entegre edebilirsiniz. | |||
| ## Özellik karşılaştırması | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Özellik</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Programlama Yaklaşımı</td> | |||
| <td align="center">API + Uygulama odaklı</td> | |||
| <td align="center">Python Kodu</td> | |||
| <td align="center">Uygulama odaklı</td> | |||
| <td align="center">API odaklı</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Desteklenen LLM'ler</td> | |||
| <td align="center">Zengin Çeşitlilik</td> | |||
| <td align="center">Zengin Çeşitlilik</td> | |||
| <td align="center">Zengin Çeşitlilik</td> | |||
| <td align="center">Yalnızca OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG Motoru</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Ajan</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">İş Akışı</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Gözlemlenebilirlik</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Kurumsal Özellikler (SSO/Erişim kontrolü)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Yerel Dağıtım</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Dify'ı Kullanma | |||
| - **Cloud </br>** | |||
| @@ -106,74 +106,6 @@ docker compose up -d | |||
| **7. 後端即服務**: | |||
| Dify 的所有功能都提供相應的 API,因此您可以輕鬆地將 Dify 整合到您自己的業務邏輯中。 | |||
| ## 功能比較 | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">功能</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">程式設計方法</td> | |||
| <td align="center">API + 應用導向</td> | |||
| <td align="center">Python 代碼</td> | |||
| <td align="center">應用導向</td> | |||
| <td align="center">API 導向</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">支援的 LLM 模型</td> | |||
| <td align="center">豐富多樣</td> | |||
| <td align="center">豐富多樣</td> | |||
| <td align="center">豐富多樣</td> | |||
| <td align="center">僅限 OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG 引擎</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">代理功能</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">工作流程</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">可觀察性</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">企業級功能 (SSO/存取控制)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">本地部署</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## 使用 Dify | |||
| - **雲端服務 </br>** | |||
| @@ -74,74 +74,6 @@ Giám sát và phân tích nhật ký và hiệu suất ứng dụng theo thời | |||
| **7. Backend-as-a-Service**: | |||
| Tất cả các dịch vụ của Dify đều đi kèm với các API tương ứng, vì vậy bạn có thể dễ dàng tích hợp Dify vào logic kinh doanh của riêng mình. | |||
| ## So sánh tính năng | |||
| <table style="width: 100%;"> | |||
| <tr> | |||
| <th align="center">Tính năng</th> | |||
| <th align="center">Dify.AI</th> | |||
| <th align="center">LangChain</th> | |||
| <th align="center">Flowise</th> | |||
| <th align="center">OpenAI Assistants API</th> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Phương pháp lập trình</td> | |||
| <td align="center">Hướng API + Ứng dụng</td> | |||
| <td align="center">Mã Python</td> | |||
| <td align="center">Hướng ứng dụng</td> | |||
| <td align="center">Hướng API</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">LLMs được hỗ trợ</td> | |||
| <td align="center">Đa dạng phong phú</td> | |||
| <td align="center">Đa dạng phong phú</td> | |||
| <td align="center">Đa dạng phong phú</td> | |||
| <td align="center">Chỉ OpenAI</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">RAG Engine</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Agent</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Quy trình làm việc</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Khả năng quan sát</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Tính năng doanh nghiệp (SSO/Kiểm soát truy cập)</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| <tr> | |||
| <td align="center">Triển khai cục bộ</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">✅</td> | |||
| <td align="center">❌</td> | |||
| </tr> | |||
| </table> | |||
| ## Sử dụng Dify | |||
| - **Cloud </br>** | |||
| @@ -80,7 +80,7 @@ | |||
| 1. If you need to handle and debug the async tasks (e.g. dataset importing and documents indexing), please start the worker service. | |||
| ```bash | |||
| uv run celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion,plugin,workflow_storage | |||
| uv run celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation | |||
| ``` | |||
| Addition, if you want to debug the celery scheduled tasks, you can use the following command in another terminal: | |||
| @@ -97,8 +97,16 @@ uv run celery -A app.celery beat | |||
| uv sync --dev | |||
| ``` | |||
| 1. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml` | |||
| 1. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml`, more can check [Claude.md](../CLAUDE.md) | |||
| ```bash | |||
| uv run -P api bash dev/pytest/pytest_all_tests.sh | |||
| ```cli | |||
| uv run --project api pytest # Run all tests | |||
| uv run --project api pytest tests/unit_tests/ # Unit tests only | |||
| uv run --project api pytest tests/integration_tests/ # Integration tests | |||
| # Code quality | |||
| ./dev/reformat # Run all formatters and linters | |||
| uv run --project api ruff check --fix ./ # Fix linting issues | |||
| uv run --project api ruff format ./ # Format code | |||
| uv run --project api mypy . # Type checking | |||
| ``` | |||
| @@ -5,6 +5,8 @@ from configs import dify_config | |||
| from contexts.wrapper import RecyclableContextVar | |||
| from dify_app import DifyApp | |||
| logger = logging.getLogger(__name__) | |||
| # ---------------------------- | |||
| # Application Factory Function | |||
| @@ -32,7 +34,7 @@ def create_app() -> DifyApp: | |||
| initialize_extensions(app) | |||
| end_time = time.perf_counter() | |||
| if dify_config.DEBUG: | |||
| logging.info("Finished create_app (%s ms)", round((end_time - start_time) * 1000, 2)) | |||
| logger.info("Finished create_app (%s ms)", round((end_time - start_time) * 1000, 2)) | |||
| return app | |||
| @@ -93,14 +95,14 @@ def initialize_extensions(app: DifyApp): | |||
| is_enabled = ext.is_enabled() if hasattr(ext, "is_enabled") else True | |||
| if not is_enabled: | |||
| if dify_config.DEBUG: | |||
| logging.info("Skipped %s", short_name) | |||
| logger.info("Skipped %s", short_name) | |||
| continue | |||
| start_time = time.perf_counter() | |||
| ext.init_app(app) | |||
| end_time = time.perf_counter() | |||
| if dify_config.DEBUG: | |||
| logging.info("Loaded %s (%s ms)", short_name, round((end_time - start_time) * 1000, 2)) | |||
| logger.info("Loaded %s (%s ms)", short_name, round((end_time - start_time) * 1000, 2)) | |||
| def create_migrations_app(): | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import fields | |||
| from flask_restx import Api, Namespace, fields | |||
| from libs.helper import AppIconUrlField | |||
| @@ -10,6 +10,12 @@ parameters__system_parameters = { | |||
| "workflow_file_upload_limit": fields.Integer, | |||
| } | |||
| def build_system_parameters_model(api_or_ns: Api | Namespace): | |||
| """Build the system parameters model for the API or Namespace.""" | |||
| return api_or_ns.model("SystemParameters", parameters__system_parameters) | |||
| parameters_fields = { | |||
| "opening_statement": fields.String, | |||
| "suggested_questions": fields.Raw, | |||
| @@ -25,6 +31,14 @@ parameters_fields = { | |||
| "system_parameters": fields.Nested(parameters__system_parameters), | |||
| } | |||
| def build_parameters_model(api_or_ns: Api | Namespace): | |||
| """Build the parameters model for the API or Namespace.""" | |||
| copied_fields = parameters_fields.copy() | |||
| copied_fields["system_parameters"] = fields.Nested(build_system_parameters_model(api_or_ns)) | |||
| return api_or_ns.model("Parameters", copied_fields) | |||
| site_fields = { | |||
| "title": fields.String, | |||
| "chat_color_theme": fields.String, | |||
| @@ -41,3 +55,8 @@ site_fields = { | |||
| "show_workflow_steps": fields.Boolean, | |||
| "use_icon_as_answer_icon": fields.Boolean, | |||
| } | |||
| def build_site_model(api_or_ns: Api | Namespace): | |||
| """Build the site model for the API or Namespace.""" | |||
| return api_or_ns.model("Site", site_fields) | |||
| @@ -84,7 +84,6 @@ from .datasets import ( | |||
| external, | |||
| hit_testing, | |||
| metadata, | |||
| upload_file, | |||
| website, | |||
| ) | |||
| from .datasets.rag_pipeline import ( | |||
| @@ -1,7 +1,7 @@ | |||
| from functools import wraps | |||
| from flask import request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import NotFound, Unauthorized | |||
| @@ -1,8 +1,8 @@ | |||
| from typing import Any, Optional | |||
| import flask_restful | |||
| import flask_restx | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, fields, marshal_with | |||
| from flask_restx import Resource, fields, marshal_with | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import Forbidden | |||
| @@ -40,7 +40,7 @@ def _get_resource(resource_id, tenant_id, resource_model): | |||
| ).scalar_one_or_none() | |||
| if resource is None: | |||
| flask_restful.abort(404, message=f"{resource_model.__name__} not found.") | |||
| flask_restx.abort(404, message=f"{resource_model.__name__} not found.") | |||
| return resource | |||
| @@ -81,7 +81,7 @@ class BaseApiKeyListResource(Resource): | |||
| ) | |||
| if current_key_count >= self.max_keys: | |||
| flask_restful.abort( | |||
| flask_restx.abort( | |||
| 400, | |||
| message=f"Cannot create more than {self.max_keys} API keys for this resource type.", | |||
| code="max_keys_exceeded", | |||
| @@ -126,7 +126,7 @@ class BaseApiKeyResource(Resource): | |||
| ) | |||
| if key is None: | |||
| flask_restful.abort(404, message="API key not found") | |||
| flask_restx.abort(404, message="API key not found") | |||
| db.session.query(ApiToken).where(ApiToken.id == api_key_id).delete() | |||
| db.session.commit() | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console import api | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console import api | |||
| from controllers.console.app.wraps import get_app_model | |||
| @@ -2,7 +2,7 @@ from typing import Literal | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal, marshal_with, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.common.errors import NoFileUploadedError, TooManyFilesError | |||
| @@ -2,7 +2,7 @@ import uuid | |||
| from typing import cast | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, inputs, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, inputs, marshal, marshal_with, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import BadRequest, Forbidden, abort | |||
| @@ -1,7 +1,7 @@ | |||
| from typing import cast | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import Forbidden | |||
| @@ -1,7 +1,7 @@ | |||
| import logging | |||
| from flask import request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import InternalServerError | |||
| import services | |||
| @@ -2,7 +2,7 @@ import logging | |||
| import flask_login | |||
| from flask import request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import InternalServerError, NotFound | |||
| import services | |||
| @@ -2,8 +2,8 @@ from datetime import datetime | |||
| import pytz # pip install pytz | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restful.inputs import int_range | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from flask_restx.inputs import int_range | |||
| from sqlalchemy import func, or_ | |||
| from sqlalchemy.orm import joinedload | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| @@ -24,6 +24,8 @@ from libs.helper import DatetimeString | |||
| from libs.login import login_required | |||
| from models import Conversation, EndUser, Message, MessageAnnotation | |||
| from models.model import AppMode | |||
| from services.conversation_service import ConversationService | |||
| from services.errors.conversation import ConversationNotExistsError | |||
| class CompletionConversationApi(Resource): | |||
| @@ -46,7 +48,9 @@ class CompletionConversationApi(Resource): | |||
| parser.add_argument("limit", type=int_range(1, 100), default=20, location="args") | |||
| args = parser.parse_args() | |||
| query = db.select(Conversation).where(Conversation.app_id == app_model.id, Conversation.mode == "completion") | |||
| query = db.select(Conversation).where( | |||
| Conversation.app_id == app_model.id, Conversation.mode == "completion", Conversation.is_deleted.is_(False) | |||
| ) | |||
| if args["keyword"]: | |||
| query = query.join(Message, Message.conversation_id == Conversation.id).where( | |||
| @@ -119,18 +123,11 @@ class CompletionConversationDetailApi(Resource): | |||
| raise Forbidden() | |||
| conversation_id = str(conversation_id) | |||
| conversation = ( | |||
| db.session.query(Conversation) | |||
| .where(Conversation.id == conversation_id, Conversation.app_id == app_model.id) | |||
| .first() | |||
| ) | |||
| if not conversation: | |||
| try: | |||
| ConversationService.delete(app_model, conversation_id, current_user) | |||
| except ConversationNotExistsError: | |||
| raise NotFound("Conversation Not Exists.") | |||
| conversation.is_deleted = True | |||
| db.session.commit() | |||
| return {"result": "success"}, 204 | |||
| @@ -171,7 +168,7 @@ class ChatConversationApi(Resource): | |||
| .subquery() | |||
| ) | |||
| query = db.select(Conversation).where(Conversation.app_id == app_model.id) | |||
| query = db.select(Conversation).where(Conversation.app_id == app_model.id, Conversation.is_deleted.is_(False)) | |||
| if args["keyword"]: | |||
| keyword_filter = f"%{args['keyword']}%" | |||
| @@ -284,18 +281,11 @@ class ChatConversationDetailApi(Resource): | |||
| raise Forbidden() | |||
| conversation_id = str(conversation_id) | |||
| conversation = ( | |||
| db.session.query(Conversation) | |||
| .where(Conversation.id == conversation_id, Conversation.app_id == app_model.id) | |||
| .first() | |||
| ) | |||
| if not conversation: | |||
| try: | |||
| ConversationService.delete(app_model, conversation_id, current_user) | |||
| except ConversationNotExistsError: | |||
| raise NotFound("Conversation Not Exists.") | |||
| conversation.is_deleted = True | |||
| db.session.commit() | |||
| return {"result": "success"}, 204 | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| @@ -1,7 +1,7 @@ | |||
| from collections.abc import Sequence | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console import api | |||
| from controllers.console.app.error import ( | |||
| @@ -2,7 +2,7 @@ import json | |||
| from enum import StrEnum | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from werkzeug.exceptions import NotFound | |||
| from controllers.console import api | |||
| @@ -1,8 +1,8 @@ | |||
| import logging | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, fields, marshal_with, reqparse | |||
| from flask_restful.inputs import int_range | |||
| from flask_restx import Resource, fields, marshal_with, reqparse | |||
| from flask_restx.inputs import int_range | |||
| from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | |||
| from controllers.console import api | |||
| @@ -3,7 +3,7 @@ from typing import cast | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from controllers.console import api | |||
| from controllers.console.app.wraps import get_app_model | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import BadRequest | |||
| from controllers.console import api | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| from constants.languages import supported_language | |||
| @@ -5,7 +5,7 @@ import pytz | |||
| import sqlalchemy as sa | |||
| from flask import jsonify | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console import api | |||
| from controllers.console.app.wraps import get_app_model | |||
| @@ -4,7 +4,7 @@ from collections.abc import Sequence | |||
| from typing import cast | |||
| from flask import abort, request | |||
| from flask_restful import Resource, inputs, marshal_with, reqparse | |||
| from flask_restx import Resource, inputs, marshal_with, reqparse | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | |||
| @@ -1,6 +1,6 @@ | |||
| from dateutil.parser import isoparse | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restful.inputs import int_range | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from flask_restx.inputs import int_range | |||
| from sqlalchemy.orm import Session | |||
| from controllers.console import api | |||
| @@ -2,7 +2,7 @@ import logging | |||
| from typing import Any, NoReturn | |||
| from flask import Response | |||
| from flask_restful import Resource, fields, inputs, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, inputs, marshal, marshal_with, reqparse | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import Forbidden | |||
| @@ -1,8 +1,8 @@ | |||
| from typing import cast | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restful.inputs import int_range | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from flask_restx.inputs import int_range | |||
| from controllers.console import api | |||
| from controllers.console.app.wraps import get_app_model | |||
| @@ -5,7 +5,7 @@ import pytz | |||
| import sqlalchemy as sa | |||
| from flask import jsonify | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console import api | |||
| from controllers.console.app.wraps import get_app_model | |||
| @@ -1,5 +1,5 @@ | |||
| from flask import request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from constants.languages import supported_language | |||
| from controllers.console import api | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.console import api | |||
| @@ -3,7 +3,7 @@ import logging | |||
| import requests | |||
| from flask import current_app, redirect, request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from werkzeug.exceptions import Forbidden | |||
| from configs import dify_config | |||
| @@ -2,7 +2,7 @@ import base64 | |||
| import secrets | |||
| from flask import request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| @@ -2,7 +2,7 @@ from typing import cast | |||
| import flask_login | |||
| from flask import request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| import services | |||
| from configs import dify_config | |||
| @@ -221,7 +221,7 @@ class EmailCodeLoginApi(Resource): | |||
| email=user_email, name=user_email, interface_language=languages[0] | |||
| ) | |||
| except WorkSpaceNotAllowedCreateError: | |||
| return NotAllowedCreateWorkspace() | |||
| raise NotAllowedCreateWorkspace() | |||
| except AccountRegisterError as are: | |||
| raise AccountInFreezeError() | |||
| except WorkspacesLimitExceededError: | |||
| @@ -3,7 +3,7 @@ from typing import Optional | |||
| import requests | |||
| from flask import current_app, redirect, request | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import Unauthorized | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console import api | |||
| from controllers.console.wraps import account_initialization_required, only_edition_cloud, setup_required | |||
| @@ -1,6 +1,6 @@ | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from libs.helper import extract_remote_ip | |||
| from libs.login import login_required | |||
| @@ -4,7 +4,7 @@ from typing import cast | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import NotFound | |||
| @@ -1,7 +1,7 @@ | |||
| import flask_restful | |||
| import flask_restx | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal, marshal_with, reqparse | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| import services | |||
| @@ -600,7 +600,7 @@ class DatasetApiKeyApi(Resource): | |||
| ) | |||
| if current_key_count >= self.max_keys: | |||
| flask_restful.abort( | |||
| flask_restx.abort( | |||
| 400, | |||
| message=f"Cannot create more than {self.max_keys} API keys for this resource type.", | |||
| code="max_keys_exceeded", | |||
| @@ -640,7 +640,7 @@ class DatasetApiDeleteApi(Resource): | |||
| ) | |||
| if key is None: | |||
| flask_restful.abort(404, message="API key not found") | |||
| flask_restx.abort(404, message="API key not found") | |||
| db.session.query(ApiToken).where(ApiToken.id == api_key_id).delete() | |||
| db.session.commit() | |||
| @@ -5,7 +5,7 @@ from typing import Literal, cast | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal, marshal_with, reqparse | |||
| from sqlalchemy import asc, desc, select | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| @@ -2,7 +2,7 @@ import uuid | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal, reqparse | |||
| from flask_restx import Resource, marshal, reqparse | |||
| from sqlalchemy import select | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| @@ -584,7 +584,12 @@ class ChildChunkUpdateApi(Resource): | |||
| child_chunk_id = str(child_chunk_id) | |||
| child_chunk = ( | |||
| db.session.query(ChildChunk) | |||
| .where(ChildChunk.id == str(child_chunk_id), ChildChunk.tenant_id == current_user.current_tenant_id) | |||
| .where( | |||
| ChildChunk.id == str(child_chunk_id), | |||
| ChildChunk.tenant_id == current_user.current_tenant_id, | |||
| ChildChunk.segment_id == segment.id, | |||
| ChildChunk.document_id == document_id, | |||
| ) | |||
| .first() | |||
| ) | |||
| if not child_chunk: | |||
| @@ -633,7 +638,12 @@ class ChildChunkUpdateApi(Resource): | |||
| child_chunk_id = str(child_chunk_id) | |||
| child_chunk = ( | |||
| db.session.query(ChildChunk) | |||
| .where(ChildChunk.id == str(child_chunk_id), ChildChunk.tenant_id == current_user.current_tenant_id) | |||
| .where( | |||
| ChildChunk.id == str(child_chunk_id), | |||
| ChildChunk.tenant_id == current_user.current_tenant_id, | |||
| ChildChunk.segment_id == segment.id, | |||
| ChildChunk.document_id == document_id, | |||
| ) | |||
| .first() | |||
| ) | |||
| if not child_chunk: | |||
| @@ -1,6 +1,6 @@ | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal, reqparse | |||
| from flask_restx import Resource, marshal, reqparse | |||
| from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | |||
| import services | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from controllers.console import api | |||
| from controllers.console.datasets.hit_testing_base import DatasetsHitTestingBase | |||
| @@ -1,7 +1,7 @@ | |||
| import logging | |||
| from flask_login import current_user | |||
| from flask_restful import marshal, reqparse | |||
| from flask_restx import marshal, reqparse | |||
| from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | |||
| import services.dataset_service | |||
| @@ -1,7 +1,7 @@ | |||
| from typing import Literal | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from werkzeug.exceptions import NotFound | |||
| from controllers.console import api | |||
| @@ -1,62 +0,0 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource | |||
| from werkzeug.exceptions import NotFound | |||
| from controllers.console import api | |||
| from controllers.console.wraps import ( | |||
| account_initialization_required, | |||
| setup_required, | |||
| ) | |||
| from core.file import helpers as file_helpers | |||
| from extensions.ext_database import db | |||
| from models.dataset import Dataset | |||
| from models.model import UploadFile | |||
| from services.dataset_service import DocumentService | |||
| class UploadFileApi(Resource): | |||
| @setup_required | |||
| @account_initialization_required | |||
| def get(self, dataset_id, document_id): | |||
| """Get upload file.""" | |||
| # check dataset | |||
| dataset_id = str(dataset_id) | |||
| dataset = ( | |||
| db.session.query(Dataset) | |||
| .filter(Dataset.tenant_id == current_user.current_tenant_id, Dataset.id == dataset_id) | |||
| .first() | |||
| ) | |||
| if not dataset: | |||
| raise NotFound("Dataset not found.") | |||
| # check document | |||
| document_id = str(document_id) | |||
| document = DocumentService.get_document(dataset.id, document_id) | |||
| if not document: | |||
| raise NotFound("Document not found.") | |||
| # check upload file | |||
| if document.data_source_type != "upload_file": | |||
| raise ValueError(f"Document data source type ({document.data_source_type}) is not upload_file.") | |||
| data_source_info = document.data_source_info_dict | |||
| if data_source_info and "upload_file_id" in data_source_info: | |||
| file_id = data_source_info["upload_file_id"] | |||
| upload_file = db.session.query(UploadFile).where(UploadFile.id == file_id).first() | |||
| if not upload_file: | |||
| raise NotFound("UploadFile not found.") | |||
| else: | |||
| raise ValueError("Upload file id not found in document data source info.") | |||
| url = file_helpers.get_signed_file_url(upload_file_id=upload_file.id) | |||
| return { | |||
| "id": upload_file.id, | |||
| "name": upload_file.name, | |||
| "size": upload_file.size, | |||
| "extension": upload_file.extension, | |||
| "url": url, | |||
| "download_url": f"{url}&as_attachment=true", | |||
| "mime_type": upload_file.mime_type, | |||
| "created_by": upload_file.created_by, | |||
| "created_at": upload_file.created_at.timestamp(), | |||
| }, 200 | |||
| api.add_resource(UploadFileApi, "/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/upload-file") | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console import api | |||
| from controllers.console.datasets.error import WebsiteCrawlError | |||
| @@ -65,7 +65,7 @@ class ChatAudioApi(InstalledAppResource): | |||
| class ChatTextApi(InstalledAppResource): | |||
| def post(self, installed_app): | |||
| from flask_restful import reqparse | |||
| from flask_restx import reqparse | |||
| app_model = installed_app.app | |||
| try: | |||
| @@ -1,7 +1,7 @@ | |||
| import logging | |||
| from flask_login import current_user | |||
| from flask_restful import reqparse | |||
| from flask_restx import reqparse | |||
| from werkzeug.exceptions import InternalServerError, NotFound | |||
| import services | |||
| @@ -1,6 +1,6 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import marshal_with, reqparse | |||
| from flask_restful.inputs import int_range | |||
| from flask_restx import marshal_with, reqparse | |||
| from flask_restx.inputs import int_range | |||
| from sqlalchemy.orm import Session | |||
| from werkzeug.exceptions import NotFound | |||
| @@ -3,7 +3,7 @@ from typing import Any | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, inputs, marshal_with, reqparse | |||
| from flask_restx import Resource, inputs, marshal_with, reqparse | |||
| from sqlalchemy import and_ | |||
| from werkzeug.exceptions import BadRequest, Forbidden, NotFound | |||
| @@ -1,8 +1,8 @@ | |||
| import logging | |||
| from flask_login import current_user | |||
| from flask_restful import marshal_with, reqparse | |||
| from flask_restful.inputs import int_range | |||
| from flask_restx import marshal_with, reqparse | |||
| from flask_restx.inputs import int_range | |||
| from werkzeug.exceptions import InternalServerError, NotFound | |||
| from controllers.console.app.error import ( | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import marshal_with | |||
| from flask_restx import marshal_with | |||
| from controllers.common import fields | |||
| from controllers.console import api | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, fields, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, marshal_with, reqparse | |||
| from constants.languages import languages | |||
| from controllers.console import api | |||
| @@ -1,6 +1,6 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import fields, marshal_with, reqparse | |||
| from flask_restful.inputs import int_range | |||
| from flask_restx import fields, marshal_with, reqparse | |||
| from flask_restx.inputs import int_range | |||
| from werkzeug.exceptions import NotFound | |||
| from controllers.console import api | |||
| @@ -1,6 +1,6 @@ | |||
| import logging | |||
| from flask_restful import reqparse | |||
| from flask_restx import reqparse | |||
| from werkzeug.exceptions import InternalServerError | |||
| from controllers.console.app.error import ( | |||
| @@ -1,7 +1,7 @@ | |||
| from functools import wraps | |||
| from flask_login import current_user | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from werkzeug.exceptions import NotFound | |||
| from controllers.console.explore.error import AppAccessDeniedError | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from constants import HIDDEN_VALUE | |||
| from controllers.console import api | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from libs.login import login_required | |||
| from services.feature_service import FeatureService | |||
| @@ -2,7 +2,7 @@ from typing import Literal | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with | |||
| from flask_restx import Resource, marshal_with | |||
| from werkzeug.exceptions import Forbidden | |||
| import services | |||
| @@ -1,7 +1,7 @@ | |||
| import os | |||
| from flask import session | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from controllers.console import api | |||
| @@ -3,7 +3,7 @@ from typing import cast | |||
| import httpx | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| import services | |||
| from controllers.common import helpers | |||
| @@ -1,5 +1,5 @@ | |||
| from flask import request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from configs import dify_config | |||
| from libs.helper import StrLen, email, extract_remote_ip | |||
| @@ -1,11 +1,11 @@ | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, marshal_with, reqparse | |||
| from flask_restx import Resource, marshal_with, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.console import api | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| from fields.tag_fields import tag_fields | |||
| from fields.tag_fields import dataset_tag_fields | |||
| from libs.login import login_required | |||
| from models.model import Tag | |||
| from services.tag_service import TagService | |||
| @@ -21,7 +21,7 @@ class TagListApi(Resource): | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @marshal_with(tag_fields) | |||
| @marshal_with(dataset_tag_fields) | |||
| def get(self): | |||
| tag_type = request.args.get("type", type=str, default="") | |||
| keyword = request.args.get("keyword", default=None, type=str) | |||
| @@ -2,7 +2,7 @@ import json | |||
| import logging | |||
| import requests | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from packaging import version | |||
| from configs import dify_config | |||
| @@ -3,7 +3,7 @@ from datetime import datetime | |||
| import pytz | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, fields, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, marshal_with, reqparse | |||
| from sqlalchemy import select | |||
| from sqlalchemy.orm import Session | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from controllers.console import api | |||
| from controllers.console.wraps import account_initialization_required, setup_required | |||
| @@ -1,5 +1,5 @@ | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.console import api | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.console import api | |||
| @@ -2,7 +2,7 @@ from urllib import parse | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, abort, marshal_with, reqparse | |||
| from flask_restx import Resource, abort, marshal_with, reqparse | |||
| import services | |||
| from configs import dify_config | |||
| @@ -2,7 +2,7 @@ import io | |||
| from flask import send_file | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.console import api | |||
| @@ -1,7 +1,7 @@ | |||
| import logging | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from controllers.console import api | |||
| @@ -2,7 +2,7 @@ import io | |||
| from flask import request, send_file | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import Forbidden | |||
| from configs import dify_config | |||
| @@ -3,7 +3,7 @@ from urllib.parse import urlparse | |||
| from flask import make_response, redirect, request, send_file | |||
| from flask_login import current_user | |||
| from flask_restful import ( | |||
| from flask_restx import ( | |||
| Resource, | |||
| reqparse, | |||
| ) | |||
| @@ -2,7 +2,7 @@ import logging | |||
| from flask import request | |||
| from flask_login import current_user | |||
| from flask_restful import Resource, fields, inputs, marshal, marshal_with, reqparse | |||
| from flask_restx import Resource, fields, inputs, marshal, marshal_with, reqparse | |||
| from sqlalchemy import select | |||
| from werkzeug.exceptions import Unauthorized | |||
| @@ -1,9 +1,20 @@ | |||
| from flask import Blueprint | |||
| from flask_restx import Namespace | |||
| from libs.external_api import ExternalApi | |||
| bp = Blueprint("files", __name__) | |||
| api = ExternalApi(bp) | |||
| bp = Blueprint("files", __name__, url_prefix="/files") | |||
| api = ExternalApi( | |||
| bp, | |||
| version="1.0", | |||
| title="Files API", | |||
| description="API for file operations including upload and preview", | |||
| doc="/docs", # Enable Swagger UI at /files/docs | |||
| ) | |||
| files_ns = Namespace("files", description="File operations", path="/") | |||
| from . import image_preview, tool_files, upload | |||
| api.add_namespace(files_ns) | |||
| @@ -1,16 +1,17 @@ | |||
| from urllib.parse import quote | |||
| from flask import Response, request | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import NotFound | |||
| import services | |||
| from controllers.common.errors import UnsupportedFileTypeError | |||
| from controllers.files import api | |||
| from controllers.files import files_ns | |||
| from services.account_service import TenantService | |||
| from services.file_service import FileService | |||
| @files_ns.route("/<uuid:file_id>/image-preview") | |||
| class ImagePreviewApi(Resource): | |||
| """ | |||
| Deprecated | |||
| @@ -39,6 +40,7 @@ class ImagePreviewApi(Resource): | |||
| return Response(generator, mimetype=mimetype) | |||
| @files_ns.route("/<uuid:file_id>/file-preview") | |||
| class FilePreviewApi(Resource): | |||
| def get(self, file_id): | |||
| file_id = str(file_id) | |||
| @@ -94,6 +96,7 @@ class FilePreviewApi(Resource): | |||
| return response | |||
| @files_ns.route("/workspaces/<uuid:workspace_id>/webapp-logo") | |||
| class WorkspaceWebappLogoApi(Resource): | |||
| def get(self, workspace_id): | |||
| workspace_id = str(workspace_id) | |||
| @@ -112,8 +115,3 @@ class WorkspaceWebappLogoApi(Resource): | |||
| raise UnsupportedFileTypeError() | |||
| return Response(generator, mimetype=mimetype) | |||
| api.add_resource(ImagePreviewApi, "/files/<uuid:file_id>/image-preview") | |||
| api.add_resource(FilePreviewApi, "/files/<uuid:file_id>/file-preview") | |||
| api.add_resource(WorkspaceWebappLogoApi, "/files/workspaces/<uuid:workspace_id>/webapp-logo") | |||
| @@ -1,17 +1,18 @@ | |||
| from urllib.parse import quote | |||
| from flask import Response | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from werkzeug.exceptions import Forbidden, NotFound | |||
| from controllers.common.errors import UnsupportedFileTypeError | |||
| from controllers.files import api | |||
| from controllers.files import files_ns | |||
| from core.tools.signature import verify_tool_file_signature | |||
| from core.tools.tool_file_manager import ToolFileManager | |||
| from models import db as global_db | |||
| class ToolFilePreviewApi(Resource): | |||
| @files_ns.route("/tools/<uuid:file_id>.<string:extension>") | |||
| class ToolFileApi(Resource): | |||
| def get(self, file_id, extension): | |||
| file_id = str(file_id) | |||
| @@ -52,6 +53,3 @@ class ToolFilePreviewApi(Resource): | |||
| response.headers["Content-Disposition"] = f"attachment; filename*=UTF-8''{encoded_filename}" | |||
| return response | |||
| api.add_resource(ToolFilePreviewApi, "/files/tools/<uuid:file_id>.<string:extension>") | |||
| @@ -1,7 +1,9 @@ | |||
| from mimetypes import guess_extension | |||
| from typing import Optional | |||
| from flask import request | |||
| from flask_restful import Resource, marshal_with | |||
| from flask_restx import Resource, reqparse | |||
| from flask_restx.api import HTTPStatus | |||
| from werkzeug.datastructures import FileStorage | |||
| from werkzeug.exceptions import Forbidden | |||
| import services | |||
| @@ -10,39 +12,76 @@ from controllers.common.errors import ( | |||
| UnsupportedFileTypeError, | |||
| ) | |||
| from controllers.console.wraps import setup_required | |||
| from controllers.files import api | |||
| from controllers.files import files_ns | |||
| from controllers.inner_api.plugin.wraps import get_user | |||
| from core.file.helpers import verify_plugin_file_signature | |||
| from core.tools.tool_file_manager import ToolFileManager | |||
| from fields.file_fields import file_fields | |||
| from fields.file_fields import build_file_model | |||
| # Define parser for both documentation and validation | |||
| upload_parser = reqparse.RequestParser() | |||
| upload_parser.add_argument("file", location="files", type=FileStorage, required=True, help="File to upload") | |||
| upload_parser.add_argument( | |||
| "timestamp", type=str, required=True, location="args", help="Unix timestamp for signature verification" | |||
| ) | |||
| upload_parser.add_argument( | |||
| "nonce", type=str, required=True, location="args", help="Random string for signature verification" | |||
| ) | |||
| upload_parser.add_argument( | |||
| "sign", type=str, required=True, location="args", help="HMAC signature for request validation" | |||
| ) | |||
| upload_parser.add_argument("tenant_id", type=str, required=True, location="args", help="Tenant identifier") | |||
| upload_parser.add_argument("user_id", type=str, required=False, location="args", help="User identifier") | |||
| @files_ns.route("/upload/for-plugin") | |||
| class PluginUploadFileApi(Resource): | |||
| @setup_required | |||
| @marshal_with(file_fields) | |||
| @files_ns.expect(upload_parser) | |||
| @files_ns.doc("upload_plugin_file") | |||
| @files_ns.doc(description="Upload a file for plugin usage with signature verification") | |||
| @files_ns.doc( | |||
| responses={ | |||
| 201: "File uploaded successfully", | |||
| 400: "Invalid request parameters", | |||
| 403: "Forbidden - Invalid signature or missing parameters", | |||
| 413: "File too large", | |||
| 415: "Unsupported file type", | |||
| } | |||
| ) | |||
| @files_ns.marshal_with(build_file_model(files_ns), code=HTTPStatus.CREATED) | |||
| def post(self): | |||
| # get file from request | |||
| file = request.files["file"] | |||
| timestamp = request.args.get("timestamp") | |||
| nonce = request.args.get("nonce") | |||
| sign = request.args.get("sign") | |||
| tenant_id = request.args.get("tenant_id") | |||
| if not tenant_id: | |||
| raise Forbidden("Invalid request.") | |||
| user_id = request.args.get("user_id") | |||
| """Upload a file for plugin usage. | |||
| Accepts a file upload with signature verification for security. | |||
| The file must be accompanied by valid timestamp, nonce, and signature parameters. | |||
| Returns: | |||
| dict: File metadata including ID, URLs, and properties | |||
| int: HTTP status code (201 for success) | |||
| Raises: | |||
| Forbidden: Invalid signature or missing required parameters | |||
| FileTooLargeError: File exceeds size limit | |||
| UnsupportedFileTypeError: File type not supported | |||
| """ | |||
| # Parse and validate all arguments | |||
| args = upload_parser.parse_args() | |||
| file: FileStorage = args["file"] | |||
| timestamp: str = args["timestamp"] | |||
| nonce: str = args["nonce"] | |||
| sign: str = args["sign"] | |||
| tenant_id: str = args["tenant_id"] | |||
| user_id: Optional[str] = args.get("user_id") | |||
| user = get_user(tenant_id, user_id) | |||
| filename = file.filename | |||
| mimetype = file.mimetype | |||
| filename: Optional[str] = file.filename | |||
| mimetype: Optional[str] = file.mimetype | |||
| if not filename or not mimetype: | |||
| raise Forbidden("Invalid request.") | |||
| if not timestamp or not nonce or not sign: | |||
| raise Forbidden("Invalid request.") | |||
| if not verify_plugin_file_signature( | |||
| filename=filename, | |||
| mimetype=mimetype, | |||
| @@ -88,6 +127,3 @@ class PluginUploadFileApi(Resource): | |||
| raise FileTooLargeError(file_too_large_error.description) | |||
| except services.errors.file.UnsupportedFileTypeError: | |||
| raise UnsupportedFileTypeError() | |||
| api.add_resource(PluginUploadFileApi, "/files/upload/for-plugin") | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console.wraps import setup_required | |||
| from controllers.inner_api import api | |||
| @@ -1,4 +1,4 @@ | |||
| from flask_restful import Resource | |||
| from flask_restx import Resource | |||
| from controllers.console.wraps import setup_required | |||
| from controllers.inner_api import api | |||
| @@ -4,7 +4,7 @@ from typing import Optional | |||
| from flask import current_app, request | |||
| from flask_login import user_logged_in | |||
| from flask_restful import reqparse | |||
| from flask_restx import reqparse | |||
| from pydantic import BaseModel | |||
| from sqlalchemy.orm import Session | |||
| @@ -1,6 +1,6 @@ | |||
| import json | |||
| from flask_restful import Resource, reqparse | |||
| from flask_restx import Resource, reqparse | |||
| from controllers.console.wraps import setup_required | |||
| from controllers.inner_api import api | |||
| @@ -1,8 +1,20 @@ | |||
| from flask import Blueprint | |||
| from flask_restx import Namespace | |||
| from libs.external_api import ExternalApi | |||
| bp = Blueprint("mcp", __name__, url_prefix="/mcp") | |||
| api = ExternalApi(bp) | |||
| api = ExternalApi( | |||
| bp, | |||
| version="1.0", | |||
| title="MCP API", | |||
| description="API for Model Context Protocol operations", | |||
| doc="/docs", # Enable Swagger UI at /mcp/docs | |||
| ) | |||
| mcp_ns = Namespace("mcp", description="MCP operations", path="/") | |||
| from . import mcp | |||
| api.add_namespace(mcp_ns) | |||