diff --git a/.gitattributes b/.gitattributes index dcf74f06de..a10da53408 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Ensure that .sh scripts use LF as line separator, even if they are checked out -# to Windows(NTFS) file-system, by a user of Docker for Window. +# to Windows(NTFS) file-system, by a user of Docker for Windows. # These .sh scripts will be run from the Container after `docker compose up -d`. # If they appear to be CRLF style, Dash from the Container will fail to execute # them. diff --git a/README.md b/README.md index 41f571fe28..deb05fe07f 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Türkçe README README Tiếng Việt README in Deutsch + README in বাংলা

diff --git a/README_AR.md b/README_AR.md index ceb0be4b8f..ae21281afb 100644 --- a/README_AR.md +++ b/README_AR.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

@@ -53,8 +54,7 @@ **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). @@ -69,7 +69,9 @@ **6. الـ LLMOps**: راقب وتحلل سجلات التطبيق والأداء على مر الزمن. يمكنك تحسين الأوامر والبيانات والنماذج باستمرار استنادًا إلى البيانات الإنتاجية والتعليقات. **7.الواجهة الخلفية (Backend) كخدمة**: تأتي جميع عروض Dify مع APIs مطابقة، حتى يمكنك دمج Dify بسهولة في منطق أعمالك الخاص. + ## مقارنة الميزات + @@ -136,8 +138,8 @@
الميزة
- ## استخدام Dify + - **سحابة
** نحن نستضيف [خدمة Dify Cloud](https://dify.ai) لأي شخص لتجربتها بدون أي إعدادات. توفر كل قدرات النسخة التي تمت استضافتها ذاتيًا، وتتضمن 200 أمر GPT-4 مجانًا في خطة الصندوق الرملي. @@ -147,15 +149,19 @@ - **مشروع Dify للشركات / المؤسسات
** نحن نوفر ميزات إضافية مركزة على الشركات. [جدول اجتماع معنا](https://cal.com/guchenhe/30min) أو [أرسل لنا بريدًا إلكترونيًا](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry) لمناقشة احتياجات الشركات.
+ > بالنسبة للشركات الناشئة والشركات الصغيرة التي تستخدم خدمات AWS، تحقق من [Dify Premium على AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) ونشرها في شبكتك الخاصة على AWS VPC بنقرة واحدة. إنها عرض AMI بأسعار معقولة مع خيار إنشاء تطبيقات بشعار وعلامة تجارية مخصصة. +> ## البقاء قدمًا قم بإضافة نجمة إلى Dify على GitHub وتلق تنبيهًا فوريًا بالإصدارات الجديدة. ![نجمنا](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4) + ## البداية السريعة +> > قبل تثبيت Dify، تأكد من أن جهازك يلبي الحد الأدنى من متطلبات النظام التالية: -> +> >- معالج >= 2 نواة >- ذاكرة وصول عشوائي (RAM) >= 4 جيجابايت @@ -188,24 +194,26 @@ docker compose up -d انشر Dify إلى منصة السحابة بنقرة واحدة باستخدام [terraform](https://www.terraform.io/) ##### Azure Global + - [Azure Terraform بواسطة @nikawang](https://github.com/nikawang/dify-azure-terraform) ##### Google Cloud + - [Google Cloud Terraform بواسطة @sotazum](https://github.com/DeNA/dify-google-cloud-terraform) #### استخدام AWS CDK للنشر انشر Dify على AWS باستخدام [CDK](https://aws.amazon.com/cdk/) -##### AWS +##### AWS + - [AWS CDK بواسطة @KevinZhao](https://github.com/aws-samples/solution-for-deploying-dify-on-aws) ## المساهمة -لأولئك الذين يرغبون في المساهمة، انظر إلى [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) لدينا. +لأولئك الذين يرغبون في المساهمة، انظر إلى [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) لدينا. في الوقت نفسه، يرجى النظر في دعم Dify عن طريق مشاركته على وسائل التواصل الاجتماعي وفي الفعاليات والمؤتمرات. - > نحن نبحث عن مساهمين لمساعدة في ترجمة Dify إلى لغات أخرى غير اللغة الصينية المندرين أو الإنجليزية. إذا كنت مهتمًا بالمساعدة، يرجى الاطلاع على [README للترجمة](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) لمزيد من المعلومات، واترك لنا تعليقًا في قناة `global-users` على [خادم المجتمع على Discord](https://discord.gg/8Tpq4AcN9c). **المساهمون** @@ -215,26 +223,26 @@ docker compose up -d ## المجتمع والاتصال -* [مناقشة Github](https://github.com/langgenius/dify/discussions). الأفضل لـ: مشاركة التعليقات وطرح الأسئلة. -* [المشكلات على GitHub](https://github.com/langgenius/dify/issues). الأفضل لـ: الأخطاء التي تواجهها في استخدام Dify.AI، واقتراحات الميزات. انظر [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). -* [Discord](https://discord.gg/FngNHpbcY7). الأفضل لـ: مشاركة تطبيقاتك والترفيه مع المجتمع. -* [تويتر](https://twitter.com/dify_ai). الأفضل لـ: مشاركة تطبيقاتك والترفيه مع المجتمع. +- [مناقشة Github](https://github.com/langgenius/dify/discussions). الأفضل لـ: مشاركة التعليقات وطرح الأسئلة. +- [المشكلات على GitHub](https://github.com/langgenius/dify/issues). الأفضل لـ: الأخطاء التي تواجهها في استخدام Dify.AI، واقتراحات الميزات. انظر [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). +- [Discord](https://discord.gg/FngNHpbcY7). الأفضل لـ: مشاركة تطبيقاتك والترفيه مع المجتمع. +- [تويتر](https://twitter.com/dify_ai). الأفضل لـ: مشاركة تطبيقاتك والترفيه مع المجتمع. ## تاريخ النجمة [![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date) - ## الكشف عن الأمان -لحماية خصوصيتك، يرجى تجنب نشر مشكلات الأمان على GitHub. بدلاً من ذلك، أرسل أسئلتك إلى security@dify.ai وسنقدم لك إجابة أكثر تفصيلاً. +لحماية خصوصيتك، يرجى تجنب نشر مشكلات الأمان على GitHub. بدلاً من ذلك، أرسل أسئلتك إلى وسنقدم لك إجابة أكثر تفصيلاً. ## الرخصة هذا المستودع متاح تحت [رخصة البرنامج الحر Dify](LICENSE)، والتي تعتبر بشكل أساسي Apache 2.0 مع بعض القيود الإضافية. + ## الكشف عن الأمان -لحماية خصوصيتك، يرجى تجنب نشر مشكلات الأمان على GitHub. بدلاً من ذلك، أرسل أسئلتك إلى security@dify.ai وسنقدم لك إجابة أكثر تفصيلاً. +لحماية خصوصيتك، يرجى تجنب نشر مشكلات الأمان على GitHub. بدلاً من ذلك، أرسل أسئلتك إلى وسنقدم لك إجابة أكثر تفصيلاً. ## الرخصة diff --git a/README_BN.md b/README_BN.md new file mode 100644 index 0000000000..751de4a4f1 --- /dev/null +++ b/README_BN.md @@ -0,0 +1,258 @@ +![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) + +

+ 📌 ডিফাই ওয়ার্কফ্লো ফাইল আপলোড পরিচিতি: গুগল নোটবুক-এলএম পডকাস্ট পুনর্নির্মাণ +

+ +

+ ডিফাই ক্লাউড · + সেল্ফ-হোস্টিং · + ডকুমেন্টেশন · + ব্যাবসায়িক অনুসন্ধান +

+ +

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

+ +

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

+ +ডিফাই একটি ওপেন-সোর্স LLM অ্যাপ ডেভেলপমেন্ট প্ল্যাটফর্ম। এটি ইন্টুইটিভ ইন্টারফেস, এজেন্টিক AI ওয়ার্কফ্লো, RAG পাইপলাইন, এজেন্ট ক্যাপাবিলিটি, মডেল ম্যানেজমেন্ট, মনিটরিং সুবিধা এবং আরও অনেক কিছু একত্রিত করে, যা দ্রুত প্রোটোটাইপ থেকে প্রোডাকশন পর্যন্ত নিয়ে যেতে সহায়তা করে। + +## কুইক স্টার্ট +> +> ডিফাই ইনস্টল করার আগে, নিশ্চিত করুন যে আপনার মেশিন নিম্নলিখিত ন্যূনতম কনফিগারেশনের প্রয়োজনীয়তা পূরন করে : +> +>- সিপিউ >= 2 কোর +>- র‍্যাম >= 4 জিবি + +
+ +ডিফাই সার্ভার চালু করার সবচেয়ে সহজ উপায় [docker compose](docker/docker-compose.yaml) মাধ্যমে। নিম্নলিখিত কমান্ডগুলো ব্যবহার করে ডিফাই চালানোর আগে, নিশ্চিত করুন যে আপনার মেশিনে [Docker](https://docs.docker.com/get-docker/) এবং [Docker Compose](https://docs.docker.com/compose/install/) ইনস্টল করা আছে : +```bash +cd dify +cd docker +cp .env.example .env +docker compose up -d +``` +চালানোর পর, আপনি আপনার ব্রাউজারে [http://localhost/install](http://localhost/install)-এ ডিফাই ড্যাশবোর্ডে অ্যাক্সেস করতে পারেন এবং ইনিশিয়ালাইজেশন প্রক্রিয়া শুরু করতে পারেন। + +#### সাহায্যের খোঁজে + +ডিফাই সেট আপ করতে সমস্যা হলে দয়া করে আমাদের [FAQ](https://docs.dify.ai/getting-started/install-self-hosted/faqs) দেখুন। যদি তবুও সমস্যা থেকে থাকে, তাহলে [কমিউনিটি এবং আমাদের](#community--contact) সাথে যোগাযোগ করুন। + +> যদি আপনি ডিফাইতে অবদান রাখতে বা অতিরিক্ত উন্নয়ন করতে চান, আমাদের [সোর্স কোড থেকে ডিপ্লয়মেন্টের গাইড](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code) দেখুন। + +## প্রধান ফিচারসমূহ + +**১. ওয়ার্কফ্লো**: + ভিজ্যুয়াল ক্যানভাসে AI ওয়ার্কফ্লো তৈরি এবং পরীক্ষা করুন, নিম্নলিখিত সব ফিচার এবং তার বাইরেও আরও অনেক কিছু ব্যবহার করে। + + + +**২. মডেল সাপোর্ট**: + GPT, Mistral, Llama3, এবং যেকোনো OpenAI API-সামঞ্জস্যপূর্ণ মডেলসহ, কয়েক ডজন ইনফারেন্স প্রদানকারী এবং সেল্ফ-হোস্টেড সমাধান থেকে শুরু করে প্রোপ্রাইটরি/ওপেন-সোর্স LLM-এর সাথে সহজে ইন্টিগ্রেশন। সমর্থিত মডেল প্রদানকারীদের একটি সম্পূর্ণ তালিকা পাওয়া যাবে [এখানে](https://docs.dify.ai/getting-started/readme/model-providers)। + +![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3) + +**3. প্রম্পট IDE**: + প্রম্পট তৈরি, মডেলের পারফরম্যান্স তুলনা এবং চ্যাট-বেজড অ্যাপে টেক্সট-টু-স্পিচের মতো বৈশিষ্ট্য যুক্ত করার জন্য ইন্টুইটিভ ইন্টারফেস। + +**4. RAG পাইপলাইন**: + ডকুমেন্ট ইনজেশন থেকে শুরু করে রিট্রিভ পর্যন্ত সবকিছুই বিস্তৃত RAG ক্যাপাবিলিটির আওতাভুক্ত। PDF, PPT এবং অন্যান্য সাধারণ ডকুমেন্ট ফর্ম্যাট থেকে টেক্সট এক্সট্রাকশনের জন্য আউট-অফ-বক্স সাপোর্ট। + +**5. এজেন্ট ক্যাপাবিলিটি**: + LLM ফাংশন কলিং বা ReAct উপর ভিত্তি করে এজেন্ট ডিফাইন করতে পারেন এবং এজেন্টের জন্য পূর্ব-নির্মিত বা কাস্টম টুলস যুক্ত করতে পারেন। Dify AI এজেন্টদের জন্য 50+ বিল্ট-ইন টুলস সরবরাহ করে, যেমন Google Search, DALL·E, Stable Diffusion এবং WolframAlpha। + +**6. এলএলএম-অপ্স**: + সময়ের সাথে সাথে অ্যাপ্লিকেশন লগ এবং পারফরম্যান্স মনিটর এবং বিশ্লেষণ করুন। প্রডাকশন ডেটা এবং annotation এর উপর ভিত্তি করে প্রম্পট, ডেটাসেট এবং মডেলগুলিকে ক্রমাগত উন্নত করতে পারেন। + +**7. ব্যাকএন্ড-অ্যাজ-এ-সার্ভিস**: + ডিফাই-এর সমস্ত অফার সংশ্লিষ্ট API-সহ আছে, যাতে আপনি অনায়াসে ডিফাইকে আপনার নিজস্ব বিজনেস লজিকে ইন্টেগ্রেট করতে পারেন। + +## বৈশিষ্ট্য তুলনা + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
বৈশিষ্ট্যDify.AILangChainFlowiseOpenAI Assistants API
প্রোগ্রামিং পদ্ধতিAPI + App-orientedPython CodeApp-orientedAPI-oriented
সাপোর্টেড LLMsRich VarietyRich VarietyRich VarietyOpenAI-only
RAG ইঞ্জিন
এজেন্ট
ওয়ার্কফ্লো
অবজার্ভেবল
এন্টারপ্রাইজ ফিচার (SSO/Access control)
লোকাল ডেপ্লয়মেন্ট
+ +## ডিফাই-এর ব্যবহার + +- **ক্লাউড
** +জিরো সেটাপে ব্যবহার করতে আমাদের [Dify Cloud](https://dify.ai) সার্ভিসটি ব্যবহার করতে পারেন। এখানে সেল্ফহোস্টিং-এর সকল ফিচার ও ক্যাপাবিলিটিসহ স্যান্ডবক্সে ২০০ জিপিটি-৪ কল ফ্রি পাবেন। + +- **সেল্ফহোস্টিং ডিফাই কমিউনিটি সংস্করণ
** +সেল্ফহোস্ট করতে এই [স্টার্টার গাইড](#quick-start) ব্যবহার করে দ্রুত আপনার এনভায়রনমেন্টে ডিফাই চালান। +আরো ইন-ডেপথ রেফারেন্সের জন্য [ডকুমেন্টেশন](https://docs.dify.ai) দেখেন। + +- **এন্টারপ্রাইজ / প্রতিষ্ঠানের জন্য Dify
** +আমরা এন্টারপ্রাইজ/প্রতিষ্ঠান-কেন্দ্রিক সেবা প্রদান করে থাকি । [এই চ্যাটবটের মাধ্যমে আপনার প্রশ্নগুলি আমাদের জন্য লগ করুন।](https://udify.app/chat/22L1zSxg6yW1cWQg) অথবা [আমাদের ইমেল পাঠান](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry) আপনার চাহিদা সম্পর্কে আলোচনা করার জন্য।
+ + > AWS ব্যবহারকারী স্টার্টআপ এবং ছোট ব্যবসার জন্য, [AWS মার্কেটপ্লেসে Dify Premium](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) দেখুন এবং এক-ক্লিকের মাধ্যমে এটি আপনার নিজস্ব AWS VPC-তে ডিপ্লয় করুন। এটি একটি সাশ্রয়ী মূল্যের AMI অফার, যাতে কাস্টম লোগো এবং ব্র্যান্ডিং সহ অ্যাপ তৈরির সুবিধা আছে। + +## এগিয়ে থাকুন + +GitHub-এ ডিফাইকে স্টার দিয়ে রাখুন এবং নতুন রিলিজের খবর তাৎক্ষণিকভাবে পান। + +![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4) + +## Advanced Setup + +যদি আপনার কনফিগারেশনটি কাস্টমাইজ করার প্রয়োজন হয়, তাহলে অনুগ্রহ করে আমাদের [.env.example](docker/.env.example) ফাইল দেখুন এবং আপনার `.env` ফাইলে সংশ্লিষ্ট মানগুলি আপডেট করুন। এছাড়াও, আপনার নির্দিষ্ট এনভায়রনমেন্ট এবং প্রয়োজনীয়তার উপর ভিত্তি করে আপনাকে `docker-compose.yaml` ফাইলে সমন্বয় করতে হতে পারে, যেমন ইমেজ ভার্সন পরিবর্তন করা, পোর্ট ম্যাপিং করা, অথবা ভলিউম মাউন্ট করা। +যেকোনো পরিবর্তন করার পর, অনুগ্রহ করে `docker-compose up -d` পুনরায় চালান। ভেরিয়েবলের সম্পূর্ণ তালিকা [এখানে] (https://docs.dify.ai/getting-started/install-self-hosted/environments) খুঁজে পেতে পারেন। + +যদি আপনি একটি হাইলি এভেইলেবল সেটআপ কনফিগার করতে চান, তাহলে কমিউনিটি [Helm Charts](https://helm.sh/) এবং YAML ফাইল রয়েছে যা Dify কে Kubernetes-এ ডিপ্লয় করার প্রক্রিয়া বর্ণনা করে। + +- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify) +- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm) +- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes) + +#### টেরাফর্ম ব্যবহার করে ডিপ্লয় + +[terraform](https://www.terraform.io/) ব্যবহার করে এক ক্লিকেই ক্লাউড প্ল্যাটফর্মে Dify ডিপ্লয় করুন। + +##### অ্যাজুর গ্লোবাল + +- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform) + +##### গুগল ক্লাউড + +- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform) + +#### AWS CDK ব্যবহার করে ডিপ্লয় + +[CDK](https://aws.amazon.com/cdk/) দিয়ে AWS-এ Dify ডিপ্লয় করুন + +##### AWS + +- [AWS CDK by @KevinZhao](https://github.com/aws-samples/solution-for-deploying-dify-on-aws) + +## Contributing + +যারা কোড অবদান রাখতে চান, তাদের জন্য আমাদের [অবদান নির্দেশিকা] দেখুন (https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)। +একই সাথে, সোশ্যাল মিডিয়া এবং ইভেন্ট এবং কনফারেন্সে এটি শেয়ার করে Dify কে সমর্থন করুন। + +> আমরা ম্যান্ডারিন বা ইংরেজি ছাড়া অন্য ভাষায় Dify অনুবাদ করতে সাহায্য করার জন্য অবদানকারীদের খুঁজছি। আপনি যদি সাহায্য করতে আগ্রহী হন, তাহলে আরও তথ্যের জন্য [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) দেখুন এবং আমাদের [ডিসকর্ড কমিউনিটি সার্ভার](https://discord.gg/8Tpq4AcN9c) এর `গ্লোবাল-ইউজারস` চ্যানেলে আমাদের একটি মন্তব্য করুন। + +## কমিউনিটি এবং যোগাযোগ + +- [Github Discussion](https://github.com/langgenius/dify/discussions) ফিডব্যাক এবং প্রতিক্রিয়া জানানোর মাধ্যম। +- [GitHub Issues](https://github.com/langgenius/dify/issues). Dify.AI ব্যবহার করে আপনি যেসব বাগের সম্মুখীন হন এবং ফিচার প্রস্তাবনা। আমাদের [অবদান নির্দেশিকা](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) দেখুন। +- [Discord](https://discord.gg/FngNHpbcY7) আপনার এপ্লিকেশন শেয়ার এবং কমিউনিটি আড্ডার মাধ্যম। +- [X(Twitter)](https://twitter.com/dify_ai) আপনার এপ্লিকেশন শেয়ার এবং কমিউনিটি আড্ডার মাধ্যম। + +**অবদানকারীদের তালিকা** + + + + + +## স্টার হিস্ট্রি + +[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date) + +## নিরাপত্তা বিষয়ক + +আপনার গোপনীয়তা রক্ষা করতে, অনুগ্রহ করে GitHub-এ নিরাপত্তা সংক্রান্ত সমস্যা পোস্ট করা এড়িয়ে চলুন। পরিবর্তে, আপনার প্রশ্নগুলি ঠিকানায় পাঠান এবং আমরা আপনাকে আরও বিস্তারিত উত্তর প্রদান করব। + +## লাইসেন্স + +এই রিপোজিটরিটি [ডিফাই ওপেন সোর্স লাইসেন্স](LICENSE) এর অধিনে , যা মূলত অ্যাপাচি ২.০, তবে কিছু অতিরিক্ত বিধিনিষেধ রয়েছে। diff --git a/README_CN.md b/README_CN.md index ca94db87b0..6c57b9f59c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা
diff --git a/README_DE.md b/README_DE.md index 8814a39b64..32f9983e93 100644 --- a/README_DE.md +++ b/README_DE.md @@ -50,6 +50,7 @@ Türkçe README README Tiếng Việt README in Deutsch + README in বাংলা

Dify ist eine Open-Source-Plattform zur Entwicklung von LLM-Anwendungen. Ihre intuitive Benutzeroberfläche vereint agentenbasierte KI-Workflows, RAG-Pipelines, Agentenfunktionen, Modellverwaltung, Überwachungsfunktionen und mehr, sodass Sie schnell von einem Prototyp in die Produktion übergehen können. diff --git a/README_ES.md b/README_ES.md index bbbd6f854d..29ea5dbfcb 100644 --- a/README_ES.md +++ b/README_ES.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

# diff --git a/README_FR.md b/README_FR.md index afbf18b069..59a8402141 100644 --- a/README_FR.md +++ b/README_FR.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

# diff --git a/README_JA.md b/README_JA.md index e7c99fba2c..2ebddbb5b9 100644 --- a/README_JA.md +++ b/README_JA.md @@ -15,7 +15,7 @@ Discordでチャット - + Reddit @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

# @@ -56,7 +57,7 @@ DifyはオープンソースのLLMアプリケーション開発プラットフォームです。直感的なインターフェイスには、AIワークフロー、RAGパイプライン、エージェント機能、モデル管理、観測機能などが組み合わさっており、プロトタイプから生産まで迅速に進めることができます。以下の機能が含まれます:

-**1. ワークフロー**: +**1. ワークフロー**: 強力なAIワークフローをビジュアルキャンバス上で構築し、テストできます。すべての機能、および以下の機能を使用できます。 @@ -64,25 +65,25 @@ DifyはオープンソースのLLMアプリケーション開発プラットフ -**2. 総合的なモデルサポート**: +**2. 総合的なモデルサポート**: 数百ものプロプライエタリ/オープンソースのLLMと、数十もの推論プロバイダーおよびセルフホスティングソリューションとのシームレスな統合を提供します。GPT、Mistral、Llama3、OpenAI APIと互換性のあるすべてのモデルを統合されています。サポートされているモデルプロバイダーの完全なリストは[こちら](https://docs.dify.ai/getting-started/readme/model-providers)をご覧ください。 ![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3) -**3. プロンプトIDE**: +**3. プロンプトIDE**: プロンプトの作成、モデルパフォーマンスの比較が行え、チャットベースのアプリに音声合成などの機能も追加できます。 -**4. RAGパイプライン**: +**4. RAGパイプライン**: ドキュメントの取り込みから検索までをカバーする広範なRAG機能ができます。ほかにもPDF、PPT、その他の一般的なドキュメントフォーマットからのテキスト抽出のサポートも提供します。 -**5. エージェント機能**: +**5. エージェント機能**: LLM Function CallingやReActに基づくエージェントの定義が可能で、AIエージェント用のプリビルトまたはカスタムツールを追加できます。Difyには、Google検索、DALL·E、Stable Diffusion、WolframAlphaなどのAIエージェント用の50以上の組み込みツールが提供します。 -**6. LLMOps**: +**6. LLMOps**: アプリケーションのログやパフォーマンスを監視と分析し、生産のデータと注釈に基づいて、プロンプト、データセット、モデルを継続的に改善できます。 -**7. Backend-as-a-Service**: +**7. Backend-as-a-Service**: すべての機能はAPIを提供されており、Difyを自分のビジネスロジックに簡単に統合できます。 @@ -164,7 +165,7 @@ DifyはオープンソースのLLMアプリケーション開発プラットフ - **企業/組織向けのDify
** 企業中心の機能を提供しています。[メールを送信](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry)して企業のニーズについて相談してください。
- > AWSを使用しているスタートアップ企業や中小企業の場合は、[AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t23mebxzwjhu6)のDify Premiumをチェックして、ワンクリックで自分のAWS VPCにデプロイできます。さらに、手頃な価格のAMIオファリングとして、ロゴやブランディングをカスタマイズしてアプリケーションを作成するオプションがあります。 + > AWSを使用しているスタートアップ企業や中小企業の場合は、[AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6)のDify Premiumをチェックして、ワンクリックで自分のAWS VPCにデプロイできます。さらに、手頃な価格のAMIオファリングとして、ロゴやブランディングをカスタマイズしてアプリケーションを作成するオプションがあります。 ## 最新の情報を入手 @@ -177,7 +178,7 @@ GitHub上でDifyにスターを付けることで、Difyに関する新しいニ ## クイックスタート > Difyをインストールする前に、お使いのマシンが以下の最小システム要件を満たしていることを確認してください: -> +> >- CPU >= 2コア >- RAM >= 4GB @@ -219,7 +220,7 @@ docker compose up -d [CDK](https://aws.amazon.com/cdk/) を使用して、DifyをAWSにデプロイします -##### AWS +##### AWS - [@KevinZhaoによるAWS CDK](https://github.com/aws-samples/solution-for-deploying-dify-on-aws) ## 貢献 diff --git a/README_KL.md b/README_KL.md index c8cd22810e..eebd7d67ee 100644 --- a/README_KL.md +++ b/README_KL.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

# diff --git a/README_KR.md b/README_KR.md index 7be18b2312..4c1735a81e 100644 --- a/README_KR.md +++ b/README_KR.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

diff --git a/README_PT.md b/README_PT.md index 16f3d4041a..af7eb3c822 100644 --- a/README_PT.md +++ b/README_PT.md @@ -50,6 +50,7 @@ README em Turco README em Vietnamita README em Português - BR + README in বাংলা

Dify é uma plataforma de desenvolvimento de aplicativos LLM de código aberto. Sua interface intuitiva combina workflow de IA, pipeline RAG, capacidades de agente, gerenciamento de modelos, recursos de observabilidade e muito mais, permitindo que você vá rapidamente do protótipo à produção. Aqui está uma lista das principais funcionalidades: diff --git a/README_SI.md b/README_SI.md index 29e2ad4fb5..5b7c9611f9 100644 --- a/README_SI.md +++ b/README_SI.md @@ -47,6 +47,7 @@ Türkçe README README Tiếng Việt README Slovenščina + README in বাংলা

diff --git a/README_TR.md b/README_TR.md index d858618eaa..7af8582b7d 100644 --- a/README_TR.md +++ b/README_TR.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

diff --git a/README_VI.md b/README_VI.md index 730a415ebe..2f64541285 100644 --- a/README_VI.md +++ b/README_VI.md @@ -45,6 +45,7 @@ README بالعربية Türkçe README README Tiếng Việt + README in বাংলা

diff --git a/api/configs/middleware/vdb/oracle_config.py b/api/configs/middleware/vdb/oracle_config.py index 5d2cf67ba3..ea39909ef4 100644 --- a/api/configs/middleware/vdb/oracle_config.py +++ b/api/configs/middleware/vdb/oracle_config.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import Field, PositiveInt +from pydantic import Field from pydantic_settings import BaseSettings @@ -9,16 +9,6 @@ class OracleConfig(BaseSettings): Configuration settings for Oracle database """ - ORACLE_HOST: Optional[str] = Field( - description="Hostname or IP address of the Oracle database server (e.g., 'localhost' or 'oracle.example.com')", - default=None, - ) - - ORACLE_PORT: PositiveInt = Field( - description="Port number on which the Oracle database server is listening (default is 1521)", - default=1521, - ) - ORACLE_USER: Optional[str] = Field( description="Username for authenticating with the Oracle database", default=None, @@ -29,7 +19,28 @@ class OracleConfig(BaseSettings): default=None, ) - ORACLE_DATABASE: Optional[str] = Field( - description="Name of the Oracle database or service to connect to (e.g., 'ORCL' or 'pdborcl')", + ORACLE_DSN: Optional[str] = Field( + description="Oracle database connection string. For traditional database, use format 'host:port/service_name'. " + "For autonomous database, use the service name from tnsnames.ora in the wallet", default=None, ) + + ORACLE_CONFIG_DIR: Optional[str] = Field( + description="Directory containing the tnsnames.ora configuration file. Only used in thin mode connection", + default=None, + ) + + ORACLE_WALLET_LOCATION: Optional[str] = Field( + description="Oracle wallet directory path containing the wallet files for secure connection", + default=None, + ) + + ORACLE_WALLET_PASSWORD: Optional[str] = Field( + description="Password to decrypt the Oracle wallet, if it is encrypted", + default=None, + ) + + ORACLE_IS_AUTONOMOUS: bool = Field( + description="Flag indicating whether connecting to Oracle Autonomous Database", + default=False, + ) diff --git a/api/contexts/__init__.py b/api/contexts/__init__.py index 91438d086a..127b8fe76d 100644 --- a/api/contexts/__init__.py +++ b/api/contexts/__init__.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING from contexts.wrapper import RecyclableContextVar if TYPE_CHECKING: + from core.model_runtime.entities.model_entities import AIModelEntity from core.plugin.entities.plugin_daemon import PluginModelProviderEntity from core.tools.plugin_tool.provider import PluginToolProviderController from core.workflow.entities.variable_pool import VariablePool @@ -20,11 +21,19 @@ To avoid race-conditions caused by gunicorn thread recycling, using RecyclableCo plugin_tool_providers: RecyclableContextVar[dict[str, "PluginToolProviderController"]] = RecyclableContextVar( ContextVar("plugin_tool_providers") ) + plugin_tool_providers_lock: RecyclableContextVar[Lock] = RecyclableContextVar(ContextVar("plugin_tool_providers_lock")) plugin_model_providers: RecyclableContextVar[list["PluginModelProviderEntity"] | None] = RecyclableContextVar( ContextVar("plugin_model_providers") ) + plugin_model_providers_lock: RecyclableContextVar[Lock] = RecyclableContextVar( ContextVar("plugin_model_providers_lock") ) + +plugin_model_schema_lock: RecyclableContextVar[Lock] = RecyclableContextVar(ContextVar("plugin_model_schema_lock")) + +plugin_model_schemas: RecyclableContextVar[dict[str, "AIModelEntity"]] = RecyclableContextVar( + ContextVar("plugin_model_schemas") +) diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index 3d6ba5ce37..5dd3ba11c8 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -582,6 +582,15 @@ class AdvancedChatAppGenerateTaskPipeline: session.commit() yield workflow_finish_resp + elif event.stopped_by in ( + QueueStopEvent.StopBy.INPUT_MODERATION, + QueueStopEvent.StopBy.ANNOTATION_REPLY, + ): + # When hitting input-moderation or annotation-reply, the workflow will not start + with Session(db.engine, expire_on_commit=False) as session: + # Save message + self._save_message(session=session) + session.commit() yield self._message_end_to_stream_response() break diff --git a/api/core/model_runtime/model_providers/__base/ai_model.py b/api/core/model_runtime/model_providers/__base/ai_model.py index e79a3c0157..cdd1bba6be 100644 --- a/api/core/model_runtime/model_providers/__base/ai_model.py +++ b/api/core/model_runtime/model_providers/__base/ai_model.py @@ -1,8 +1,11 @@ import decimal +import hashlib +from threading import Lock from typing import Optional from pydantic import BaseModel, ConfigDict, Field +import contexts from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.defaults import PARAMETER_RULE_TEMPLATE from core.model_runtime.entities.model_entities import ( @@ -139,15 +142,35 @@ class AIModel(BaseModel): :return: model schema """ plugin_model_manager = PluginModelManager() - return plugin_model_manager.get_model_schema( - tenant_id=self.tenant_id, - user_id="unknown", - plugin_id=self.plugin_id, - provider=self.provider_name, - model_type=self.model_type.value, - model=model, - credentials=credentials or {}, - ) + cache_key = f"{self.tenant_id}:{self.plugin_id}:{self.provider_name}:{self.model_type.value}:{model}" + # sort credentials + sorted_credentials = sorted(credentials.items()) if credentials else [] + cache_key += ":".join([hashlib.md5(f"{k}:{v}".encode()).hexdigest() for k, v in sorted_credentials]) + + try: + contexts.plugin_model_schemas.get() + except LookupError: + contexts.plugin_model_schemas.set({}) + contexts.plugin_model_schema_lock.set(Lock()) + + with contexts.plugin_model_schema_lock.get(): + if cache_key in contexts.plugin_model_schemas.get(): + return contexts.plugin_model_schemas.get()[cache_key] + + schema = plugin_model_manager.get_model_schema( + tenant_id=self.tenant_id, + user_id="unknown", + plugin_id=self.plugin_id, + provider=self.provider_name, + model_type=self.model_type.value, + model=model, + credentials=credentials or {}, + ) + + if schema: + contexts.plugin_model_schemas.get()[cache_key] = schema + + return schema def get_customizable_model_schema_from_credentials(self, model: str, credentials: dict) -> Optional[AIModelEntity]: """ diff --git a/api/core/model_runtime/model_providers/model_provider_factory.py b/api/core/model_runtime/model_providers/model_provider_factory.py index 4e9b20b033..d2fd4916a4 100644 --- a/api/core/model_runtime/model_providers/model_provider_factory.py +++ b/api/core/model_runtime/model_providers/model_provider_factory.py @@ -1,3 +1,4 @@ +import hashlib import logging import os from collections.abc import Sequence @@ -206,17 +207,35 @@ class ModelProviderFactory: Get model schema """ plugin_id, provider_name = self.get_plugin_id_and_provider_name_from_provider(provider) - model_schema = self.plugin_model_manager.get_model_schema( - tenant_id=self.tenant_id, - user_id="unknown", - plugin_id=plugin_id, - provider=provider_name, - model_type=model_type.value, - model=model, - credentials=credentials, - ) + cache_key = f"{self.tenant_id}:{plugin_id}:{provider_name}:{model_type.value}:{model}" + # sort credentials + sorted_credentials = sorted(credentials.items()) if credentials else [] + cache_key += ":".join([hashlib.md5(f"{k}:{v}".encode()).hexdigest() for k, v in sorted_credentials]) - return model_schema + try: + contexts.plugin_model_schemas.get() + except LookupError: + contexts.plugin_model_schemas.set({}) + contexts.plugin_model_schema_lock.set(Lock()) + + with contexts.plugin_model_schema_lock.get(): + if cache_key in contexts.plugin_model_schemas.get(): + return contexts.plugin_model_schemas.get()[cache_key] + + schema = self.plugin_model_manager.get_model_schema( + tenant_id=self.tenant_id, + user_id="unknown", + plugin_id=plugin_id, + provider=provider_name, + model_type=model_type.value, + model=model, + credentials=credentials or {}, + ) + + if schema: + contexts.plugin_model_schemas.get()[cache_key] = schema + + return schema def get_models( self, diff --git a/api/core/rag/datasource/vdb/milvus/milvus_vector.py b/api/core/rag/datasource/vdb/milvus/milvus_vector.py index 9a184f7dd9..cc29b92825 100644 --- a/api/core/rag/datasource/vdb/milvus/milvus_vector.py +++ b/api/core/rag/datasource/vdb/milvus/milvus_vector.py @@ -72,8 +72,18 @@ class MilvusVector(BaseVector): self._client = self._init_client(config) self._consistency_level = "Session" # Consistency level for Milvus operations self._fields: list[str] = [] # List of fields in the collection + if self._client.has_collection(collection_name): + self._load_collection_fields() self._hybrid_search_enabled = self._check_hybrid_search_support() # Check if hybrid search is supported + def _load_collection_fields(self, fields: Optional[list[str]] = None) -> None: + if fields is None: + # Load collection fields from remote server + collection_info = self._client.describe_collection(self._collection_name) + fields = [field["name"] for field in collection_info["fields"]] + # Since primary field is auto-id, no need to track it + self._fields = [f for f in fields if f != Field.PRIMARY_KEY.value] + def _check_hybrid_search_support(self) -> bool: """ Check if the current Milvus version supports hybrid search. @@ -306,10 +316,7 @@ class MilvusVector(BaseVector): ) schema.add_function(bm25_function) - for x in schema.fields: - self._fields.append(x.name) - # Since primary field is auto-id, no need to track it - self._fields.remove(Field.PRIMARY_KEY.value) + self._load_collection_fields([f.name for f in schema.fields]) # Create Index params for the collection index_params_obj = IndexParams() diff --git a/api/core/rag/datasource/vdb/oracle/oraclevector.py b/api/core/rag/datasource/vdb/oracle/oraclevector.py index a58df7eb9f..8262c219b4 100644 --- a/api/core/rag/datasource/vdb/oracle/oraclevector.py +++ b/api/core/rag/datasource/vdb/oracle/oraclevector.py @@ -23,25 +23,30 @@ oracledb.defaults.fetch_lobs = False class OracleVectorConfig(BaseModel): - host: str - port: int user: str password: str - database: str + dsn: str + config_dir: str | None = None + wallet_location: str | None = None + wallet_password: str | None = None + is_autonomous: bool = False @model_validator(mode="before") @classmethod def validate_config(cls, values: dict) -> dict: - if not values["host"]: - raise ValueError("config ORACLE_HOST is required") - if not values["port"]: - raise ValueError("config ORACLE_PORT is required") if not values["user"]: raise ValueError("config ORACLE_USER is required") if not values["password"]: raise ValueError("config ORACLE_PASSWORD is required") - if not values["database"]: - raise ValueError("config ORACLE_DB is required") + if not values["dsn"]: + raise ValueError("config ORACLE_DSN is required") + if values.get("is_autonomous", False): + if not values.get("config_dir"): + raise ValueError("config_dir is required for autonomous database") + if not values.get("wallet_location"): + raise ValueError("wallet_location is required for autonomous database") + if not values.get("wallet_password"): + raise ValueError("wallet_password is required for autonomous database") return values @@ -56,7 +61,7 @@ CREATE TABLE IF NOT EXISTS {table_name} ( SQL_CREATE_INDEX = """ CREATE INDEX IF NOT EXISTS idx_docs_{table_name} ON {table_name}(text) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS -('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER sys.my_chinese_vgram_lexer') +('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER multilingual_lexer') """ @@ -103,14 +108,25 @@ class OracleVector(BaseVector): ) def _create_connection_pool(self, config: OracleVectorConfig): - return oracledb.create_pool( - user=config.user, - password=config.password, - dsn="{}:{}/{}".format(config.host, config.port, config.database), - min=1, - max=50, - increment=1, - ) + pool_params = { + "user": config.user, + "password": config.password, + "dsn": config.dsn, + "min": 1, + "max": 50, + "increment": 1, + } + + if config.is_autonomous: + pool_params.update( + { + "config_dir": config.config_dir, + "wallet_location": config.wallet_location, + "wallet_password": config.wallet_password, + } + ) + + return oracledb.create_pool(**pool_params) @contextmanager def _get_cursor(self): @@ -287,10 +303,12 @@ class OracleVectorFactory(AbstractVectorFactory): return OracleVector( collection_name=collection_name, config=OracleVectorConfig( - host=dify_config.ORACLE_HOST or "localhost", - port=dify_config.ORACLE_PORT, user=dify_config.ORACLE_USER or "system", password=dify_config.ORACLE_PASSWORD or "oracle", - database=dify_config.ORACLE_DATABASE or "orcl", + dsn=dify_config.ORACLE_DSN or "oracle:1521/freepdb1", + config_dir=dify_config.ORACLE_CONFIG_DIR, + wallet_location=dify_config.ORACLE_WALLET_LOCATION, + wallet_password=dify_config.ORACLE_WALLET_PASSWORD, + is_autonomous=dify_config.ORACLE_IS_AUTONOMOUS, ), ) diff --git a/api/core/workflow/graph_engine/entities/graph.py b/api/core/workflow/graph_engine/entities/graph.py index 1c6b4b6618..5c672c985b 100644 --- a/api/core/workflow/graph_engine/entities/graph.py +++ b/api/core/workflow/graph_engine/entities/graph.py @@ -590,8 +590,6 @@ class Graph(BaseModel): start_node_id=node_id, routes_node_ids=routes_node_ids, ) - # Exclude conditional branch nodes - and all(edge.run_condition is None for edge in reverse_edge_mapping.get(node_id, [])) ): if node_id not in merge_branch_node_ids: merge_branch_node_ids[node_id] = [] diff --git a/api/core/workflow/nodes/llm/node.py b/api/core/workflow/nodes/llm/node.py index f717a2a877..b61b5b5cb5 100644 --- a/api/core/workflow/nodes/llm/node.py +++ b/api/core/workflow/nodes/llm/node.py @@ -191,6 +191,22 @@ class LLMNode(BaseNode[LLMNodeData]): # deduct quota self.deduct_llm_quota(tenant_id=self.tenant_id, model_instance=model_instance, usage=usage) break + outputs = {"text": result_text, "usage": jsonable_encoder(usage), "finish_reason": finish_reason} + + yield RunCompletedEvent( + run_result=NodeRunResult( + status=WorkflowNodeExecutionStatus.SUCCEEDED, + inputs=node_inputs, + process_data=process_data, + outputs=outputs, + metadata={ + NodeRunMetadataKey.TOTAL_TOKENS: usage.total_tokens, + NodeRunMetadataKey.TOTAL_PRICE: usage.total_price, + NodeRunMetadataKey.CURRENCY: usage.currency, + }, + llm_usage=usage, + ) + ) except LLMNodeError as e: yield RunCompletedEvent( run_result=NodeRunResult( @@ -211,23 +227,6 @@ class LLMNode(BaseNode[LLMNodeData]): ) ) - outputs = {"text": result_text, "usage": jsonable_encoder(usage), "finish_reason": finish_reason} - - yield RunCompletedEvent( - run_result=NodeRunResult( - status=WorkflowNodeExecutionStatus.SUCCEEDED, - inputs=node_inputs, - process_data=process_data, - outputs=outputs, - metadata={ - NodeRunMetadataKey.TOTAL_TOKENS: usage.total_tokens, - NodeRunMetadataKey.TOTAL_PRICE: usage.total_price, - NodeRunMetadataKey.CURRENCY: usage.currency, - }, - llm_usage=usage, - ) - ) - def _invoke_llm( self, node_data_model: ModelConfig, diff --git a/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py b/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py index 814dec423c..0facd0ecc0 100644 --- a/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py +++ b/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py @@ -1,4 +1,5 @@ """add retry_index field to node-execution model + Revision ID: e1944c35e15e Revises: 11b07f66c737 Create Date: 2024-12-20 06:28:30.287197 diff --git a/api/models/workflow.py b/api/models/workflow.py index a0120f275b..8f550357a7 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -191,7 +191,7 @@ class Workflow(Base): features["file_upload"]["enabled"] = image_enabled features["file_upload"]["number_limits"] = image_number_limits features["file_upload"]["allowed_file_upload_methods"] = image_transfer_methods - features["file_upload"]["allowed_file_types"] = ["image"] + features["file_upload"]["allowed_file_types"] = features["file_upload"].get("allowed_file_types", ["image"]) features["file_upload"]["allowed_file_extensions"] = [] del features["file_upload"]["image"] self._features = json.dumps(features) diff --git a/api/services/plugin/plugin_service.py b/api/services/plugin/plugin_service.py index f84baf6b81..749bb1a5b4 100644 --- a/api/services/plugin/plugin_service.py +++ b/api/services/plugin/plugin_service.py @@ -1,6 +1,9 @@ import logging -from collections.abc import Sequence +from collections.abc import Mapping, Sequence from mimetypes import guess_type +from typing import Optional + +from pydantic import BaseModel from configs import dify_config from core.helper import marketplace @@ -18,11 +21,71 @@ from core.plugin.entities.plugin_daemon import PluginInstallTask, PluginUploadRe from core.plugin.manager.asset import PluginAssetManager from core.plugin.manager.debugging import PluginDebuggingManager from core.plugin.manager.plugin import PluginInstallationManager +from extensions.ext_redis import redis_client logger = logging.getLogger(__name__) class PluginService: + class LatestPluginCache(BaseModel): + plugin_id: str + version: str + unique_identifier: str + + REDIS_KEY_PREFIX = "plugin_service:latest_plugin:" + REDIS_TTL = 60 * 5 # 5 minutes + + @staticmethod + def fetch_latest_plugin_version(plugin_ids: Sequence[str]) -> Mapping[str, Optional[LatestPluginCache]]: + """ + Fetch the latest plugin version + """ + result: dict[str, Optional[PluginService.LatestPluginCache]] = {} + + try: + cache_not_exists = [] + + # Try to get from Redis first + for plugin_id in plugin_ids: + cached_data = redis_client.get(f"{PluginService.REDIS_KEY_PREFIX}{plugin_id}") + if cached_data: + result[plugin_id] = PluginService.LatestPluginCache.model_validate_json(cached_data) + else: + cache_not_exists.append(plugin_id) + + if cache_not_exists: + manifests = { + manifest.plugin_id: manifest + for manifest in marketplace.batch_fetch_plugin_manifests(cache_not_exists) + } + + for plugin_id, manifest in manifests.items(): + latest_plugin = PluginService.LatestPluginCache( + plugin_id=plugin_id, + version=manifest.latest_version, + unique_identifier=manifest.latest_package_identifier, + ) + + # Store in Redis + redis_client.setex( + f"{PluginService.REDIS_KEY_PREFIX}{plugin_id}", + PluginService.REDIS_TTL, + latest_plugin.model_dump_json(), + ) + + result[plugin_id] = latest_plugin + + # pop plugin_id from cache_not_exists + cache_not_exists.remove(plugin_id) + + for plugin_id in cache_not_exists: + result[plugin_id] = None + + return result + except Exception: + logger.exception("failed to fetch latest plugin version") + return result + @staticmethod def get_debugging_key(tenant_id: str) -> str: """ @@ -40,9 +103,7 @@ class PluginService: plugins = manager.list_plugins(tenant_id) plugin_ids = [plugin.plugin_id for plugin in plugins if plugin.source == PluginInstallationSource.Marketplace] try: - manifests = { - manifest.plugin_id: manifest for manifest in marketplace.batch_fetch_plugin_manifests(plugin_ids) - } + manifests = PluginService.fetch_latest_plugin_version(plugin_ids) except Exception: manifests = {} logger.exception("failed to fetch plugin manifests") @@ -50,9 +111,11 @@ class PluginService: for plugin in plugins: if plugin.source == PluginInstallationSource.Marketplace: if plugin.plugin_id in manifests: - # set latest_version - plugin.latest_version = manifests[plugin.plugin_id].latest_version - plugin.latest_unique_identifier = manifests[plugin.plugin_id].latest_package_identifier + latest_plugin_cache = manifests[plugin.plugin_id] + if latest_plugin_cache: + # set latest_version + plugin.latest_version = latest_plugin_cache.version + plugin.latest_unique_identifier = latest_plugin_cache.unique_identifier return plugins diff --git a/api/tests/integration_tests/vdb/oracle/test_oraclevector.py b/api/tests/integration_tests/vdb/oracle/test_oraclevector.py index 3252b04276..76e8b7bccd 100644 --- a/api/tests/integration_tests/vdb/oracle/test_oraclevector.py +++ b/api/tests/integration_tests/vdb/oracle/test_oraclevector.py @@ -13,11 +13,9 @@ class OracleVectorTest(AbstractVectorTest): self.vector = OracleVector( collection_name=self.collection_name, config=OracleVectorConfig( - host="localhost", - port=1521, user="dify", password="dify", - database="FREEPDB1", + dsn="localhost:1521/FREEPDB1", ), ) diff --git a/docker/.env.example b/docker/.env.example index afd2caf501..438af9fc52 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -483,11 +483,13 @@ CHROMA_AUTH_PROVIDER=chromadb.auth.token_authn.TokenAuthClientProvider CHROMA_AUTH_CREDENTIALS= # Oracle configuration, only available when VECTOR_STORE is `oracle` -ORACLE_HOST=oracle -ORACLE_PORT=1521 ORACLE_USER=dify ORACLE_PASSWORD=dify -ORACLE_DATABASE=FREEPDB1 +ORACLE_DSN=oracle:1521/FREEPDB1 +ORACLE_CONFIG_DIR=/app/api/storage/wallet +ORACLE_WALLET_LOCATION=/app/api/storage/wallet +ORACLE_WALLET_PASSWORD=dify +ORACLE_IS_AUTONOMOUS=false # relyt configurations, only available when VECTOR_STORE is `relyt` RELYT_HOST=db diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml index 860fce1f9a..bae3636570 100644 --- a/docker/docker-compose-template.yaml +++ b/docker/docker-compose-template.yaml @@ -12,6 +12,8 @@ services: SENTRY_DSN: ${API_SENTRY_DSN:-} SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} + PLUGIN_REMOTE_INSTALL_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} + PLUGIN_REMOTE_INSTALL_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} depends_on: @@ -142,8 +144,8 @@ services: PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false} DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} DIFY_INNER_API_KEY: ${INNER_API_KEY_FOR_PLUGIN:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} - PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_REMOTE_INSTALL_HOST:-0.0.0.0} - PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_REMOTE_INSTALL_PORT:-5003} + PLUGIN_REMOTE_INSTALLING_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} + PLUGIN_REMOTE_INSTALLING_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd} FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true} ports: diff --git a/docker/docker-compose.middleware.yaml b/docker/docker-compose.middleware.yaml index d9d723df31..72e60d3cff 100644 --- a/docker/docker-compose.middleware.yaml +++ b/docker/docker-compose.middleware.yaml @@ -84,8 +84,8 @@ services: PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false} DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://host.docker.internal:5001} DIFY_INNER_API_KEY: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} - PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_DEBUGGING_HOST:-0.0.0.0} - PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_DEBUGGING_PORT:-5003} + PLUGIN_REMOTE_INSTALLING_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} + PLUGIN_REMOTE_INSTALLING_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd} FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true} ports: diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 469c2d1b77..72a615263f 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -197,11 +197,13 @@ x-shared-env: &shared-api-worker-env CHROMA_DATABASE: ${CHROMA_DATABASE:-default_database} CHROMA_AUTH_PROVIDER: ${CHROMA_AUTH_PROVIDER:-chromadb.auth.token_authn.TokenAuthClientProvider} CHROMA_AUTH_CREDENTIALS: ${CHROMA_AUTH_CREDENTIALS:-} - ORACLE_HOST: ${ORACLE_HOST:-oracle} - ORACLE_PORT: ${ORACLE_PORT:-1521} ORACLE_USER: ${ORACLE_USER:-dify} ORACLE_PASSWORD: ${ORACLE_PASSWORD:-dify} - ORACLE_DATABASE: ${ORACLE_DATABASE:-FREEPDB1} + ORACLE_DSN: ${ORACLE_DSN:-oracle:1521/FREEPDB1} + ORACLE_CONFIG_DIR: ${ORACLE_CONFIG_DIR:-/app/api/storage/wallet} + ORACLE_WALLET_LOCATION: ${ORACLE_WALLET_LOCATION:-/app/api/storage/wallet} + ORACLE_WALLET_PASSWORD: ${ORACLE_WALLET_PASSWORD:-dify} + ORACLE_IS_AUTONOMOUS: ${ORACLE_IS_AUTONOMOUS:-false} RELYT_HOST: ${RELYT_HOST:-db} RELYT_PORT: ${RELYT_PORT:-5432} RELYT_USER: ${RELYT_USER:-postgres} @@ -424,6 +426,8 @@ services: SENTRY_DSN: ${API_SENTRY_DSN:-} SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0} SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0} + PLUGIN_REMOTE_INSTALL_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} + PLUGIN_REMOTE_INSTALL_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_MAX_PACKAGE_SIZE: ${PLUGIN_MAX_PACKAGE_SIZE:-52428800} INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} depends_on: @@ -554,8 +558,8 @@ services: PPROF_ENABLED: ${PLUGIN_PPROF_ENABLED:-false} DIFY_INNER_API_URL: ${PLUGIN_DIFY_INNER_API_URL:-http://api:5001} DIFY_INNER_API_KEY: ${INNER_API_KEY_FOR_PLUGIN:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} - PLUGIN_REMOTE_INSTALLING_HOST: ${PLUGIN_REMOTE_INSTALL_HOST:-0.0.0.0} - PLUGIN_REMOTE_INSTALLING_PORT: ${PLUGIN_REMOTE_INSTALL_PORT:-5003} + PLUGIN_REMOTE_INSTALLING_HOST: ${EXPOSE_PLUGIN_DEBUGGING_HOST:-localhost} + PLUGIN_REMOTE_INSTALLING_PORT: ${EXPOSE_PLUGIN_DEBUGGING_PORT:-5003} PLUGIN_WORKING_PATH: ${PLUGIN_WORKING_PATH:-/app/storage/cwd} FORCE_VERIFYING_SIGNATURE: ${FORCE_VERIFYING_SIGNATURE:-true} ports: diff --git a/docker/startupscripts/init_user.script b/docker/startupscripts/init_user.script index 7aa7c28049..55e8510d2f 100755 --- a/docker/startupscripts/init_user.script +++ b/docker/startupscripts/init_user.script @@ -5,6 +5,6 @@ create user dify identified by dify DEFAULT TABLESPACE users quota unlimited on grant DB_DEVELOPER_ROLE to dify; BEGIN -CTX_DDL.CREATE_PREFERENCE('my_chinese_vgram_lexer','CHINESE_VGRAM_LEXER'); +CTX_DDL.CREATE_PREFERENCE('dify.multilingual_lexer','CHINESE_VGRAM_LEXER'); END; / diff --git a/web/app/(shareLayout)/layout.tsx b/web/app/(shareLayout)/layout.tsx index 0782603ebc..94ac1deb0b 100644 --- a/web/app/(shareLayout)/layout.tsx +++ b/web/app/(shareLayout)/layout.tsx @@ -1,7 +1,6 @@ import React from 'react' import type { FC } from 'react' import type { Metadata } from 'next' -import { SharePageContextProvider } from '@/context/share-page-context' export const metadata: Metadata = { icons: 'data:,', // prevent browser from using default favicon @@ -12,9 +11,7 @@ const Layout: FC<{ }> = ({ children }) => { return (
- - {children} - + {children}
) } diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index a2260a889d..54853cf875 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -219,7 +219,7 @@ const AppPublisher = ({ )} { - handleOpenInExplore() + publishedAt && handleOpenInExplore() }} disabled={!publishedAt} icon={} diff --git a/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx b/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx index ad012a7d94..9a31435c9c 100644 --- a/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx +++ b/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx @@ -132,11 +132,11 @@ const SelectDataSet: FC = ({ toggleSelect(item) }} > -
+
-
{item.name}
+
{item.name}
{!item.embedding_available && ( {t('dataset.unavailable')} )} @@ -144,13 +144,14 @@ const SelectDataSet: FC = ({ { item.indexing_technique && ( ) } { item.provider === 'external' && ( - + ) }
diff --git a/web/app/components/base/audio-gallery/AudioPlayer.tsx b/web/app/components/base/audio-gallery/AudioPlayer.tsx index d6d265c8d2..17ecc0005d 100644 --- a/web/app/components/base/audio-gallery/AudioPlayer.tsx +++ b/web/app/components/base/audio-gallery/AudioPlayer.tsx @@ -5,7 +5,7 @@ import { RiPlayLargeFill, } from '@remixicon/react' import Toast from '@/app/components/base/toast' -import { useAppContext } from '@/context/app-context' +import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' import cn from '@/utils/classnames' @@ -24,7 +24,7 @@ const AudioPlayer: React.FC = ({ src }) => { const [hasStartedPlaying, setHasStartedPlaying] = useState(false) const [hoverTime, setHoverTime] = useState(0) const [isAudioAvailable, setIsAudioAvailable] = useState(true) - const { theme } = useAppContext() + const { theme } = useTheme() useEffect(() => { const audio = audioRef.current diff --git a/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx b/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx index a4a76fbbf6..4a3e292f80 100644 --- a/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx +++ b/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx @@ -206,7 +206,7 @@ const ChatWrapper = () => { isResponding={isResponding} chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`} chatFooterClassName='pb-4' - chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`} + chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile ? 'px-2' : 'px-4'}`} onSend={doSend} inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs} inputsForm={inputsForms} diff --git a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts index 298f07603f..d4d617d4b7 100644 --- a/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts +++ b/web/app/components/base/chat/embedded-chatbot/theme/theme-context.ts @@ -48,10 +48,13 @@ export class ThemeBuilder { private buildChecker = false public get theme() { - if (this._theme === undefined) - throw new Error('The theme should be built first and then accessed') - else + if (this._theme === undefined) { + this._theme = new Theme() return this._theme + } + else { + return this._theme + } } public buildTheme(chatColorTheme: string | null = null, chatColorThemeInverted = false) { diff --git a/web/app/components/base/file-uploader/store.tsx b/web/app/components/base/file-uploader/store.tsx index 3ad0b74f05..d15a129358 100644 --- a/web/app/components/base/file-uploader/store.tsx +++ b/web/app/components/base/file-uploader/store.tsx @@ -21,7 +21,7 @@ export const createFileStore = ( onChange?: (files: FileEntity[]) => void, ) => { return create(set => ({ - files: [...value], + files: value ? [...value] : [], setFiles: (files) => { set({ files }) onChange?.(files) diff --git a/web/app/components/base/markdown.tsx b/web/app/components/base/markdown.tsx index fae0e3c0fe..dc985bd6df 100644 --- a/web/app/components/base/markdown.tsx +++ b/web/app/components/base/markdown.tsx @@ -26,7 +26,7 @@ import MarkdownButton from '@/app/components/base/markdown-blocks/button' import MarkdownForm from '@/app/components/base/markdown-blocks/form' import ThinkBlock from '@/app/components/base/markdown-blocks/think-block' import { Theme } from '@/types/app' -import { useAppContext } from '@/context/app-context' +import useTheme from '@/hooks/use-theme' import cn from '@/utils/classnames' // Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD @@ -107,7 +107,7 @@ export function PreCode(props: { children: any }) { // or use the non-minified dev environment for full errors and additional helpful warnings. const CodeBlock: any = memo(({ inline, className, children, ...props }: any) => { - const { theme } = useAppContext() + const { theme } = useTheme() const [isSVG, setIsSVG] = useState(true) const match = /language-(\w+)/.exec(className || '') const language = match?.[1] diff --git a/web/app/components/explore/app-card/index.tsx b/web/app/components/explore/app-card/index.tsx index 36034195ee..977c38776c 100644 --- a/web/app/components/explore/app-card/index.tsx +++ b/web/app/components/explore/app-card/index.tsx @@ -5,8 +5,7 @@ import Button from '../../base/button' import cn from '@/utils/classnames' import type { App } from '@/models/explore' import AppIcon from '@/app/components/base/app-icon' -import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication' -import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel' +import { AppTypeIcon } from '../../app/type-selector' export type AppCardProps = { app: App canCreate: boolean @@ -23,7 +22,7 @@ const AppCard = ({ const { t } = useTranslation() const { app: appBasicInfo } = app return ( -
+
- - {appBasicInfo.mode === 'advanced-chat' && ( - - )} - {appBasicInfo.mode === 'agent-chat' && ( - - )} - {appBasicInfo.mode === 'chat' && ( - - )} - {appBasicInfo.mode === 'completion' && ( - - )} - {appBasicInfo.mode === 'workflow' && ( - - )} - +
@@ -64,14 +48,14 @@ const AppCard = ({
-
+
{app.description}
{isExplore && canCreate && ( -
-
+
+
)} - {!isExplore && ( -
-
- -
-
- )}
) } diff --git a/web/app/components/explore/app-list/index.tsx b/web/app/components/explore/app-list/index.tsx index 4fef183d52..df15348dd3 100644 --- a/web/app/components/explore/app-list/index.tsx +++ b/web/app/components/explore/app-list/index.tsx @@ -17,7 +17,6 @@ import { fetchAppDetail, fetchAppList } from '@/service/explore' import { importDSL } from '@/service/apps' import { useTabSearchParams } from '@/hooks/use-tab-searchparams' import CreateAppModal from '@/app/components/explore/create-app-modal' -import AppTypeSelector from '@/app/components/app/type-selector' import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' import Loading from '@/app/components/base/loading' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' @@ -28,7 +27,6 @@ import { DSLImportMode } from '@/models/app' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' type AppsProps = { - pageType?: PageType onSuccess?: () => void } @@ -38,7 +36,6 @@ export enum PageType { } const Apps = ({ - pageType = PageType.EXPLORE, onSuccess, }: AppsProps) => { const { t } = useTranslation() @@ -62,7 +59,7 @@ const Apps = ({ const [currentType, setCurrentType] = useState('') const [currCategory, setCurrCategory] = useTabSearchParams({ defaultTab: allCategoriesEn, - disableSearchParams: pageType !== PageType.EXPLORE, + disableSearchParams: false, }) const { @@ -166,26 +163,18 @@ const Apps = ({ return (
- {pageType === PageType.EXPLORE && ( -
-
{t('explore.apps.title')}
-
{t('explore.apps.description')}
-
- )} + +
+
{t('explore.apps.title')}
+
{t('explore.apps.description')}
+
+
<> - {pageType !== PageType.EXPLORE && ( - <> - -
- - )}