Merge branch 'langgenius:main' into feat/copy-node-between-workflows

pull/19821/head
FyhSky 1 year ago committed by GitHub
commit 758c35bda0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

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

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
@ -54,8 +54,6 @@
**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).
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)

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

@ -1,4 +1,4 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
![cover-v5-optimized](./images/GitHub_README_if.png)
<div align="center">
<a href="https://cloud.dify.ai">Dify 云服务</a> ·
@ -61,11 +61,6 @@ Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI
**1. 工作流**:
在画布上构建和测试功能强大的 AI 工作流程,利用以下所有功能以及更多功能。
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)找到。

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

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

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

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

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

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

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

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

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

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

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

@ -476,6 +476,7 @@ LOGIN_LOCKOUT_DURATION=86400
ENABLE_OTEL=false
OTLP_BASE_ENDPOINT=http://localhost:4318
OTLP_API_KEY=
OTEL_EXPORTER_OTLP_PROTOCOL=
OTEL_EXPORTER_TYPE=otlp
OTEL_SAMPLING_RATE=0.1
OTEL_BATCH_EXPORT_SCHEDULE_DELAY=5000

@ -27,6 +27,11 @@ class OTelConfig(BaseSettings):
default="otlp",
)
OTEL_EXPORTER_OTLP_PROTOCOL: str = Field(
description="OTLP exporter protocol ('grpc' or 'http')",
default="http",
)
OTEL_SAMPLING_RATE: float = Field(default=0.1, description="Sampling rate for traces (0.0 to 1.0)")
OTEL_BATCH_EXPORT_SCHEDULE_DELAY: int = Field(

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

@ -313,7 +313,7 @@ class DatasetApi(DatasetApiResource):
try:
if DatasetService.delete_dataset(dataset_id_str, current_user):
DatasetPermissionService.clear_partial_member_list(dataset_id_str)
return {"result": "success"}, 204
return 204
else:
raise NotFound("Dataset not found.")
except services.errors.dataset.DatasetInUseError:

@ -323,7 +323,7 @@ class DocumentDeleteApi(DatasetApiResource):
except services.errors.document.DocumentIndexingError:
raise DocumentIndexingError("Cannot delete document during indexing.")
return {"result": "success"}, 204
return 204
class DocumentListApi(DatasetApiResource):

@ -159,7 +159,7 @@ class DatasetSegmentApi(DatasetApiResource):
if not segment:
raise NotFound("Segment not found.")
SegmentService.delete_segment(segment, document, dataset)
return {"result": "success"}, 204
return 204
@cloud_edition_billing_resource_check("vector_space", "dataset")
def post(self, tenant_id, dataset_id, document_id, segment_id):
@ -344,7 +344,7 @@ class DatasetChildChunkApi(DatasetApiResource):
except ChildChunkDeleteIndexServiceError as e:
raise ChildChunkDeleteIndexError(str(e))
return {"result": "success"}, 204
return 204
@cloud_edition_billing_resource_check("vector_space", "dataset")
@cloud_edition_billing_knowledge_limit_check("add_segment", "dataset")

@ -897,37 +897,36 @@ class ProviderConfiguration(BaseModel):
)
except Exception as ex:
logger.warning(f"get custom model schema failed, {ex}")
if not custom_model_schema:
continue
if custom_model_schema.model_type not in model_types:
continue
status = ModelStatus.ACTIVE
if (
custom_model_schema.model_type in model_setting_map
and custom_model_schema.model in model_setting_map[custom_model_schema.model_type]
):
model_setting = model_setting_map[custom_model_schema.model_type][
custom_model_schema.model
]
if model_setting.enabled is False:
status = ModelStatus.DISABLED
provider_models.append(
ModelWithProviderEntity(
model=custom_model_schema.model,
label=custom_model_schema.label,
model_type=custom_model_schema.model_type,
features=custom_model_schema.features,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties=custom_model_schema.model_properties,
deprecated=custom_model_schema.deprecated,
provider=SimpleModelProviderEntity(self.provider),
status=status,
)
continue
if not custom_model_schema:
continue
if custom_model_schema.model_type not in model_types:
continue
status = ModelStatus.ACTIVE
if (
custom_model_schema.model_type in model_setting_map
and custom_model_schema.model in model_setting_map[custom_model_schema.model_type]
):
model_setting = model_setting_map[custom_model_schema.model_type][custom_model_schema.model]
if model_setting.enabled is False:
status = ModelStatus.DISABLED
provider_models.append(
ModelWithProviderEntity(
model=custom_model_schema.model,
label=custom_model_schema.label,
model_type=custom_model_schema.model_type,
features=custom_model_schema.features,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties=custom_model_schema.model_properties,
deprecated=custom_model_schema.deprecated,
provider=SimpleModelProviderEntity(self.provider),
status=status,
)
)
# if llm name not in restricted llm list, remove it
restrict_model_names = [rm.model for rm in restrict_models]

@ -0,0 +1,6 @@
class EndpointSetupFailedError(ValueError):
"""
Endpoint setup failed error
"""
pass

@ -17,6 +17,7 @@ from core.model_runtime.errors.invoke import (
InvokeServerUnavailableError,
)
from core.model_runtime.errors.validate import CredentialsValidateFailedError
from core.plugin.endpoint.exc import EndpointSetupFailedError
from core.plugin.entities.plugin_daemon import PluginDaemonBasicResponse, PluginDaemonError, PluginDaemonInnerError
from core.plugin.impl.exc import (
PluginDaemonBadRequestError,
@ -219,6 +220,8 @@ class BasePluginClient:
raise InvokeServerUnavailableError(description=args.get("description"))
case CredentialsValidateFailedError.__name__:
raise CredentialsValidateFailedError(error_object.get("message"))
case EndpointSetupFailedError.__name__:
raise EndpointSetupFailedError(error_object.get("message"))
case _:
raise PluginInvokeError(description=message)
case PluginDaemonInternalServerError.__name__:

@ -1,3 +1,4 @@
import hashlib
import json
import logging
import uuid
@ -61,12 +62,12 @@ CREATE TABLE IF NOT EXISTS {table_name} (
"""
SQL_CREATE_INDEX = """
CREATE INDEX IF NOT EXISTS embedding_cosine_v1_idx ON {table_name}
CREATE INDEX IF NOT EXISTS embedding_cosine_v1_idx_{index_hash} ON {table_name}
USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
"""
SQL_CREATE_INDEX_PG_BIGM = """
CREATE INDEX IF NOT EXISTS bigm_idx ON {table_name}
CREATE INDEX IF NOT EXISTS bigm_idx_{index_hash} ON {table_name}
USING gin (text gin_bigm_ops);
"""
@ -76,6 +77,7 @@ class PGVector(BaseVector):
super().__init__(collection_name)
self.pool = self._create_connection_pool(config)
self.table_name = f"embedding_{collection_name}"
self.index_hash = hashlib.md5(self.table_name.encode()).hexdigest()[:8]
self.pg_bigm = config.pg_bigm
def get_type(self) -> str:
@ -256,10 +258,9 @@ class PGVector(BaseVector):
# PG hnsw index only support 2000 dimension or less
# ref: https://github.com/pgvector/pgvector?tab=readme-ov-file#indexing
if dimension <= 2000:
cur.execute(SQL_CREATE_INDEX.format(table_name=self.table_name))
cur.execute(SQL_CREATE_INDEX.format(table_name=self.table_name, index_hash=self.index_hash))
if self.pg_bigm:
cur.execute("CREATE EXTENSION IF NOT EXISTS pg_bigm")
cur.execute(SQL_CREATE_INDEX_PG_BIGM.format(table_name=self.table_name))
cur.execute(SQL_CREATE_INDEX_PG_BIGM.format(table_name=self.table_name, index_hash=self.index_hash))
redis_client.set(collection_exist_cache_key, 1, ex=3600)

@ -114,8 +114,10 @@ def init_app(app: DifyApp):
pass
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter as GRPCMetricExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter as GRPCSpanExporter
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter as HTTPMetricExporter
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter as HTTPSpanExporter
from opentelemetry.instrumentation.celery import CeleryInstrumentor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
@ -158,19 +160,32 @@ def init_app(app: DifyApp):
sampler = ParentBasedTraceIdRatio(dify_config.OTEL_SAMPLING_RATE)
provider = TracerProvider(resource=resource, sampler=sampler)
set_tracer_provider(provider)
exporter: Union[OTLPSpanExporter, ConsoleSpanExporter]
metric_exporter: Union[OTLPMetricExporter, ConsoleMetricExporter]
exporter: Union[GRPCSpanExporter, HTTPSpanExporter, ConsoleSpanExporter]
metric_exporter: Union[GRPCMetricExporter, HTTPMetricExporter, ConsoleMetricExporter]
protocol = (dify_config.OTEL_EXPORTER_OTLP_PROTOCOL or "").lower()
if dify_config.OTEL_EXPORTER_TYPE == "otlp":
exporter = OTLPSpanExporter(
endpoint=dify_config.OTLP_BASE_ENDPOINT + "/v1/traces",
headers={"Authorization": f"Bearer {dify_config.OTLP_API_KEY}"},
)
metric_exporter = OTLPMetricExporter(
endpoint=dify_config.OTLP_BASE_ENDPOINT + "/v1/metrics",
headers={"Authorization": f"Bearer {dify_config.OTLP_API_KEY}"},
)
if protocol == "grpc":
exporter = GRPCSpanExporter(
endpoint=dify_config.OTLP_BASE_ENDPOINT,
# Header field names must consist of lowercase letters, check RFC7540
headers=(("authorization", f"Bearer {dify_config.OTLP_API_KEY}"),),
insecure=True,
)
metric_exporter = GRPCMetricExporter(
endpoint=dify_config.OTLP_BASE_ENDPOINT,
headers=(("authorization", f"Bearer {dify_config.OTLP_API_KEY}"),),
insecure=True,
)
else:
exporter = HTTPSpanExporter(
endpoint=dify_config.OTLP_BASE_ENDPOINT + "/v1/traces",
headers={"Authorization": f"Bearer {dify_config.OTLP_API_KEY}"},
)
metric_exporter = HTTPMetricExporter(
endpoint=dify_config.OTLP_BASE_ENDPOINT + "/v1/metrics",
headers={"Authorization": f"Bearer {dify_config.OTLP_API_KEY}"},
)
else:
# Fallback to console exporter
exporter = ConsoleSpanExporter()
metric_exporter = ConsoleMetricExporter()

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

@ -992,7 +992,7 @@ class DocumentService:
created_by=account.id,
)
else:
logging.warn(
logging.warning(
f"Invalid process rule mode: {process_rule.mode}, can not find dataset process rule"
)
return

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

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

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

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

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

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

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

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

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

@ -478,7 +478,7 @@ ANALYTICDB_PORT=5432
ANALYTICDB_MIN_CONNECTION=1
ANALYTICDB_MAX_CONNECTION=5
# TiDB vector configurations, only available when VECTOR_STORE is `tidb`
# TiDB vector configurations, only available when VECTOR_STORE is `tidb_vector`
TIDB_VECTOR_HOST=tidb
TIDB_VECTOR_PORT=4000
TIDB_VECTOR_USER=
@ -1081,6 +1081,7 @@ PLUGIN_TENCENT_COS_REGION=
ENABLE_OTEL=false
OTLP_BASE_ENDPOINT=http://localhost:4318
OTLP_API_KEY=
OTEL_EXPORTER_OTLP_PROTOCOL=
OTEL_EXPORTER_TYPE=otlp
OTEL_SAMPLING_RATE=0.1
OTEL_BATCH_EXPORT_SCHEDULE_DELAY=5000

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

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

@ -478,6 +478,7 @@ x-shared-env: &shared-api-worker-env
ENABLE_OTEL: ${ENABLE_OTEL:-false}
OTLP_BASE_ENDPOINT: ${OTLP_BASE_ENDPOINT:-http://localhost:4318}
OTLP_API_KEY: ${OTLP_API_KEY:-}
OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-}
OTEL_EXPORTER_TYPE: ${OTEL_EXPORTER_TYPE:-otlp}
OTEL_SAMPLING_RATE: ${OTEL_SAMPLING_RATE:-0.1}
OTEL_BATCH_EXPORT_SCHEDULE_DELAY: ${OTEL_BATCH_EXPORT_SCHEDULE_DELAY:-5000}
@ -491,7 +492,7 @@ x-shared-env: &shared-api-worker-env
services:
# API service
api:
image: langgenius/dify-api:1.3.1
image: langgenius/dify-api:1.4.0
restart: always
environment:
# Use the shared environment variables.
@ -520,7 +521,7 @@ services:
# worker service
# The Celery worker for processing the queue.
worker:
image: langgenius/dify-api:1.3.1
image: langgenius/dify-api:1.4.0
restart: always
environment:
# Use the shared environment variables.
@ -546,7 +547,7 @@ services:
# Frontend web application.
web:
image: langgenius/dify-web:1.3.1
image: langgenius/dify-web:1.4.0
restart: always
environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-}
@ -607,7 +608,7 @@ services:
# The DifySandbox
sandbox:
image: langgenius/dify-sandbox:0.2.11
image: langgenius/dify-sandbox:0.2.12
restart: always
environment:
# The DifySandbox configurations
@ -631,7 +632,7 @@ services:
# plugin daemon
plugin_daemon:
image: langgenius/dify-plugin-daemon:0.0.9-local
image: langgenius/dify-plugin-daemon:0.0.10-local
restart: always
environment:
# Use the shared environment variables.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

@ -4,23 +4,25 @@ import { RiArrowRightUpLine, RiRobot2Line } from '@remixicon/react'
import { useRouter } from 'next/navigation'
import Button from '../components/base/button'
import Avatar from './avatar'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { useCallback } from 'react'
const Header = () => {
const { t } = useTranslation()
const router = useRouter()
const back = () => {
const back = useCallback(() => {
router.back()
}
}, [router])
return (
<div className='flex flex-1 items-center justify-between px-4'>
<div className='flex items-center gap-3'>
<div className='flex cursor-pointer items-center' onClick={back}>
<LogoSite className='object-contain' />
<DifyLogo />
</div>
<div className='h-4 w-[1px] bg-divider-regular' />
<p className='title-3xl-semi-bold text-text-primary'>{t('common.account.account')}</p>
<div className='h-4 w-[1px] origin-center rotate-[11.31deg] bg-divider-regular' />
<p className='title-3xl-semi-bold relative mt-[-2px] text-text-primary'>{t('common.account.account')}</p>
</div>
<div className='flex shrink-0 items-center gap-3'>
<Button className='system-sm-medium gap-2 px-3 py-2' onClick={back}>

@ -247,7 +247,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
</div>
{/* description */}
{appDetail.description && (
<div className='system-xs-regular text-text-tertiary'>{appDetail.description}</div>
<div className='system-xs-regular overflow-wrap-anywhere w-full max-w-full whitespace-normal break-words text-text-tertiary'>{appDetail.description}</div>
)}
{/* operations */}
<div className='flex flex-wrap items-center gap-1 self-stretch'>

@ -14,8 +14,8 @@ const SuggestedAction = ({ icon, link, disabled, children, className, ...props }
target='_blank'
rel='noreferrer'
className={classNames(
'flex justify-start items-center gap-2 py-2 px-2.5 bg-background-section-burn rounded-lg transition-colors [&:not(:first-child)]:mt-1',
disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'text-text-secondary hover:bg-state-accent-hover hover:text-text-accent cursor-pointer',
'flex justify-start items-center gap-2 py-2 px-2.5 bg-background-section-burn rounded-lg text-text-secondary transition-colors [&:not(:first-child)]:mt-1',
disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'hover:bg-state-accent-hover hover:text-text-accent cursor-pointer',
className,
)}
{...props}

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

@ -16,7 +16,7 @@ import List from '@/app/components/base/chat/chat-with-history/sidebar/list'
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
import Confirm from '@/app/components/base/confirm'
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import type { ConversationItem } from '@/models/share'
import cn from '@/utils/classnames'
@ -141,14 +141,14 @@ const Sidebar = ({ isPanel }: Props) => {
<div className='shrink-0'>
{!appData?.custom_config?.remove_webapp_brand && (
<div className={cn(
'flex shrink-0 items-center gap-1.5 px-2',
'flex shrink-0 items-center gap-1.5 px-1',
)}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
{appData?.custom_config?.replace_webapp_logo && (
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<LogoSite className='!h-5' />
<DifyLogo size='small' />
)}
</div>
)}

@ -11,7 +11,7 @@ import Tooltip from '@/app/components/base/tooltip'
import ActionButton from '@/app/components/base/action-button'
import Divider from '@/app/components/base/divider'
import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import cn from '@/utils/classnames'
export type IHeaderProps = {
@ -89,7 +89,7 @@ const Header: FC<IHeaderProps> = ({
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<LogoSite className='!h-5' />
<DifyLogo size='small' />
)}
</div>
)}
@ -132,7 +132,7 @@ const Header: FC<IHeaderProps> = ({
return (
<div
className={cn('flex h-14 shrink-0 items-center justify-between rounded-t-2xl px-3')}
style={Object.assign({}, CssTransform(theme?.backgroundHeaderColorStyle ?? ''), CssTransform(theme?.headerBorderBottomStyle ?? '')) }
style={Object.assign({}, CssTransform(theme?.backgroundHeaderColorStyle ?? ''), CssTransform(theme?.headerBorderBottomStyle ?? ''))}
>
<div className="flex grow items-center space-x-3">
{customerIcon}

@ -19,7 +19,7 @@ import Loading from '@/app/components/base/loading'
import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header'
import Header from '@/app/components/base/chat/embedded-chatbot/header'
import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import cn from '@/utils/classnames'
const Chatbot = () => {
@ -118,7 +118,7 @@ const Chatbot = () => {
<img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!appData?.custom_config?.replace_webapp_logo && (
<LogoSite className='!h-5' />
<DifyLogo size='small' />
)}
</div>
)}

@ -0,0 +1,45 @@
'use client'
import type { FC } from 'react'
import { WEB_PREFIX } from '@/config'
import classNames from '@/utils/classnames'
import useTheme from '@/hooks/use-theme'
export type LogoStyle = 'default' | 'monochromeWhite'
export const logoPathMap: Record<LogoStyle, string> = {
default: '/logo/logo.svg',
monochromeWhite: '/logo/logo-monochrome-white.svg',
}
export type LogoSize = 'large' | 'medium' | 'small'
export const logoSizeMap: Record<LogoSize, string> = {
large: 'w-16 h-7',
medium: 'w-12 h-[22px]',
small: 'w-9 h-4',
}
type DifyLogoProps = {
style?: LogoStyle
size?: LogoSize
className?: string
}
const DifyLogo: FC<DifyLogoProps> = ({
style = 'default',
size = 'medium',
className,
}) => {
const { theme } = useTheme()
const themedStyle = (theme === 'dark' && style === 'default') ? 'monochromeWhite' : style
return (
<img
src={`${WEB_PREFIX}${logoPathMap[themedStyle]}`}
className={classNames('block object-contain', logoSizeMap[size], className)}
alt='Dify logo'
/>
)
}
export default DifyLogo

@ -1,22 +0,0 @@
'use client'
import type { FC } from 'react'
import { WEB_PREFIX } from '@/config'
import classNames from '@/utils/classnames'
type LogoSiteProps = {
className?: string
}
const LogoSite: FC<LogoSiteProps> = ({
className,
}) => {
return (
<img
src={`${WEB_PREFIX}/logo/logo.png`}
className={classNames('block w-[22.651px] h-[24.5px]', className)}
alt='logo'
/>
)
}
export default LogoSite

@ -2,7 +2,7 @@
@layer components {
.premium-badge {
@apply shrink-0 relative inline-flex justify-center items-center rounded-md box-border border border-transparent text-white shadow-xs hover:shadow-lg bg-origin-border overflow-hidden;
@apply shrink-0 relative inline-flex justify-center items-center rounded-md box-border border border-transparent text-white shadow-xs hover:shadow-lg bg-origin-border overflow-hidden transition-all duration-100 ease-out;
background-clip: padding-box, border-box;
}
.allowHover {

@ -10,7 +10,7 @@ import {
RiLoader2Line,
RiPlayLargeLine,
} from '@remixicon/react'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import Switch from '@/app/components/base/switch'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
@ -246,7 +246,7 @@ const CustomWebAppBrand = () => {
<div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div>
{webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <LogoSite className='!h-5' />
: <DifyLogo size='small' />
}
</>
)}
@ -305,7 +305,7 @@ const CustomWebAppBrand = () => {
<div className='system-2xs-medium-uppercase text-text-tertiary'>POWERED BY</div>
{webappLogo
? <img src={`${webappLogo}?hash=${imgKey}`} alt='logo' className='block h-5 w-auto' />
: <LogoSite className='!h-5' />
: <DifyLogo size='small' />
}
</>
)}

@ -7,7 +7,7 @@ import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import type { LangGeniusVersionResponse } from '@/models/common'
import { IS_CE_EDITION } from '@/config'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { noop } from 'lodash-es'
type IAccountSettingProps = {
@ -28,21 +28,21 @@ export default function AccountAbout({
onClose={noop}
className='!w-[480px] !max-w-[480px] !px-6 !py-4'
>
<div className='relative pt-4'>
<div className='absolute -right-4 -top-2 flex h-8 w-8 cursor-pointer items-center justify-center' onClick={onCancel}>
<div>
<div className='absolute right-4 top-4 flex h-8 w-8 cursor-pointer items-center justify-center' onClick={onCancel}>
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
</div>
<div>
<LogoSite className='mx-auto mb-2' />
<div className='mb-3 text-center text-xs font-normal text-text-tertiary'>Version {langeniusVersionInfo?.current_version}</div>
<div className='mb-4 text-center text-xs font-normal text-text-secondary'>
<div className='flex flex-col items-center gap-4 py-8'>
<DifyLogo size='large' className='mx-auto' />
<div className='text-center text-xs font-normal text-text-tertiary'>Version {langeniusVersionInfo?.current_version}</div>
<div className='flex flex-col items-center gap-2 text-center text-xs font-normal text-text-secondary'>
<div>© {dayjs().year()} LangGenius, Inc., Contributors.</div>
<div className='text-text-accent'>
{
IS_CE_EDITION
? <Link href={'https://github.com/langgenius/dify/blob/main/LICENSE'} target='_blank' rel='noopener noreferrer'>Open Source License</Link>
: <>
<Link href='https://dify.ai/privacy' target='_blank' rel='noopener noreferrer'>Privacy Policy</Link>,<span> </span>
<Link href='https://dify.ai/privacy' target='_blank' rel='noopener noreferrer'>Privacy Policy</Link>,&nbsp;
<Link href='https://dify.ai/terms' target='_blank' rel='noopener noreferrer'>Terms of Service</Link>
</>
}
@ -51,7 +51,7 @@ export default function AccountAbout({
</div>
<div className='-mx-8 mb-4 h-[0.5px] bg-divider-regular' />
<div className='flex items-center justify-between'>
<div className='text-xs font-medium text-text-primary'>
<div className='text-xs font-medium text-text-tertiary'>
{
isLatest
? t('common.about.latestAvailable', { version: langeniusVersionInfo.latest_version })
@ -59,7 +59,7 @@ export default function AccountAbout({
}
</div>
<div className='flex items-center'>
<Button className='mr-2'>
<Button className='mr-2' size='small'>
<Link
href={'https://github.com/langgenius/dify/releases'}
target='_blank' rel='noopener noreferrer'
@ -69,7 +69,7 @@ export default function AccountAbout({
</Button>
{
!isLatest && !IS_CE_EDITION && (
<Button variant='primary'>
<Button variant='primary' size='small'>
<Link
href={langeniusVersionInfo.release_notes}
target='_blank' rel='noopener noreferrer'

@ -13,7 +13,7 @@ import ExploreNav from './explore-nav'
import ToolsNav from './tools-nav'
import { WorkspaceProvider } from '@/context/workspace-context'
import { useAppContext } from '@/context/app-context'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import WorkplaceSelector from '@/app/components/header/account-dropdown/workplace-selector'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useProviderContext } from '@/context/provider-context'
@ -60,8 +60,8 @@ const Header = () => {
{
!isMobile
&& <div className='flex shrink-0 items-center gap-1.5 self-stretch pl-3'>
<Link href="/apps" className='flex h-8 w-8 shrink-0 items-center justify-center gap-2'>
<LogoSite className='object-contain' />
<Link href="/apps" className='flex h-8 w-[52px] shrink-0 items-center justify-center gap-2'>
<DifyLogo />
</Link>
<div className='font-light text-divider-deep'>/</div>
<div className='flex items-center gap-0.5'>
@ -76,7 +76,7 @@ const Header = () => {
{isMobile && (
<div className='flex'>
<Link href="/apps" className='mr-4 flex items-center'>
<LogoSite />
<DifyLogo />
</Link>
<div className='font-light text-divider-deep'>/</div>
{enableBilling ? <PlanBadge allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />}

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

@ -36,7 +36,7 @@ import Toast from '@/app/components/base/toast'
import type { VisionFile, VisionSettings } from '@/types/app'
import { Resolution, TransferMethod } from '@/types/app'
import { useAppFavicon } from '@/hooks/use-app-favicon'
import LogoSite from '@/app/components/base/logo/logo-site'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import cn from '@/utils/classnames'
const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group.
@ -635,7 +635,7 @@ const TextGeneration: FC<IMainProps> = ({
<img src={customConfig?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
)}
{!customConfig?.replace_webapp_logo && (
<LogoSite className='!h-5' />
<DifyLogo size='small' />
)}
</div>
)}
@ -664,7 +664,7 @@ const TextGeneration: FC<IMainProps> = ({
showResultPanel()
}}
>
<div className='h-1 w-8 cursor-grab rounded bg-divider-solid'/>
<div className='h-1 w-8 cursor-grab rounded bg-divider-solid' />
</div>
)}
{renderResWrap}

@ -31,7 +31,6 @@ type NodesExtraData = {
getAvailablePrevNodes: (isChatMode: boolean) => BlockEnum[]
getAvailableNextNodes: (isChatMode: boolean) => BlockEnum[]
checkValid: any
checkVarValid?: any
}
export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
[BlockEnum.Start]: {
@ -60,7 +59,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: AnswerDefault.getAvailablePrevNodes,
getAvailableNextNodes: AnswerDefault.getAvailableNextNodes,
checkValid: AnswerDefault.checkValid,
checkVarValid: AnswerDefault.checkVarValid,
},
[BlockEnum.LLM]: {
author: 'Dify',
@ -70,7 +68,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: LLMDefault.getAvailablePrevNodes,
getAvailableNextNodes: LLMDefault.getAvailableNextNodes,
checkValid: LLMDefault.checkValid,
checkVarValid: LLMDefault.checkVarValid,
},
[BlockEnum.KnowledgeRetrieval]: {
author: 'Dify',
@ -80,7 +77,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: KnowledgeRetrievalDefault.getAvailablePrevNodes,
getAvailableNextNodes: KnowledgeRetrievalDefault.getAvailableNextNodes,
checkValid: KnowledgeRetrievalDefault.checkValid,
checkVarValid: KnowledgeRetrievalDefault.checkVarValid,
},
[BlockEnum.IfElse]: {
author: 'Dify',
@ -90,7 +86,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: IfElseDefault.getAvailablePrevNodes,
getAvailableNextNodes: IfElseDefault.getAvailableNextNodes,
checkValid: IfElseDefault.checkValid,
checkVarValid: IfElseDefault.checkVarValid,
},
[BlockEnum.Iteration]: {
author: 'Dify',
@ -100,7 +95,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: IterationDefault.getAvailablePrevNodes,
getAvailableNextNodes: IterationDefault.getAvailableNextNodes,
checkValid: IterationDefault.checkValid,
checkVarValid: IterationDefault.checkVarValid,
},
[BlockEnum.IterationStart]: {
author: 'Dify',
@ -146,7 +140,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: CodeDefault.getAvailablePrevNodes,
getAvailableNextNodes: CodeDefault.getAvailableNextNodes,
checkValid: CodeDefault.checkValid,
checkVarValid: CodeDefault.checkVarValid,
},
[BlockEnum.TemplateTransform]: {
author: 'Dify',
@ -156,7 +149,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: TemplateTransformDefault.getAvailablePrevNodes,
getAvailableNextNodes: TemplateTransformDefault.getAvailableNextNodes,
checkValid: TemplateTransformDefault.checkValid,
checkVarValid: TemplateTransformDefault.checkVarValid,
},
[BlockEnum.QuestionClassifier]: {
author: 'Dify',
@ -166,7 +158,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: QuestionClassifierDefault.getAvailablePrevNodes,
getAvailableNextNodes: QuestionClassifierDefault.getAvailableNextNodes,
checkValid: QuestionClassifierDefault.checkValid,
checkVarValid: QuestionClassifierDefault.checkVarValid,
},
[BlockEnum.HttpRequest]: {
author: 'Dify',
@ -176,7 +167,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: HttpRequestDefault.getAvailablePrevNodes,
getAvailableNextNodes: HttpRequestDefault.getAvailableNextNodes,
checkValid: HttpRequestDefault.checkValid,
checkVarValid: HttpRequestDefault.checkVarValid,
},
[BlockEnum.VariableAssigner]: {
author: 'Dify',
@ -195,7 +185,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: AssignerDefault.getAvailablePrevNodes,
getAvailableNextNodes: AssignerDefault.getAvailableNextNodes,
checkValid: AssignerDefault.checkValid,
checkVarValid: AssignerDefault.checkVarValid,
},
[BlockEnum.VariableAggregator]: {
author: 'Dify',
@ -205,7 +194,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: VariableAssignerDefault.getAvailablePrevNodes,
getAvailableNextNodes: VariableAssignerDefault.getAvailableNextNodes,
checkValid: VariableAssignerDefault.checkValid,
checkVarValid: VariableAssignerDefault.checkVarValid,
},
[BlockEnum.ParameterExtractor]: {
author: 'Dify',
@ -215,7 +203,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: ParameterExtractorDefault.getAvailablePrevNodes,
getAvailableNextNodes: ParameterExtractorDefault.getAvailableNextNodes,
checkValid: ParameterExtractorDefault.checkValid,
checkVarValid: ParameterExtractorDefault.checkVarValid,
},
[BlockEnum.Tool]: {
author: 'Dify',
@ -225,7 +212,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: ToolDefault.getAvailablePrevNodes,
getAvailableNextNodes: ToolDefault.getAvailableNextNodes,
checkValid: ToolDefault.checkValid,
checkVarValid: ToolDefault.checkVarValid,
},
[BlockEnum.DocExtractor]: {
author: 'Dify',
@ -235,7 +221,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: DocExtractorDefault.getAvailablePrevNodes,
getAvailableNextNodes: DocExtractorDefault.getAvailableNextNodes,
checkValid: DocExtractorDefault.checkValid,
checkVarValid: DocExtractorDefault.checkVarValid,
},
[BlockEnum.ListFilter]: {
author: 'Dify',
@ -245,7 +230,6 @@ export const NODES_EXTRA_DATA: Record<BlockEnum, NodesExtraData> = {
getAvailablePrevNodes: ListFilterDefault.getAvailablePrevNodes,
getAvailableNextNodes: ListFilterDefault.getAvailableNextNodes,
checkValid: ListFilterDefault.checkValid,
checkVarValid: ListFilterDefault.checkVarValid,
},
[BlockEnum.Agent]: {
author: 'Dify',

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

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

@ -164,7 +164,7 @@ const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: Val
return res
}
export const formatItem = (
const formatItem = (
item: any,
isChatMode: boolean,
filterVar: (payload: Var, selector: ValueSelector) => boolean,
@ -613,6 +613,7 @@ const getIterationItemType = ({
const isSystem = isSystemVar(valueSelector)
const targetVar = isSystem ? beforeNodesOutputVars.find(v => v.isStartNode) : beforeNodesOutputVars.find(v => v.nodeId === outputVarNodeId)
if (!targetVar)
return VarType.string
@ -623,9 +624,9 @@ const getIterationItemType = ({
arrayType = curr.find((v: any) => v.variable === (valueSelector).join('.'))?.type
}
else {
for (let i = 1; i < valueSelector.length - 1; i++) {
for (let i = 1; i < valueSelector.length; i++) {
const key = valueSelector[i]
const isLast = i === valueSelector.length - 2
const isLast = i === valueSelector.length - 1
curr = Array.isArray(curr) ? curr.find(v => v.variable === key) : []
if (isLast)

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

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

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

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

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

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

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

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

@ -124,11 +124,11 @@ const ConditionItem = ({
)}>
<div className='flex items-center p-1'>
<div className='w-0 grow'>
<div className='inline-flex h-6 items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pl-1 pr-1.5 shadow-xs'>
<div className='flex h-6 min-w-0 items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pl-1 pr-1.5 shadow-xs'>
<div className='mr-0.5 p-[1px]'>
<MetadataIcon type={currentMetadata?.type} className='h-3 w-3' />
</div>
<div className='system-xs-medium mr-0.5 text-text-secondary'>{currentMetadata?.name}</div>
<div className='system-xs-medium mr-0.5 min-w-0 flex-1 truncate text-text-secondary'>{currentMetadata?.name}</div>
<div className='system-xs-regular text-text-tertiary'>{currentMetadata?.type}</div>
</div>
</div>

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

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

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

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

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

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

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

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

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

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

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

@ -25,6 +25,7 @@ import { useToastContext } from '@/app/components/base/toast'
import { EDUCATION_VERIFYING_LOCALSTORAGE_ITEM } from '@/app/education-apply/constants'
import { getLocaleOnClient } from '@/i18n'
import { noop } from 'lodash-es'
import DifyLogo from '../components/base/logo/dify-logo'
const EducationApplyAge = () => {
const { t } = useTranslation()
@ -93,12 +94,8 @@ const EducationApplyAge = () => {
}}
>
</div>
<div className='mt-[-349px] flex h-[88px] items-center justify-between px-8 py-6'>
<img
src='/logo/logo-site-dark.png'
alt='dify logo'
className='h-10'
/>
<div className='mt-[-349px] box-content flex h-7 items-center justify-between p-6'>
<DifyLogo size='large' style='monochromeWhite' />
</div>
<div className='mx-auto max-w-[720px] px-8 pb-[180px]'>
<div className='mb-2 flex h-[192px] flex-col justify-end pb-4 pt-3 text-text-primary-on-surface'>

@ -2,19 +2,28 @@
import React from 'react'
import { useContext } from 'use-context-selector'
import Select from '@/app/components/base/select/locale'
import ThemeSelector from '@/app/components/base/theme-selector'
import Divider from '@/app/components/base/divider'
import { languages } from '@/i18n/language'
import type { Locale } from '@/i18n'
import I18n from '@/context/i18n'
import LogoSite from '@/app/components/base/logo/logo-site'
import dynamic from 'next/dynamic'
// Avoid rendering the logo and theme selector on the server
const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), {
ssr: false,
loading: () => <div className='h-7 w-16 bg-transparent' />,
})
const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), {
ssr: false,
loading: () => <div className='size-8 bg-transparent' />,
})
const Header = () => {
const { locale, setLocaleOnClient } = useContext(I18n)
return (
<div className='flex w-full items-center justify-between p-6'>
<LogoSite />
<DifyLogo size='large' />
<div className='flex items-center gap-1'>
<Select
value={locale}

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

@ -43,7 +43,7 @@ async function translateMissingKeyDeeply(sourceObj, targetObject, toLanguage) {
targetObject[key] = translation
}
catch {
console.error(`Error translating ${sourceObj[key]}(${key}) to ${toLanguage}`)
console.error(`Error translating "${sourceObj[key]}" to ${toLanguage}. Key: ${key}`)
}
}
}
@ -59,6 +59,14 @@ async function autoGenTrans(fileName, toGenLanguage) {
const toGenLanguageFilePath = path.join(__dirname, toGenLanguage, `${fileName}.ts`)
// eslint-disable-next-line sonarjs/code-eval
const fullKeyContent = eval(transpile(fs.readFileSync(fullKeyFilePath, 'utf8')))
// if toGenLanguageFilePath is not exist, create it
if (!fs.existsSync(toGenLanguageFilePath)) {
fs.writeFileSync(toGenLanguageFilePath, `const translation = {
}
export default translation
`)
}
// To keep object format and format it for magicast to work: const translation = { ... } => export default {...}
const readContent = await loadFile(toGenLanguageFilePath)
const { code: toGenContent } = generateCode(readContent)

@ -27,9 +27,14 @@ async function getKeysFromLanuage(language) {
// console.log(camelCaseFileName)
const content = fs.readFileSync(filePath, 'utf8')
// eslint-disable-next-line sonarjs/code-eval
const translation = eval(transpile(content))
const translationObj = eval(transpile(content))
// console.log(translation)
const keys = Object.keys(translation)
if(!translationObj || typeof translationObj !== 'object') {
console.error(`Error parsing file: ${filePath}`)
reject(new Error(`Error parsing file: ${filePath}`))
return
}
const keys = Object.keys(translationObj)
const nestedKeys = []
const iterateKeys = (obj, prefix = '') => {
for (const key in obj) {
@ -39,7 +44,7 @@ async function getKeysFromLanuage(language) {
iterateKeys(obj[key], nestedKey)
}
}
iterateKeys(translation)
iterateKeys(translationObj)
allKeys = [...keys, ...nestedKeys].map(
key => `${camelCaseFileName}.${key}`,

@ -161,6 +161,10 @@ const translation = {
description: 'Opik ist eine Open-Source-Plattform zum Bewerten, Testen und Überwachen von LLM-Anwendungen.',
title: 'Opik',
},
weave: {
title: 'Weben',
description: 'Weave ist eine Open-Source-Plattform zur Bewertung, Testung und Überwachung von LLM-Anwendungen.',
},
},
answerIcon: {
descriptionInExplore: 'Gibt an, ob das WebApp-Symbol zum Ersetzen 🤖 in Explore verwendet werden soll',
@ -201,6 +205,17 @@ const translation = {
label: 'APP',
noParams: 'Keine Parameter erforderlich',
},
structOutput: {
required: 'Erforderlich',
structured: 'Strukturiert',
structuredTip: 'Strukturierte Ausgaben ist eine Funktion, die sicherstellt, dass das Modell immer Antworten generiert, die Ihrem bereitgestellten JSON-Schema entsprechen.',
modelNotSupportedTip: 'Das aktuelle Modell unterstützt diese Funktion nicht und wird automatisch auf Eingabeinjektion heruntergestuft.',
modelNotSupported: 'Modell nicht unterstützt',
configure: 'Konfigurieren',
notConfiguredTip: 'Die strukturierte Ausgabe wurde bisher nicht konfiguriert.',
moreFillTip: 'Maximal 10 Ebenen der Verschachtelung anzeigen',
LLMResponse: 'LLM-Antwort',
},
}
export default translation

@ -69,6 +69,7 @@ const translation = {
messageRequest: {
title: 'Nachrichtenguthaben',
tooltip: 'Nachrichtenaufrufkontingente für verschiedene Tarife unter Verwendung von OpenAI-Modellen (außer gpt4).Nachrichten über dem Limit verwenden Ihren OpenAI-API-Schlüssel.',
titlePerMonth: '{{count,number}} Nachrichten/Monat',
},
annotatedResponse: {
title: 'Kontingentgrenzen für Annotationen',
@ -77,27 +78,94 @@ const translation = {
ragAPIRequestTooltip: 'Bezieht sich auf die Anzahl der API-Aufrufe, die nur die Wissensdatenbankverarbeitungsfähigkeiten von Dify aufrufen.',
receiptInfo: 'Nur der Teaminhaber und der Teamadministrator können abonnieren und Abrechnungsinformationen einsehen',
annotationQuota: 'Kontingent für Anmerkungen',
unlimitedApiRate: 'Keine API-Ratebeschränkung',
teamMember_other: '{{count,number}} Teammitglieder',
priceTip: 'pro Arbeitsbereich/',
teamWorkspace: '{{count,number}} Team Arbeitsplatz',
annualBilling: 'Jährliche Abrechnung',
self: 'Selbst gehostet',
freeTrialTipPrefix: 'Melden Sie sich an und erhalten Sie ein',
cloud: 'Cloud-Dienst',
apiRateLimitTooltip: 'Die API-Datenbeschränkung gilt für alle Anfragen, die über die Dify-API gemacht werden, einschließlich Textgenerierung, Chat-Konversationen, Workflow-Ausführungen und Dokumentenverarbeitung.',
getStarted: 'Loslegen',
apiRateLimitUnit: '{{count,number}}/Tag',
documentsTooltip: 'Vorgabe für die Anzahl der Dokumente, die aus der Wissensdatenquelle importiert werden.',
apiRateLimit: 'API-Datenlimit',
documents: '{{count,number}} Wissensdokumente',
comparePlanAndFeatures: 'Pläne und Funktionen vergleichen',
freeTrialTipSuffix: 'Keine Kreditkarte erforderlich',
freeTrialTip: 'kostenlose Testversion von 200 OpenAI-Anfragen.',
documentsRequestQuota: '{{count,number}}/min Wissensanforderungsratenlimit',
teamMember_one: '{{count,number}} Teammitglied',
documentsRequestQuotaTooltip: 'Gibt die Gesamtzahl der Aktionen an, die ein Arbeitsbereich pro Minute innerhalb der Wissensbasis ausführen kann, einschließlich der Erstellung, Löschung, Aktualisierung von Datensätzen, des Hochladens von Dokumenten, von Änderungen, der Archivierung und von Abfragen in der Wissensbasis. Diese Kennzahl wird verwendet, um die Leistung von Anfragen an die Wissensbasis zu bewerten. Wenn ein Sandbox-Nutzer beispielsweise in einer Minute 10 aufeinanderfolgende Testdurchläufe durchführt, wird sein Arbeitsbereich für die nächste Minute vorübergehend daran gehindert, die folgenden Aktionen auszuführen: Erstellung, Löschung, Aktualisierung von Datensätzen sowie das Hochladen oder Ändern von Dokumenten.',
},
plans: {
sandbox: {
name: 'Sandbox',
description: '200 mal GPT kostenlos testen',
includesTitle: 'Beinhaltet:',
for: 'Kostenlose Testversion der Kernfunktionen',
},
professional: {
name: 'Professionell',
description: 'Für Einzelpersonen und kleine Teams, um mehr Leistung erschwinglich freizuschalten.',
includesTitle: 'Alles im kostenlosen Tarif, plus:',
for: 'Für unabhängige Entwickler/kleine Teams',
},
team: {
name: 'Team',
description: 'Zusammenarbeiten ohne Grenzen und Top-Leistung genießen.',
includesTitle: 'Alles im Professionell-Tarif, plus:',
for: 'Für mittelgroße Teams',
},
enterprise: {
name: 'Unternehmen',
description: 'Erhalten Sie volle Fähigkeiten und Unterstützung für großangelegte, missionskritische Systeme.',
includesTitle: 'Alles im Team-Tarif, plus:',
features: {
2: 'Exklusive Unternehmensfunktionen',
8: 'Professioneller technischer Support',
6: 'Erweiterte Sicherheits- und Kontrollsysteme',
4: 'SSO',
0: 'Enterprise-Grade Skalierbare Bereitstellungslösungen',
3: 'Mehrere Arbeitsbereiche und Unternehmensverwaltung',
1: 'Kommerzielle Lizenzgenehmigung',
5: 'Verhandelte SLAs durch Dify-Partner',
7: 'Updates und Wartung von Dify offiziell',
},
btnText: 'Vertrieb kontaktieren',
price: 'Benutzerdefiniert',
priceTip: 'Jährliche Abrechnung nur',
for: 'Für große Teams',
},
community: {
features: {
2: 'Entspricht der Dify Open Source Lizenz',
1: 'Einzelner Arbeitsbereich',
0: 'Alle Kernfunktionen wurden im öffentlichen Repository veröffentlicht.',
},
description: 'Für Einzelbenutzer, kleine Teams oder nicht-kommerzielle Projekte',
for: 'Für Einzelbenutzer, kleine Teams oder nicht-kommerzielle Projekte',
btnText: 'Beginnen Sie mit der Gemeinschaft',
price: 'Kostenlos',
includesTitle: 'Kostenlose Funktionen:',
name: 'Gemeinschaft',
},
premium: {
features: {
2: 'WebApp-Logo und Branding-Anpassung',
0: 'Selbstverwaltete Zuverlässigkeit durch verschiedene Cloud-Anbieter',
3: 'Priorisierte E-Mail- und Chat-Unterstützung',
1: 'Einzelner Arbeitsbereich',
},
includesTitle: 'Alles aus der Community, plus:',
name: 'Premium',
priceTip: 'Basierend auf dem Cloud-Marktplatz',
for: 'Für mittelgroße Organisationen und Teams',
btnText: 'Jetzt Premium erhalten in',
comingSoon: 'Microsoft Azure- und Google Cloud-Support demnächst verfügbar',
description: 'Für mittelgroße Organisationen und Teams',
price: 'Skalierbar',
},
},
vectorSpace: {
@ -107,12 +175,26 @@ const translation = {
apps: {
fullTipLine1: 'Upgraden Sie Ihren Tarif, um',
fullTipLine2: 'mehr Apps zu bauen.',
contactUs: 'Kontaktieren Sie uns',
fullTip1: 'Upgrade, um mehr Apps zu erstellen',
fullTip2des: 'Es wird empfohlen, inaktive Anwendungen zu bereinigen, um Speicherplatz freizugeben, oder uns zu kontaktieren.',
fullTip1des: 'Sie haben das Limit für das Erstellen von Apps in diesem Plan erreicht.',
fullTip2: 'Limit erreicht',
},
annotatedResponse: {
fullTipLine1: 'Upgraden Sie Ihren Tarif, um',
fullTipLine2: 'mehr Konversationen zu annotieren.',
quotaTitle: 'Kontingent für Annotation-Antworten',
},
usagePage: {
buildApps: 'Apps erstellen',
annotationQuota: 'Annotierungsquote',
teamMembers: 'Teammitglieder',
documentsUploadQuota: 'Dokumenten-Upload-Quota',
vectorSpace: 'Wissensdatenbank',
vectorSpaceTooltip: 'Dokumente mit dem Hochqualitäts-Indexierungsmodus verbrauchen Ressourcen des Knowledge Data Storage. Wenn der Knowledge Data Storage die Grenze erreicht, werden keine neuen Dokumente hochgeladen.',
},
teamMembers: 'Teammitglieder',
}
export default translation

@ -54,6 +54,10 @@ const translation = {
viewDetails: 'Details anzeigen',
in: 'in',
copied: 'Kopiert',
downloadFailed: 'Download fehlgeschlagen. Bitte versuchen Sie es später erneut.',
downloadSuccess: 'Download abgeschlossen.',
more: 'Mehr',
format: 'Format',
},
placeholder: {
input: 'Bitte eingeben',
@ -153,6 +157,9 @@ const translation = {
community: 'Gemeinschaft',
about: 'Über',
logout: 'Abmelden',
compliance: 'Einhaltung',
support: 'Unterstützung',
github: 'GitHub',
},
settings: {
accountGroup: 'KONTO',
@ -202,6 +209,9 @@ const translation = {
feedbackLabel: 'Sagen Sie uns, warum Sie Ihr Konto gelöscht haben?',
feedbackPlaceholder: 'Wahlfrei',
permanentlyDeleteButton: 'Konto dauerhaft löschen',
workspaceIcon: 'Arbeitsbereichssymbol',
workspaceName: 'Arbeitsbereichsname',
editWorkspaceInfo: 'Arbeitsbereichsinformationen bearbeiten',
},
members: {
team: 'Team',
@ -543,6 +553,7 @@ const translation = {
inputPlaceholder: 'Sprechen Sie mit dem Bot',
thought: 'Gedanke',
thinking: 'Denken...',
resend: 'Erneut senden',
},
promptEditor: {
placeholder: 'Schreiben Sie hier Ihr Aufforderungswort, geben Sie \'{\' ein, um eine Variable einzufügen, geben Sie \'/\' ein, um einen Aufforderungs-Inhaltsblock einzufügen',
@ -637,6 +648,25 @@ const translation = {
pagination: {
perPage: 'Artikel pro Seite',
},
theme: {
light: 'Licht',
theme: 'Thema',
dark: 'dunkel',
auto: 'System',
},
compliance: {
iso27001: 'ISO 27001:2022 Zertifizierung',
professionalUpgradeTooltip: 'Nur verfügbar mit einem Teamplan oder höher.',
gdpr: 'DSGVO DPA',
soc2Type2: 'SOC 2 Typ II Bericht',
soc2Type1: 'SOC 2 Typ I Bericht',
sandboxUpgradeTooltip: 'Nur verfügbar mit einem Professional- oder Teamplan.',
},
imageInput: {
dropImageHere: 'Laden Sie Ihr Bild hierher hoch oder',
browse: 'blättern',
supportedFormats: 'Unterstützt PNG, JPG, JPEG, WEBP und GIF',
},
}
export default translation

@ -3,6 +3,8 @@ const translation = {
upgradeTip: {
prefix: 'Erweitere deinen Plan auf',
suffix: 'um deine Marke anzupassen.',
title: 'Upgrade deinen Plan',
des: 'Upgrade deinen Plan, um deine Marke anzupassen.',
},
webapp: {
title: 'WebApp Marke anpassen',

@ -82,6 +82,14 @@ const translation = {
jinaReaderNotConfiguredDescription: 'Richten Sie Jina Reader ein, indem Sie Ihren kostenlosen API-Schlüssel für den Zugriff eingeben.',
useSitemapTooltip: 'Folgen Sie der Sitemap, um die Website zu crawlen. Ist dies nicht der Fall, crawlt Jina Reader iterativ basierend auf der Seitenrelevanz, sodass weniger, aber qualitativ hochwertigere Seiten angezeigt werden.',
jinaReaderDoc: 'Erfahre mehr über Jina Reader',
configureJinaReader: 'Jina Reader konfigurieren',
waterCrawlNotConfigured: 'Watercrawl ist nicht konfiguriert',
configureWatercrawl: 'Wasserkrabbe konfigurieren',
watercrawlDocLink: 'https://docs.dify.ai/de/guides/knowledge-base/create-knowledge-and-upload-documents/import-content-data/sync-from-website',
watercrawlTitle: 'Webinhalt mit Watercrawl extrahieren',
watercrawlDoc: 'Wasserkriechen-Dokumente',
configureFirecrawl: 'Firecrawl konfigurieren',
waterCrawlNotConfiguredDescription: 'Konfigurieren Sie Watercrawl mit dem API-Schlüssel, um es zu verwenden.',
},
cancel: 'Abbrechen',
},
@ -200,6 +208,11 @@ const translation = {
title: 'Verbinden Sie sich mit anderen Datenquellen?',
description: 'Derzeit verfügt die Wissensdatenbank von Dify nur über begrenzte Datenquellen. Das Beitragen einer Datenquelle zur Dify-Wissensdatenbank ist eine fantastische Möglichkeit, die Flexibilität und Leistungsfähigkeit der Plattform für alle Benutzer zu verbessern. Unser Beitragsleitfaden erleichtert Ihnen den Einstieg. Bitte klicken Sie auf den untenstehenden Link, um mehr zu erfahren.',
},
watercrawl: {
configWatercrawl: 'Wasserkrabbe konfigurieren',
apiKeyPlaceholder: 'API-Schlüssel von watercrawl.dev',
getApiKeyLinkText: 'Holen Sie sich Ihren API-Schlüssel von watercrawl.dev',
},
}
export default translation

@ -25,6 +25,7 @@ const translation = {
learnMore: 'Mehr erfahren',
description: ' über die Abrufmethode.',
longDescription: ' über die Abrufmethode, dies kann jederzeit in den Wissenseinstellungen geändert werden.',
method: 'Abrufmethode',
},
save: 'Speichern',
permissionsInvitedMembers: 'Teilweise Teammitglieder',

@ -168,6 +168,54 @@ const translation = {
documentsDisabled: '{{num}} Dokumente deaktiviert - seit über 30 Tagen inaktiv',
allKnowledge: 'Alles Wissen',
allKnowledgeDescription: 'Wählen Sie diese Option aus, um das gesamte Wissen in diesem Arbeitsbereich anzuzeigen. Nur der Workspace-Besitzer kann das gesamte Wissen verwalten.',
metadata: {
createMetadata: {
namePlaceholder: 'Metadatenname hinzufügen',
back: 'Zurück',
title: 'Neue Metadaten',
name: 'Name',
type: 'Art',
},
checkName: {
empty: 'Der Metadatenname darf nicht leer sein.',
invalid: 'Der Metadatenname darf nur Kleinbuchstaben, Zahlen und Unterstriche enthalten und muss mit einem Kleinbuchstaben beginnen.',
},
batchEditMetadata: {
editMetadata: 'Metadaten bearbeiten',
multipleValue: 'Mehrwert',
applyToAllSelectDocument: 'Auf alle ausgewählten Dokumente anwenden',
applyToAllSelectDocumentTip: 'Erstellen Sie automatisch alle oben bearbeiteten und neuen Metadaten für alle ausgewählten Dokumente, andernfalls wird die Bearbeitung der Metadaten nur auf Dokumente angewendet, die bereits Metadaten enthalten.',
editDocumentsNum: 'Bearbeiten von {{num}} Dokumenten',
},
selectMetadata: {
manageAction: 'Verwalten',
search: 'Metadaten durchsuchen',
newAction: 'Neue Metadaten',
},
datasetMetadata: {
name: 'Name',
disabled: 'Deaktiviert',
description: 'Sie können alle Metadaten in diesem Wissen hier verwalten. Änderungen werden mit jedem Dokument synchronisiert.',
deleteContent: 'Bist du sicher, dass du die Metadaten "{{name}}" löschen möchtest?',
addMetaData: 'Metadaten hinzufügen',
deleteTitle: 'Bestätigen Sie das Löschen',
values: '{{num}} Werte',
builtIn: 'Eingebaut',
rename: 'Umbenennen',
builtInDescription: 'Integrierte Metadaten werden automatisch extrahiert und generiert. Sie müssen vor der Verwendung aktiviert werden und können nicht bearbeitet werden.',
namePlaceholder: 'Metadatenname',
},
documentMetadata: {
startLabeling: 'Labeling starten',
technicalParameters: 'Technische Parameter',
documentInformation: 'Dokumentinformationen',
metadataToolTip: 'Metadaten dienen als ein entscheidender Filter, der die Genauigkeit und Relevanz der Informationsbeschaffung verbessert. Sie können die Metadaten für dieses Dokument hier ändern und hinzufügen.',
},
chooseTime: 'Wählen Sie eine Zeit...',
metadata: 'Metadaten',
addMetadata: 'Metadaten hinzufügen',
},
embeddingModelNotAvailable: 'Das Einbettungsmodell ist nicht verfügbar.',
}
export default translation

@ -0,0 +1,47 @@
const translation = {
toVerifiedTip: {
coupon: 'exklusiver 100% Gutschein',
end: 'für den Dify Professional Plan.',
front: 'Sie sind jetzt berechtigt, den Status „Bildung verifiziert“ zu erhalten. Bitte geben Sie unten Ihre Bildungsinformationen ein, um den Prozess abzuschließen und eine Zu erhalten.',
},
form: {
schoolName: {
placeholder: 'Geben Sie den offiziellen, unabgekürzten Namen Ihrer Schule ein.',
title: 'Ihr Schulname',
},
schoolRole: {
option: {
teacher: 'Lehrer',
administrator: 'Schuladministrator',
student: 'Schüler',
},
title: 'Ihre Schulrolle',
},
terms: {
desc: {
and: 'und',
privacyPolicy: 'Datenschutzrichtlinie',
termsOfService: 'Nutzungsbedingungen',
end: '. Durch die Einreichung:',
front: 'Ihre Informationen und die Nutzung des Status "Bildung bestätigt" unterliegen unseren',
},
option: {
inSchool: 'Ich bestätige, dass ich an der angegebenen Einrichtung eingeschrieben oder angestellt bin. Dify kann einen Nachweis über die Einschreibung/Anstellung anfordern. Wenn ich meine Berechtigung falsch darstelle, stimme ich zu, alle Gebühren zu zahlen, die aufgrund meines Bildungsstatus ursprünglich erlassen wurden.',
age: 'Ich bestätige, dass ich mindestens 18 Jahre alt bin.',
},
title: 'Allgemeine Geschäftsbedingungen',
},
},
toVerified: 'Bildung überprüfen lassen',
rejectTitle: 'Ihre Dify-Ausbildungsüberprüfung wurde abgelehnt.',
currentSigned: 'DERZEIT ANGEMELDET ALS',
submit: 'Einreichen',
submitError: 'Die Formularübermittlung ist fehlgeschlagen. Bitte versuchen Sie es später erneut.',
rejectContent: 'Leider sind Sie nicht für den Status "Education Verified" berechtigt und können daher den exklusiven 100%-Gutschein für den Dify Professional Plan nicht erhalten, wenn Sie diese E-Mail-Adresse verwenden.',
successContent: 'Wir haben einen 100% Rabattgutschein für den Dify Professional Plan auf Ihr Konto ausgestellt. Der Gutschein ist ein Jahr lang gültig, bitte nutzen Sie ihn innerhalb des Gültigkeitszeitraums.',
learn: 'Erfahren Sie, wie Sie Ihre Ausbildung überprüfen lassen.',
emailLabel: 'Ihre aktuelle E-Mail',
successTitle: 'Sie haben die Dify-Ausbildung verifiziert',
}
export default translation

@ -37,6 +37,7 @@ const translation = {
HR: 'Personalwesen',
Agent: 'Agent',
Workflow: 'Arbeitsablauf',
Entertainment: 'Unterhaltung',
},
}

@ -180,6 +180,8 @@ const translation = {
pluginsResult: '{{num}} Ergebnisse',
empower: 'Unterstützen Sie Ihre KI-Entwicklung',
and: 'und',
partnerTip: 'Von einem Dify-Partner verifiziert',
verifiedTip: 'Von Dify überprüft',
},
task: {
clearAll: 'Alle löschen',
@ -204,6 +206,10 @@ const translation = {
findMoreInMarketplace: 'Weitere Informationen finden Sie im Marketplace',
installPlugin: 'Plugin installieren',
installFrom: 'INSTALLIEREN VON',
metadata: {
title: 'Plugins',
},
difyVersionNotCompatible: 'Die aktuelle Dify-Version ist mit diesem Plugin nicht kompatibel, bitte aktualisieren Sie auf die erforderliche Mindestversion: {{minimalDifyVersion}}',
}
export default translation

@ -32,6 +32,10 @@ const translation = {
temporarySystemIssue: 'Entschuldigung, vorübergehendes Systemproblem.',
expand: 'Erweitern',
collapse: 'Reduzieren',
chatSettingsTitle: 'Neues Chat-Setup',
newChatTip: 'Bereits in einem neuen Chat',
viewChatSettings: 'Chateinstellungen anzeigen',
chatFormTip: 'Chat-Einstellungen können nach Beginn des Chats nicht mehr geändert werden.',
},
generation: {
tabs: {
@ -70,6 +74,8 @@ const translation = {
moreThanMaxLengthLine: 'Zeile {{rowIndex}}: {{varName}} Wert darf nicht mehr als {{maxLength}} Zeichen sein',
atLeastOne: 'Bitte geben Sie mindestens eine Zeile in die hochgeladene Datei ein.',
},
executions: '{{num}} HINRICHTUNGEN',
execution: 'AUSFÜHRUNG',
},
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save