merge: bring origin/main into feat/creator-profile-home

This commit is contained in:
CodingOnStar 2026-08-25 11:28:23 +08:00
commit ebe06c7eee
242 changed files with 6643 additions and 5308 deletions

View File

@ -1,13 +0,0 @@
have_fun: false
memory_config:
disabled: false
code_review:
disable: true
comment_severity_threshold: MEDIUM
max_review_comments: -1
pull_request_opened:
help: false
summary: false
code_review: false
include_drafts: false
ignore_patterns: []

View File

@ -9,7 +9,7 @@ runs:
with:
run_install: false
- name: Setup Vite+
uses: voidzero-dev/setup-vp@313600b80b104eadebb9111787d37a2e83e014ca # v1.17.0
uses: voidzero-dev/setup-vp@1b32467adbe183473499fd9d5d372c3ed9641754 # v1.18.0
with:
node-version-file: .nvmrc
cache: true

View File

@ -0,0 +1,25 @@
name: Deploy HITL IM Dev
on:
workflow_run:
workflows: ['Build and Push API & Web']
branches:
- 'build/feat/hitl-im-dev'
types:
- completed
jobs:
deploy:
runs-on: depot-ubuntu-24.04
if: |
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'build/feat/hitl-im-dev'
steps:
- name: Deploy to server
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
with:
host: ${{ secrets.HITL_SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
${{ vars.SSH_SCRIPT || secrets.SSH_SCRIPT }}

View File

@ -129,87 +129,35 @@ All of Dify's offerings come with corresponding APIs, so you could effortlessly
Star Dify on GitHub and be instantly notified of new releases.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Advanced Setup
### Custom configurations
If you need to customize the configuration, edit `docker/.env`. The essential startup defaults live in [`docker/.env.example`](docker/.env.example), and optional advanced variables are split under `docker/envs/` by theme. After making any changes, re-run `docker compose up -d` from the `docker` directory. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### Metrics Monitoring with Grafana
Import the dashboard to Grafana, using Dify's PostgreSQL database as data source, to monitor metrics in granularity of apps, tenants, messages, and more.
- [Grafana Dashboard by @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Deployment with Kubernetes
If you'd like to configure a highly available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Using Terraform for Deployment
Deploy Dify to Cloud Platform with a single click using [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Using AWS CDK for Deployment
Deploy Dify to AWS with [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Using Alibaba Cloud Computing Nest
Quickly deploy Dify to Alibaba cloud with [Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Using Alibaba Cloud Data Management
One-Click deploy Dify to Alibaba Cloud with [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Deploy to AKS with Azure Devops Pipeline
One-Click deploy Dify to AKS with [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### Using Sealos for Deployment
Deploy Dify with one click using the [Sealos App Store](https://sealos.io/products/app-store/dify/)
For custom configuration, observability, and deployment options, see [Advanced Setup](docs/ADVANCED_SETUP.md).
## Contributing
For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
At the same time, please consider supporting Dify by sharing it on social media and at events and conferences.
Dify welcomes contributions of all kinds:
> We are looking for contributors to help translate Dify into languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c).
- **Code**: Read the [Contribution Guide](CONTRIBUTING.md), then browse [good first issues](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ideas and feedback**: Start or join a [GitHub Discussion](https://github.com/langgenius/dify/discussions).
- **Translations**: Follow the [internationalization guide](web/i18n-config/README.md) to add or update a locale.
- **Community**: Share the apps you build, help other users, and spread the word about Dify.
### Contributors
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Community & contact
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and asking questions.
- [GitHub Issues](https://github.com/langgenius/dify/issues). Best for: bugs you encounter using Dify.AI, and feature proposals. See our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Best for: sharing your applications and hanging out with the community.
- [X(Twitter)](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
Choose the channel that best fits your question:
**Contributors**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): Get help, share feedback, and propose ideas.
- [GitHub Issues](https://github.com/langgenius/dify/issues): Report reproducible bugs and track engineering work. Read the [Contribution Guide](CONTRIBUTING.md) before opening one.
- [Discord](https://discord.gg/FngNHpbcY7): Chat in real time, share your apps, and connect with other Dify users.
- [X](https://x.com/dify_ai): Follow Dify for release news and project updates.
## Star History

View File

@ -15,6 +15,117 @@ root_packages =
services
include_external_packages = True
[importlinter:contract:backend-layers]
name = Backend modules follow the transport, application, domain, and shared-library layers
type = layers
layers =
controllers
services
core
libs
# Migration baseline: keep every exception exact. The default unmatched-import
# error makes this list shrink whenever a legacy dependency is removed.
unmatched_ignore_imports_alerting = error
ignore_imports =
core.app.app_config.easy_ui_based_app.dataset.manager -> services.dataset_service
core.app.apps.advanced_chat.app_generator -> controllers.console.app.workflow
core.app.apps.advanced_chat.app_generator -> services.conversation_service
core.app.apps.advanced_chat.app_generator -> services.errors.conversation
core.app.apps.advanced_chat.app_generator -> services.workflow_draft_variable_service
core.app.apps.advanced_chat.app_runner -> services.conversation_variable_updater
core.app.apps.agent_app.app_generator -> services.agent.composer_service
core.app.apps.agent_app.app_generator -> services.agent.workspace_service
core.app.apps.agent_app.app_generator -> services.conversation_service
core.app.apps.agent_app.runtime_request_builder -> services.agent.prompt_mentions
core.app.apps.agent_app.session_store -> services.agent.workspace_service
core.app.apps.agent_chat.app_generator -> services.conversation_service
core.app.apps.base_app_generator -> services.workflow_draft_variable_service
core.app.apps.chat.app_generator -> services.conversation_service
core.app.apps.common.workflow_response_converter -> services.variable_truncator
core.app.apps.completion.app_generator -> services.errors.app
core.app.apps.completion.app_generator -> services.errors.message
core.app.apps.message_based_app_generator -> services.errors.app_model_config
core.app.apps.message_based_app_generator -> services.errors.conversation
core.app.apps.message_based_app_generator -> services.errors.message
core.app.apps.pipeline.pipeline_generator -> services
core.app.apps.pipeline.pipeline_generator -> services.dataset_service
core.app.apps.pipeline.pipeline_generator -> services.datasource_provider_service
core.app.apps.pipeline.pipeline_generator -> services.feature_service
core.app.apps.pipeline.pipeline_generator -> services.workflow_draft_variable_service
core.app.apps.pipeline.pipeline_runner -> services.dataset_ref_service
core.app.apps.workflow.app_generator -> controllers.console.app.workflow
core.app.apps.workflow.app_generator -> services.snippet_generate_service
core.app.apps.workflow.app_generator -> services.workflow_draft_variable_service
core.app.apps.workflow_app_runner -> tasks.mail_human_input_delivery_task
core.app.features.annotation_reply.annotation_reply -> services.annotation_service
core.app.features.annotation_reply.annotation_reply -> services.dataset_service
core.app.llm.quota -> services.credit_pool_service
core.app.task_pipeline.message_cycle_manager -> services.account_service
core.app.task_pipeline.message_cycle_manager -> services.annotation_service
core.app.workflow.layers.persistence -> services.workflow.inspector_events
core.datasource.datasource_manager -> services.datasource_provider_service
core.helper.creators -> services.oauth_server
core.helper.credential_utils -> services.enterprise.plugin_manager_service
core.helper.credential_utils -> services.feature_service
core.indexing_runner -> services.vector_space_admission_service
core.mcp.auth_client -> services
core.provider_manager -> services.credential_permission_service
core.provider_manager -> services.credit_pool_service
core.provider_manager -> services.feature_service
core.rag.datasource.retrieval_service -> services.external_knowledge_service
core.rag.index_processor.index_processor -> services.vector_space_admission_service
core.rag.index_processor.index_processor_base -> services.file_service
core.rag.index_processor.processor.paragraph_index_processor -> services.account_service
core.rag.index_processor.processor.paragraph_index_processor -> services.summary_index_service
core.rag.index_processor.processor.parent_child_index_processor -> services.account_service
core.rag.index_processor.processor.parent_child_index_processor -> services.summary_index_service
core.rag.index_processor.processor.qa_index_processor -> services.summary_index_service
core.rag.retrieval.dataset_retrieval -> services.external_knowledge_service
core.rag.retrieval.dataset_retrieval -> services.feature_service
core.rag.summary_index.summary_index -> services.summary_index_service
core.rag.summary_index.summary_index -> tasks.generate_summary_index_task
core.repositories.sqlalchemy_workflow_node_execution_repository -> services.file_service
core.repositories.sqlalchemy_workflow_node_execution_repository -> services.variable_truncator
core.tools.tool_manager -> services
core.workflow.node_runtime -> services
core.workflow.nodes.agent.message_transformer -> services
core.workflow.nodes.agent_v2.agent_node -> services.agent.prompt_mentions
core.workflow.nodes.agent_v2.agent_node -> services.agent.workspace_service
core.workflow.nodes.agent_v2.dify_tools_builder -> services
core.workflow.nodes.agent_v2.runtime_feature_manifest -> services.agent.knowledge_datasets
core.workflow.nodes.agent_v2.runtime_request_builder -> services.agent.prompt_mentions
core.workflow.nodes.agent_v2.runtime_request_builder -> services.skill_management_service
core.workflow.nodes.agent_v2.session_store -> services.agent.workspace_service
core.workflow.nodes.agent_v2.validators -> services.agent.knowledge_datasets
core.workflow.nodes.agent_v2.workspace_retirement_layer -> tasks.collect_agent_resources_task
libs.device_flow_security -> controllers.openapi._models
libs.device_flow_security -> services.entities.feature_entities
libs.device_flow_security -> services.feature_service
libs.email_i18n -> services.entities.feature_entities
libs.email_i18n -> services.feature_service
libs.external_api -> core
libs.external_api -> core.errors.error
libs.external_api -> extensions.ext_logging
libs.flask_utils -> models
libs.helper -> core.app.features.rate_limiting.rate_limit
libs.helper -> models
libs.helper -> models.model
libs.json_in_md_parser -> core.llm_generator.output_parser.errors
libs.login -> extensions.ext_login
libs.login -> models
libs.login -> models.model
libs.oauth -> core.helper.http_client_pooling
libs.oauth_bearer -> models
libs.oauth_data_source -> core.db.session_factory
libs.oauth_data_source -> core.helper.http_client_pooling
libs.rsa -> extensions.ext_storage
libs.workspace_permission -> services.enterprise.enterprise_service
libs.workspace_permission -> services.feature_service
services.account_service -> controllers
services.account_service -> controllers.console.error
services.app_generate_service -> controllers.console.app.workflow
services.billing_service -> controllers.console.error
[importlinter:contract:no-direct-rsa-imports]
# Note: `libs` itself is deliberately excluded from source_modules -- import-linter's
# `forbidden` contract cannot check a package against its own descendant (libs.rsa lives

View File

@ -75,18 +75,18 @@ class FeatureVectorSpaceApi(Resource):
@console_ns.route("/trial-models")
class TrialModelsApi(Resource):
@console_ns.doc("get_trial_models")
@console_ns.doc(description="Get hosted trial model provider configuration")
@console_ns.doc(description="Get hosted credit model provider configuration for the current workspace")
@console_ns.response(
200,
"Success",
console_ns.models[TrialModelsResponse.__name__],
)
@console_account_admission()
def get(self, _request_context: RequestContext):
"""Get hosted trial model provider configuration for model-provider pages."""
def get(self, request_context: RequestContext):
"""Get hosted credit provider configuration for the current workspace."""
return dump_response(
TrialModelsResponse,
{"trial_models": application_services().feature_queries.get_trial_models()},
{"trial_models": application_services().feature_queries.get_trial_models(request_context)},
)

View File

@ -31,6 +31,8 @@ from controllers.console.auth.error import (
from controllers.console.error import (
AccountInFreezeError,
AccountNotFound,
EducationActivateLimitError,
EducationVerifyLimitError,
EmailDomainSuspendedError,
EmailSendIpLimitError,
)
@ -239,6 +241,10 @@ class EducationVerifyResponse(ResponseModel):
token: str | None = None
class EducationActivateResponse(ResponseModel):
message: str
class EducationStatusResponse(ResponseModel):
result: bool | None = None
is_student: bool | None = None
@ -263,6 +269,7 @@ register_response_schema_models(
AccountIntegrateResponse,
AccountIntegrateListResponse,
AvatarUrlResponse,
EducationActivateResponse,
EducationVerifyResponse,
EducationStatusResponse,
EducationAutocompleteResponse,
@ -528,29 +535,33 @@ class EducationVerifyApi(Resource):
def get(self, request_context: RequestContext):
try:
verification = application_services().accounts.education.verify(request_context)
except account_errors.AccountNotFoundError:
raise AccountNotFound() from None
except account_errors.AccountNotFoundError as error:
raise AccountNotFound() from error
except account_errors.EducationRateLimitExceededError as error:
raise EducationVerifyLimitError() from error
return dump_response(EducationVerifyResponse, verification)
@console_ns.route("/account/education")
class EducationApi(Resource):
@console_ns.expect(console_ns.models[EducationActivatePayload.__name__])
# response-contract:ignore billing-service activation payload; TODO: model education activation result.
@console_ns.response(HTTPStatus.OK, "Success")
@console_ns.response(HTTPStatus.OK, "Success", console_ns.models[EducationActivateResponse.__name__])
@console_account_admission(editions=frozenset({DeploymentEdition.CLOUD}))
def post(self, request_context: RequestContext):
payload = console_ns.payload or {}
args = EducationActivatePayload.model_validate(payload)
try:
return application_services().accounts.education.activate(
activation = application_services().accounts.education.activate(
request_context,
token=args.token,
institution=args.institution,
role=args.role,
)
except account_errors.AccountNotFoundError:
raise AccountNotFound() from None
except account_errors.AccountNotFoundError as error:
raise AccountNotFound() from error
except account_errors.EducationRateLimitExceededError as error:
raise EducationActivateLimitError() from error
return dump_response(EducationActivateResponse, activation)
@console_ns.response(HTTPStatus.OK, "Success", console_ns.models[EducationStatusResponse.__name__])
@console_account_admission(editions=frozenset({DeploymentEdition.CLOUD}))

View File

@ -417,6 +417,7 @@ class ModelProviderPaymentCheckoutUrlApi(Resource):
def get(self, current_tenant_id: str, current_user: Account, provider: str):
if provider != "anthropic":
raise ValueError(f"provider name {provider} is invalid")
# pyrefly: ignore [deprecated]
data = BillingService.get_model_provider_payment_link(
provider_name=provider,
tenant_id=current_tenant_id,

View File

@ -8,7 +8,7 @@ from werkzeug.exceptions import Forbidden
import services
from core.db.session_factory import session_factory
from core.tools.signature import verify_plugin_file_signature
from core.tools.signature import sign_tool_file, verify_plugin_file_signature
from core.tools.tool_file_manager import ToolFileManager, resolve_extension
from core.workflow.file_reference import build_file_reference
from fields.file_fields import FileResponse
@ -136,7 +136,7 @@ class PluginUploadFileApi(Resource):
)
extension = resolve_extension(filename=tool_file.name, mimetype=tool_file.mimetype)
preview_url = ToolFileManager.sign_file(tool_file_id=tool_file.id, extension=extension)
preview_url = sign_tool_file(tool_file_id=tool_file.id, extension=extension, for_external=True)
# Create a dictionary with all the necessary attributes
result = FileResponse(

View File

@ -945,7 +945,7 @@ class LLMGenerator:
)
),
]
model_parameters = {"temperature": 0.4}
model_parameters, stop = _normalize_completion_params(model_config.completion_params)
response: LLMResult | None = None
error: str | None = None
@ -954,7 +954,10 @@ class LLMGenerator:
with measure_time() as timer:
try:
response = model_instance.invoke_llm(
prompt_messages=list(prompt_messages), model_parameters=model_parameters, stream=False
prompt_messages=list(prompt_messages),
model_parameters=model_parameters,
stop=stop,
stream=False,
)
generated_raw = response.message.get_text_content()
first_brace = generated_raw.find("{")

View File

@ -74,6 +74,7 @@ class WorkflowAgentNodeValidator:
session=session,
workflow=workflow,
require_binding=False,
require_agent_model=False,
validate_previous_node_topology=False,
)
@ -83,6 +84,7 @@ class WorkflowAgentNodeValidator:
session=session,
workflow=workflow,
require_binding=True,
require_agent_model=True,
validate_previous_node_topology=True,
)
@ -93,6 +95,7 @@ class WorkflowAgentNodeValidator:
session: Session,
workflow: Workflow,
require_binding: bool,
require_agent_model: bool,
validate_previous_node_topology: bool,
) -> None:
graph = workflow.graph_dict
@ -112,7 +115,12 @@ class WorkflowAgentNodeValidator:
f"Workflow Agent node {node_id} requires a binding before publishing."
)
continue
cls.validate_binding(session=session, binding=binding, topology=topology)
cls.validate_binding(
session=session,
binding=binding,
topology=topology,
require_agent_model=require_agent_model,
)
if require_binding:
for node_id, node_data in cls.iter_tool_nodes(graph):
@ -125,6 +133,7 @@ class WorkflowAgentNodeValidator:
session: Session,
binding: WorkflowAgentNodeBinding,
topology: _WorkflowGraphTopology | None = None,
require_agent_model: bool = True,
) -> None:
"""Validate binding ownership, publication state, Agent Soul, and node-job references."""
@ -174,7 +183,7 @@ class WorkflowAgentNodeValidator:
)
agent_soul = AgentSoulConfig.model_validate(snapshot.config_snapshot_dict)
if agent_soul.model is None:
if require_agent_model and agent_soul.model is None:
raise WorkflowAgentNodeValidationError(
f"Workflow Agent node {binding.node_id} requires Agent Soul model config."
)

View File

@ -14,6 +14,10 @@ class CloudPlan(StrEnum):
PROFESSIONAL = auto()
TEAM = auto()
@property
def is_paid(self) -> bool:
return self in (CloudPlan.PROFESSIONAL, CloudPlan.TEAM)
class DeploymentEdition(StrEnum):
"""Enum representing the deployment edition of the platform."""

View File

@ -233,6 +233,18 @@ def build_application_services(
education=AccountEducationService(
accounts=accounts,
education=BillingAccountEducationGateway(),
verification_rate_limiter=RateLimiter(
prefix="edu_verification_rate_limit",
max_attempts=10,
time_window=60,
redis_client=redis,
),
activation_rate_limiter=RateLimiter(
prefix="edu_activation_rate_limit",
max_attempts=10,
time_window=60,
redis_client=redis,
),
),
initialization=AccountInitializationService(
accounts=accounts,
@ -298,7 +310,6 @@ def build_application_services(
),
feature_queries=FeatureQueryService(
features=feature_gateway,
trial_models=FeatureService.get_trial_models(),
app_dsl_version=CURRENT_APP_DSL_VERSION,
),
init_validation=InitValidationService(

View File

@ -280,9 +280,9 @@ class AliyunLogStore:
else:
logger.info("Using SDK mode for project %s", self.project_name)
return False
except Exception as e:
except Exception:
logger.info("Using SDK mode for project %s", self.project_name)
logger.debug("PG connection details: %s", str(e))
logger.debug("PG connection details", exc_info=True)
self._use_pg_protocol = False
return False

View File

@ -42,8 +42,8 @@ class AliyunLogStorePG:
result = sock.connect_ex((host, port))
sock.close()
return result == 0
except Exception as e:
logger.debug("Port connectivity check failed for %s:%d: %s", host, port, str(e))
except Exception:
logger.debug("Port connectivity check failed for %s:%d", host, port, exc_info=True)
return False
def init_connection(self) -> bool:
@ -100,7 +100,7 @@ class AliyunLogStorePG:
)
return True
except Exception as e:
except Exception:
self._use_pg_protocol = False
if self._engine:
try:
@ -109,7 +109,7 @@ class AliyunLogStorePG:
logger.debug("Failed to dispose engine during cleanup, ignoring")
self._engine = None
logger.debug("Using SDK mode for region: %s", str(e))
logger.debug("Using SDK mode for region", exc_info=True)
return False
@contextmanager

View File

@ -1662,16 +1662,28 @@ class Message(Base):
if not self.answer:
return self.answer
pattern = r"\[!?.*?\]\((((http|https):\/\/.+)?\/files\/(tools\/)?[\w-]+.*?timestamp=.*&nonce=.*&sign=.*)\)"
matches = re.findall(pattern, self.answer)
if not matches:
return self.answer
urls = [match[0] for match in matches]
# remove duplicate urls
urls = list(set(urls))
# Match file URLs in three shapes the agent runtime may produce:
# - Markdown link: [text](https://.../files/tools/.../timestamp=&nonce=&sign=)
# - Backticked link: `https://.../files/tools/.../timestamp=&nonce=&sign=`
# - Bare URL: https://.../files/tools/.../timestamp=&nonce=&sign=
# The original implementation only matched the markdown form, so
# bare and backticked tool file URLs kept the long-lived
# INTERNAL_FILES_URL host and 5xx-ed at serve time. Refs #40788.
# The host prefix is optional so relative `/files/...` URLs that
# the agent returns without a host are also covered. The
# `(?=[)\s`]|$)` at the end of the bare-URL pattern (and the
# closing backtick / paren on the wrapped forms) stops the
# greedy `.*?=.*?` after `&sign=` from running off the end of
# the answer.
url_core = r"(?:https?:\/\/.+?)?\/files\/(tools\/)?[\w-]+.*?timestamp=[^)\s]*?&nonce=[^)\s]*?&sign=[^)\s]*?"
patterns = [
r"\[!?.*?\]\((" + url_core + r")\)", # [text](url)
r"`(" + url_core + r")`", # `url`
r"(?:^|\s|\()(" + url_core + r")(?:[\s)\].,;]|$)", # bare url
]
urls: set[str] = set()
for pattern in patterns:
urls.update(m.group(1) for m in re.finditer(pattern, self.answer))
if not urls:
return self.answer
@ -1718,6 +1730,7 @@ class Message(Base):
result = re.search(upload_file_id_pattern, url)
if not result:
continue
upload_file_id = result.group(1)
if not upload_file_id:
continue

View File

@ -592,7 +592,7 @@ class LangFuseDataTrace(BaseTraceInstance):
try:
return self.langfuse_client.auth_check()
except Exception as e:
logger.debug("LangFuse API check failed: %s", str(e))
logger.debug("LangFuse API check failed", exc_info=True)
raise ValueError(f"LangFuse API check failed: {str(e)}")
def get_project_key(self):
@ -600,5 +600,5 @@ class LangFuseDataTrace(BaseTraceInstance):
projects = self.langfuse_client.api.projects.get()
return projects.data[0].id
except Exception as e:
logger.debug("LangFuse get project key failed: %s", str(e))
logger.debug("LangFuse get project key failed", exc_info=True)
raise ValueError(f"LangFuse get project key failed: {str(e)}")

View File

@ -509,7 +509,7 @@ class LangSmithDataTrace(BaseTraceInstance):
self.langsmith_client.delete_project(project_name=random_project_name)
return True
except Exception as e:
logger.debug("LangSmith API check failed: %s", str(e))
logger.debug("LangSmith API check failed", exc_info=True)
raise ValueError(f"LangSmith API check failed: {str(e)}")
def get_project_url(self):
@ -528,5 +528,5 @@ class LangSmithDataTrace(BaseTraceInstance):
)
return project_url.split("/r/")[0]
except Exception as e:
logger.debug("LangSmith get run url failed: %s", str(e))
logger.debug("LangSmith get run url failed", exc_info=True)
raise ValueError(f"LangSmith get run url failed: {str(e)}")

View File

@ -75,7 +75,7 @@ class WeaveDataTrace(BaseTraceInstance):
project_url = f"https://wandb.ai/{project_identifier}"
return project_url
except Exception as e:
logger.debug("Weave get run url failed: %s", str(e))
logger.debug("Weave get run url failed", exc_info=True)
raise ValueError(f"Weave get run url failed: {str(e)}")
@override
@ -433,7 +433,7 @@ class WeaveDataTrace(BaseTraceInstance):
logger.info("Weave login successful")
return True
except Exception as e:
logger.debug("Weave API check failed: %s", str(e))
logger.debug("Weave API check failed", exc_info=True)
raise ValueError(f"Weave API check failed: {str(e)}")
def _normalize_time(self, dt: datetime | None) -> datetime:

View File

@ -6,7 +6,7 @@ requires-python = "~=3.12.0"
dependencies = [
# Legacy: mature and widely deployed
"bleach>=6.4.0,<7.0.0",
"boto3>=1.43.71,<2.0.0",
"boto3>=1.43.76,<2.0.0",
"celery>=5.6.3,<6.0.0",
"croniter>=6.2.2,<7.0.0",
"dify-agent",

View File

@ -11,6 +11,7 @@ from libs.helper import escape_like_pattern
from models.dataset import Dataset
from models.enums import TagType
from models.model import App, Tag, TagBinding
from models.skill import Skill
from models.snippet import CustomizedSnippet
from services.tag_application_service import (
CreateTagInput,
@ -207,6 +208,8 @@ class TagRepository(TagStore):
CustomizedSnippet.tenant_id == workspace_id,
CustomizedSnippet.id == binding.target_id,
)
elif binding.type == "skill":
stmt = select(Skill.id).where(Skill.tenant_id == workspace_id, Skill.id == binding.target_id)
else:
raise InvalidTagBindingTypeError

View File

@ -1,33 +1,23 @@
"""Billing adapters for account education and deletion-feedback use cases."""
from dataclasses import dataclass
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any, cast, override
from typing import override
from services.account_deletion_feedback_service import AccountDeletionFeedbackGateway
from services.account_education_service import AccountEducationGateway
from services.billing_service import BillingService
from services.entities.account_entities import (
AccountEducationActivation,
AccountEducationAutocomplete,
AccountEducationStatus,
AccountEducationVerification,
)
if TYPE_CHECKING:
from models.account import Account
@dataclass(frozen=True, slots=True)
class _EducationAccount:
id: str
email: str
current_tenant_id: str
class BillingAccountEducationGateway(AccountEducationGateway):
@override
def verify(self, *, account_id: str, email: str) -> AccountEducationVerification:
result = BillingService.EducationIdentity.verify(account_id, email) or {}
def verify(self, *, account_id: str) -> AccountEducationVerification:
result = BillingService.EducationIdentity.verify(account_id=account_id) or {}
return AccountEducationVerification(token=result.get("token"))
@override
@ -35,18 +25,23 @@ class BillingAccountEducationGateway(AccountEducationGateway):
self,
*,
account_id: str,
email: str,
tenant_id: str,
token: str,
institution: str,
role: str,
) -> dict[str, Any] | None:
account = cast("Account", _EducationAccount(id=account_id, email=email, current_tenant_id=tenant_id))
return BillingService.EducationIdentity.activate(account, token, institution, role)
) -> AccountEducationActivation:
result = BillingService.EducationIdentity.activate(
account_id=account_id,
tenant_id=tenant_id,
token=token,
institution=institution,
role=role,
)
return AccountEducationActivation(message=result["message"])
@override
def status(self, account_id: str) -> AccountEducationStatus:
result: dict[str, Any] = BillingService.EducationIdentity.status(account_id) or {}
result = BillingService.EducationIdentity.status(account_id) or {}
expire_at = result.get("expire_at")
return AccountEducationStatus(
result=result.get("result"),
@ -57,7 +52,7 @@ class BillingAccountEducationGateway(AccountEducationGateway):
@override
def autocomplete(self, *, keywords: str, page: int, limit: int) -> AccountEducationAutocomplete:
result: dict[str, Any] = BillingService.EducationIdentity.autocomplete(keywords, page, limit) or {}
result = BillingService.EducationIdentity.autocomplete(keywords, page, limit) or {}
return AccountEducationAutocomplete(
data=tuple(result.get("data") or ()),
curr_page=result.get("curr_page"),

View File

@ -1,31 +1,37 @@
"""Application service for account education-discount use cases."""
from typing import Any, Protocol
from typing import Protocol
from machinery.context import RequestContext
from machinery.errors import ActiveWorkspaceRequiredError
from services.account_errors import AccountNotFoundError
from services.account_errors import AccountNotFoundError, EducationRateLimitExceededError
from services.account_ports import AccountRepository
from services.entities.account_entities import (
AccountEducationActivation,
AccountEducationAutocomplete,
AccountEducationStatus,
AccountEducationVerification,
)
class AccountEducationRateLimiter(Protocol):
def is_rate_limited(self, key: str, /) -> bool: ...
def increment_rate_limit(self, key: str, /) -> None: ...
class AccountEducationGateway(Protocol):
def verify(self, *, account_id: str, email: str) -> AccountEducationVerification: ...
def verify(self, *, account_id: str) -> AccountEducationVerification: ...
def activate(
self,
*,
account_id: str,
email: str,
tenant_id: str,
token: str,
institution: str,
role: str,
) -> dict[str, Any] | None: ...
) -> AccountEducationActivation: ...
def status(self, account_id: str) -> AccountEducationStatus: ...
@ -38,15 +44,22 @@ class AccountEducationService:
*,
accounts: AccountRepository,
education: AccountEducationGateway,
verification_rate_limiter: AccountEducationRateLimiter,
activation_rate_limiter: AccountEducationRateLimiter,
) -> None:
self._accounts = accounts
self._education = education
self._verification_rate_limiter = verification_rate_limiter
self._activation_rate_limiter = activation_rate_limiter
def verify(self, context: RequestContext) -> AccountEducationVerification:
account = self._accounts.get(context.account_id)
if account is None:
raise AccountNotFoundError
return self._education.verify(account_id=account.id, email=account.email)
if self._verification_rate_limiter.is_rate_limited(account.email):
raise EducationRateLimitExceededError
self._verification_rate_limiter.increment_rate_limit(account.email)
return self._education.verify(account_id=account.id)
def activate(
self,
@ -55,15 +68,17 @@ class AccountEducationService:
token: str,
institution: str,
role: str,
) -> dict[str, Any] | None:
) -> AccountEducationActivation:
account = self._accounts.get(context.account_id)
if account is None:
raise AccountNotFoundError
if context.active_workspace_id is None:
raise ActiveWorkspaceRequiredError
if self._activation_rate_limiter.is_rate_limited(account.email):
raise EducationRateLimitExceededError
self._activation_rate_limiter.increment_rate_limit(account.email)
return self._education.activate(
account_id=account.id,
email=account.email,
tenant_id=context.active_workspace_id,
token=token,
institution=institution,

View File

@ -87,3 +87,7 @@ class AccountEmailAlreadyInUseError(AccountApplicationError):
class EducationDiscountPausedError(AccountApplicationError):
"""Education discount activation is temporarily paused."""
class EducationRateLimitExceededError(AccountApplicationError):
"""Too many education verification or activation requests were made."""

View File

@ -64,6 +64,7 @@ from services.entities.agent_entities import (
ComposerVariant,
WorkflowNodeJobConfig,
)
from services.skill_management_service import SkillManagementService
from tasks.collect_agent_resources_task import enqueue_agent_resource_collection
from tasks.new_agent_beta_task import register_new_agent_beta_publish_after_commit
@ -353,6 +354,14 @@ class AgentComposerService:
icon=source_agent.icon,
icon_background=source_agent.icon_background,
)
SkillManagementService(session=session).copy_agent_bindings(
tenant_id=tenant_id,
source_agent_id=source_agent.id,
source_snapshot_id=source_version.id,
target_agent_id=inline_agent.id,
target_snapshot_id=inline_agent.active_config_snapshot_id,
user_id=account_id,
)
binding.binding_type = WorkflowAgentBindingType.INLINE_AGENT
binding.agent_id = inline_agent.id
binding.current_snapshot_id = inline_agent.active_config_snapshot_id
@ -640,6 +649,13 @@ class AgentComposerService:
agent.updated_by = account_id
draft.base_snapshot_id = version.id
draft.updated_by = account_id
SkillManagementService(session=session).publish_agent_bindings(
tenant_id=tenant_id,
agent_id=agent.id,
snapshot_id=version.id,
user_id=account_id,
)
if not access_was_ready:
if not agent.app_id:
raise AgentNotFoundError()
@ -1488,6 +1504,12 @@ class AgentComposerService:
agent.active_config_has_model = agent_soul_has_model(payload.agent_soul)
agent.active_config_is_published = True
agent.updated_by = account_id
SkillManagementService(session=session).publish_agent_bindings(
tenant_id=tenant_id,
agent_id=agent.id,
snapshot_id=version.id,
user_id=account_id,
)
binding.current_snapshot_id = version.id
if payload.node_job is not None:
binding.node_job_config = payload.node_job
@ -1528,6 +1550,12 @@ class AgentComposerService:
agent.active_config_has_model = agent_soul_has_model(payload.agent_soul)
agent.active_config_is_published = True
agent.updated_by = account_id
SkillManagementService(session=session).publish_agent_bindings(
tenant_id=tenant_id,
agent_id=agent.id,
snapshot_id=version.id,
user_id=account_id,
)
binding.current_snapshot_id = version.id
binding.updated_by = account_id
if payload.node_job is not None:
@ -1564,6 +1592,17 @@ class AgentComposerService:
operation=AgentConfigRevisionOperation.SAVE_NEW_AGENT,
version_note=payload.version_note,
)
source_agent_id = binding.agent_id if binding else None
source_snapshot_id = binding.current_snapshot_id if binding else None
if source_agent_id and source_snapshot_id and agent.active_config_snapshot_id:
SkillManagementService(session=session).copy_agent_bindings(
tenant_id=tenant_id,
source_agent_id=source_agent_id,
source_snapshot_id=source_snapshot_id,
target_agent_id=agent.id,
target_snapshot_id=agent.active_config_snapshot_id,
user_id=account_id,
)
node_job = payload.node_job or WorkflowNodeJobConfig()
if not binding:
binding = WorkflowAgentNodeBinding(
@ -1619,6 +1658,15 @@ class AgentComposerService:
operation=AgentConfigRevisionOperation.SAVE_TO_ROSTER,
version_note=payload.version_note,
)
if source_agent.active_config_snapshot_id and roster_agent.active_config_snapshot_id:
SkillManagementService(session=session).copy_agent_bindings(
tenant_id=tenant_id,
source_agent_id=source_agent.id,
source_snapshot_id=source_version.id,
target_agent_id=roster_agent.id,
target_snapshot_id=roster_agent.active_config_snapshot_id,
user_id=account_id,
)
binding.binding_type = WorkflowAgentBindingType.ROSTER_AGENT
binding.agent_id = roster_agent.id
binding.current_snapshot_id = roster_agent.active_config_snapshot_id

View File

@ -36,6 +36,17 @@ class AgentPackageOmittedAsset(BaseModel):
mime_type: str | None = None
class AgentPackageWorkspaceSkill(BaseModel):
"""Workspace Skill binding represented without source-workspace identifiers."""
model_config = ConfigDict(extra="forbid")
name: str = Field(min_length=1, max_length=64)
display_name: str = ""
description: str = ""
priority: int = Field(ge=0)
class AgentPackage(BaseModel):
"""One portable Agent Soul with display metadata and omitted asset hints."""
@ -45,6 +56,7 @@ class AgentPackage(BaseModel):
metadata: AgentPackageMetadata
soul: AgentSoulConfig
omitted_assets: list[AgentPackageOmittedAsset] = Field(default_factory=list)
workspace_skills: list[AgentPackageWorkspaceSkill] = Field(default_factory=list)
def portable_ref(prefix: str, value: str) -> str:
@ -75,7 +87,11 @@ def _strip_sensitive_values(value: Any) -> Any:
return result
def make_portable_agent_package(agent: Agent, agent_soul: AgentSoulConfig) -> AgentPackage:
def make_portable_agent_package(
agent: Agent,
agent_soul: AgentSoulConfig,
workspace_skills: list[AgentPackageWorkspaceSkill] | None = None,
) -> AgentPackage:
"""Return a package safe to place in YAML or the system clipboard."""
soul_data = agent_soul.model_dump(mode="json")
@ -152,4 +168,5 @@ def make_portable_agent_package(agent: Agent, agent_soul: AgentSoulConfig) -> Ag
),
soul=portable_soul,
omitted_assets=omitted_assets,
workspace_skills=workspace_skills or [],
)

View File

@ -38,6 +38,7 @@ from models.agent import (
)
from models.agent_config_entities import AgentSoulConfig, WorkflowNodeJobConfig
from models.model import App, AppModelConfig
from models.skill import AgentSkillBinding, AgentSkillBindingSnapshot, Skill
from models.workflow import Workflow
from services.agent.agent_soul_state import agent_soul_has_model
from services.agent.dsl_entities import (
@ -45,6 +46,7 @@ from services.agent.dsl_entities import (
AGENT_PACKAGE_REF_KEY,
AgentPackage,
AgentPackageMetadata,
AgentPackageWorkspaceSkill,
make_portable_agent_package,
portable_ref,
)
@ -108,7 +110,13 @@ class AgentDslService:
soul = AgentSoulConfig.model_validate(snapshot.config_snapshot_dict)
package_ref = "agent_1"
return package_ref, {package_ref: make_portable_agent_package(agent, soul)}
workspace_skills = self._workspace_skills_for_export(
tenant_id=app.tenant_id,
agent_id=agent.id,
snapshot_id=agent.active_config_snapshot_id,
include_draft=draft is not None,
)
return package_ref, {package_ref: make_portable_agent_package(agent, soul, workspace_skills=workspace_skills)}
def export_workflow_packages(
self, *, workflow: Workflow, graph: Mapping[str, Any]
@ -151,6 +159,12 @@ class AgentDslService:
packages[package_ref] = make_portable_agent_package(
agent,
AgentSoulConfig.model_validate(snapshot.config_snapshot_dict),
workspace_skills=self._workspace_skills_for_export(
tenant_id=workflow.tenant_id,
agent_id=agent.id,
snapshot_id=snapshot.id,
include_draft=False,
),
)
node_data["agent_binding"] = {
"binding_type": binding.binding_type.value,
@ -215,6 +229,14 @@ class AgentDslService:
agent_id=agent.id,
snapshot_id=agent.active_config_snapshot_id,
)
self._restore_workspace_skill_bindings(
tenant_id=app.tenant_id,
agent=agent,
snapshot=snapshot,
package=package,
warnings=warnings,
account_id=account.id,
)
self.session.add(
AgentConfigDraft(
tenant_id=app.tenant_id,
@ -378,6 +400,102 @@ class AgentDslService:
)
return dependencies
def _workspace_skills_for_export(
self,
*,
tenant_id: str,
agent_id: str,
snapshot_id: str | None,
include_draft: bool,
) -> list[AgentPackageWorkspaceSkill]:
if include_draft:
rows = list(
self.session.execute(
select(AgentSkillBinding, Skill)
.join(Skill, Skill.id == AgentSkillBinding.skill_id)
.where(AgentSkillBinding.tenant_id == tenant_id, AgentSkillBinding.agent_id == agent_id)
.order_by(AgentSkillBinding.priority)
)
)
if rows or not snapshot_id:
return [
AgentPackageWorkspaceSkill(
name=skill.name,
display_name=skill.display_name,
description=skill.description,
priority=binding.priority,
)
for binding, skill in rows
]
if snapshot_id:
rows = list(
self.session.execute(
select(AgentSkillBindingSnapshot, Skill)
.join(Skill, Skill.id == AgentSkillBindingSnapshot.skill_id)
.where(
AgentSkillBindingSnapshot.tenant_id == tenant_id,
AgentSkillBindingSnapshot.agent_id == agent_id,
AgentSkillBindingSnapshot.config_snapshot_id == snapshot_id,
)
.order_by(AgentSkillBindingSnapshot.priority)
)
)
else:
return []
return [
AgentPackageWorkspaceSkill(
name=skill.name,
display_name=skill.display_name,
description=skill.description,
priority=binding.priority,
)
for binding, skill in rows
]
def _restore_workspace_skill_bindings(
self,
*,
tenant_id: str,
agent: Agent,
snapshot: AgentConfigSnapshot,
package: AgentPackage,
warnings: list[DslImportWarning],
account_id: str,
) -> None:
for workspace_skill in sorted(package.workspace_skills, key=lambda item: item.priority):
skill = self.session.scalar(
select(Skill).where(Skill.tenant_id == tenant_id, Skill.name == workspace_skill.name).limit(1)
)
if skill is None:
warnings.append(
DslImportWarning(
code="agent_workspace_skill_unresolved",
path="agent.workspace_skills",
message=f"Workspace Skill {workspace_skill.name!r} is unavailable in the target workspace.",
details={"name": workspace_skill.name},
)
)
continue
self.session.add(
AgentSkillBinding(
tenant_id=tenant_id,
agent_id=agent.id,
skill_id=skill.id,
priority=workspace_skill.priority,
created_by=account_id,
)
)
self.session.add(
AgentSkillBindingSnapshot(
tenant_id=tenant_id,
agent_id=agent.id,
config_snapshot_id=snapshot.id,
skill_id=skill.id,
priority=workspace_skill.priority,
created_by=account_id,
)
)
def _create_imported_inline_agent(
self,
*,
@ -401,6 +519,14 @@ class AgentDslService:
source=AgentSource.IMPORTED,
operation=AgentConfigRevisionOperation.IMPORT_PACKAGE,
)
self._restore_workspace_skill_bindings(
tenant_id=workflow.tenant_id,
agent=agent,
snapshot=snapshot,
package=package,
warnings=warnings,
account_id=account.id,
)
return AgentPackageImportResult(agent=agent, snapshot=snapshot, warnings=warnings)
def _create_workflow_only_agent(

View File

@ -1106,6 +1106,19 @@ class AgentRosterService:
target_app_id=target_app.id,
account_id=account.id,
)
from services.skill_management_service import SkillManagementService
target_agent = self.get_app_backing_agent(tenant_id=tenant_id, app_id=target_app.id)
if target_agent is None:
raise AgentNotFoundError()
SkillManagementService(session=self._session).copy_agent_bindings(
tenant_id=tenant_id,
source_agent_id=source_agent.id,
source_snapshot_id=source_agent.active_config_snapshot_id or "",
target_agent_id=target_agent.id,
user_id=account.id,
source_include_draft=not source_agent.active_config_is_published,
)
self._session.commit()
if FeatureService.get_system_features().webapp_auth.enabled:
try:

View File

@ -41,7 +41,6 @@ from models.agent import (
)
from models.model import App, AppMode, AppModelConfig, IconType, Site, load_annotation_reply_config
from models.skill import AgentSkillBinding
from models.tools import ApiToolProvider
from models.workflow import Workflow
from services.agent.errors import AgentAccessNotReadyError, AgentNameConflictError
from services.agent.home_snapshot_service import AgentHomeSnapshotService
@ -1042,9 +1041,9 @@ class AppService:
WorkflowAgentNodeBinding.app_id == app.id,
)
)
agent_ids_to_unbind = [*workflow_agent_ids]
agent_ids_to_unbind = set(workflow_agent_ids)
if backing_agent is not None:
agent_ids_to_unbind.append(backing_agent.id)
agent_ids_to_unbind.add(backing_agent.id)
if agent_ids_to_unbind:
session.execute(
delete(AgentSkillBinding).where(

View File

@ -2,18 +2,18 @@ import json
import logging
import os
from collections.abc import Sequence
from typing import Literal, NotRequired, TypedDict
from typing import Any, Literal, NotRequired, TypedDict
import httpx
from pydantic import TypeAdapter, ValidationError
from tenacity import retry, retry_if_exception_type, stop_before_delay, wait_fixed
from typing_extensions import deprecated
from werkzeug.exceptions import InternalServerError
from core.helper.http_client_pooling import get_pooled_http_client
from enums import CloudPlan
from extensions.ext_redis import redis_client
from libs.helper import RateLimiter
from models import Account
from services.billing_portal_service import BillingPortalLink
from services.errors.billing import (
BillingUpstreamInvalidResponseError,
@ -35,7 +35,7 @@ EmailFreezeType = Literal["freeze", "email_domain_suspended"]
class _BillingHTTPStatusError(ValueError):
def __init__(self, message: str, status_code: int):
def __init__(self, message: str, status_code: int) -> None:
super().__init__(message)
self.status_code = status_code
@ -47,6 +47,27 @@ class SubscriptionPlan(TypedDict):
expiration_date: int
class MessageResponseDict(TypedDict):
message: str
class EducationVerifyResponseDict(TypedDict):
token: str
class EducationStatusResponseDict(TypedDict):
result: bool
is_student: bool
expire_at: str
allow_refresh: bool
class EducationAutocompleteResponseDict(TypedDict):
data: list[str]
curr_page: int
has_next: bool
_billing_portal_link_adapter = TypeAdapter(BillingPortalLink)
@ -244,13 +265,6 @@ class BillingService:
def invalidate_vector_space_cache(cls, tenant_id: str) -> None:
cls.get_vector_space(tenant_id, bypass_cache=True)
@classmethod
def get_tenant_feature_plan_usage_info(cls, tenant_id: str):
"""Deprecated: Use get_quota_info instead."""
params = {"tenant_id": tenant_id}
usage_info = cls._send_request("GET", "/tenant-feature-usage/info", params=params)
return usage_info
@classmethod
def get_quota_info(cls, tenant_id: str) -> TenantFeatureQuotaInfo:
params = {"tenant_id": tenant_id}
@ -370,6 +384,7 @@ class BillingService:
return cls._send_billing_portal_request("/subscription/payment-link", params=params)
@classmethod
@deprecated("Only used by the deprecated model-provider checkout endpoint.")
def get_model_provider_payment_link(cls, provider_name: str, tenant_id: str, account_id: str, prefilled_email: str):
params = {
"provider_name": provider_name,
@ -420,6 +435,7 @@ class BillingService:
return cls._send_request("POST", "/tenant-feature-usage/refund", params={"quota_usage_history_id": history_id})
@classmethod
@deprecated("Legacy tenant feature-plan usage endpoint; use the quota APIs instead.")
def get_tenant_feature_plan_usage(cls, tenant_id: str, feature_key: str):
params = {"tenant_id": tenant_id, "feature_key": feature_key}
return cls._send_request("GET", "/billing/tenant_feature_plan/usage", params=params)
@ -427,7 +443,7 @@ class BillingService:
@classmethod
def _send_quota_request(
cls, method: Literal["GET", "POST", "DELETE", "PUT"], endpoint: str, json=None, params=None
):
) -> dict[str, Any]:
return cls._send_request(method, endpoint, json=json, params=params, base_url=cls.quota_base_url)
@classmethod
@ -444,7 +460,7 @@ class BillingService:
json=None,
params=None,
base_url: str | None = None,
):
) -> Any:
headers = {"Content-Type": "application/json", "Billing-Api-Secret-Key": cls.secret_key}
url = f"{base_url or cls.base_url}{endpoint}"
@ -492,7 +508,7 @@ class BillingService:
raise RuntimeError("Unexpected billing service value error") from error
@classmethod
def delete_account(cls, account_id: str):
def delete_account(cls, account_id: str) -> MessageResponseDict:
"""Delete account."""
params = {"account_id": account_id}
return cls._send_request("DELETE", "/account", params=params)
@ -520,41 +536,33 @@ class BillingService:
return cls.get_email_freeze_type(email) is not None
@classmethod
def update_account_deletion_feedback(cls, email: str, feedback: str):
def update_account_deletion_feedback(cls, email: str, feedback: str) -> MessageResponseDict:
"""Update account deletion feedback."""
json = {"email": email, "feedback": feedback}
return cls._send_request("POST", "/account/delete-feedback", json=json)
class EducationIdentity:
verification_rate_limit = RateLimiter(prefix="edu_verification_rate_limit", max_attempts=10, time_window=60)
activation_rate_limit = RateLimiter(prefix="edu_activation_rate_limit", max_attempts=10, time_window=60)
@classmethod
def verify(cls, account_id: str, account_email: str):
if cls.verification_rate_limit.is_rate_limited(account_email):
from controllers.console.error import EducationVerifyLimitError
raise EducationVerifyLimitError()
cls.verification_rate_limit.increment_rate_limit(account_email)
def verify(cls, account_id: str) -> EducationVerifyResponseDict:
params = {"account_id": account_id}
return BillingService._send_request("GET", "/education/verify", params=params)
@classmethod
def status(cls, account_id: str):
def status(cls, account_id: str) -> EducationStatusResponseDict:
params = {"account_id": account_id}
return BillingService._send_request("GET", "/education/status", params=params)
@classmethod
def activate(cls, account: Account, token: str, institution: str, role: str):
if cls.activation_rate_limit.is_rate_limited(account.email):
from controllers.console.error import EducationActivateLimitError
raise EducationActivateLimitError()
cls.activation_rate_limit.increment_rate_limit(account.email)
params = {"account_id": account.id, "curr_tenant_id": account.current_tenant_id}
def activate(
cls,
*,
account_id: str,
tenant_id: str,
token: str,
institution: str,
role: str,
) -> MessageResponseDict:
params = {"account_id": account_id, "curr_tenant_id": tenant_id}
json = {
"institution": institution,
"token": token,
@ -563,7 +571,7 @@ class BillingService:
return BillingService._send_request("POST", "/education/", json=json, params=params)
@classmethod
def autocomplete(cls, keywords: str, page: int = 0, limit: int = 20):
def autocomplete(cls, keywords: str, page: int = 0, limit: int = 20) -> EducationAutocompleteResponseDict:
params = {"keywords": keywords, "page": page, "limit": limit}
return BillingService._send_request("GET", "/education/autocomplete", params=params)
@ -594,11 +602,11 @@ class BillingService:
return res
@classmethod
def clean_billing_info_cache(cls, tenant_id: str):
def clean_billing_info_cache(cls, tenant_id: str) -> None:
redis_client.delete(f"tenant:{tenant_id}:billing_info")
@classmethod
def sync_partner_tenants_bindings(cls, account_id: str, partner_key: str, click_id: str):
def sync_partner_tenants_bindings(cls, account_id: str, partner_key: str, click_id: str) -> dict[str, Any]:
payload = {"account_id": account_id, "click_id": click_id}
return cls._send_request("PUT", f"/partners/{partner_key}/tenants", json=payload)

View File

@ -183,6 +183,11 @@ class AccountEducationVerification:
token: str | None
@dataclass(frozen=True, slots=True)
class AccountEducationActivation:
message: str
@dataclass(frozen=True, slots=True)
class AccountEducationStatus:
result: bool | None

View File

@ -1,6 +1,5 @@
"""Application service for feature queries exposed by API adapters."""
from collections.abc import Sequence
from typing import Protocol
from machinery.context import RequestContext
@ -17,6 +16,8 @@ class FeatureQueryGateway(Protocol):
def get_workspace_features(self, workspace_id: str) -> FeatureModel: ...
def get_trial_models(self, workspace_id: str) -> list[str]: ...
def get_vector_space(self, workspace_id: str) -> VectorSpaceLimitationModel: ...
def get_public_system_features(self) -> SystemFeatureModel: ...
@ -29,11 +30,9 @@ class FeatureQueryService:
self,
*,
features: FeatureQueryGateway,
trial_models: Sequence[str],
app_dsl_version: str,
) -> None:
self._features = features
self._trial_models = tuple(trial_models)
self._app_dsl_version = app_dsl_version
def get_features(self, context: RequestContext) -> FeatureModel:
@ -42,8 +41,8 @@ class FeatureQueryService:
def get_vector_space(self, context: RequestContext) -> VectorSpaceLimitationModel:
return self._features.get_vector_space(self._require_active_workspace(context))
def get_trial_models(self) -> list[str]:
return list(self._trial_models)
def get_trial_models(self, context: RequestContext) -> list[str]:
return self._features.get_trial_models(self._require_active_workspace(context))
def get_app_dsl_version(self) -> str:
return self._app_dsl_version

View File

@ -20,6 +20,16 @@ class _EnterprisePluginInstallationPermission(BaseModel):
class FeatureService:
@classmethod
def get_workspace_plan(cls, tenant_id: str) -> CloudPlan:
if dify_config.DEPLOYMENT_EDITION != DeploymentEdition.CLOUD:
return CloudPlan.SANDBOX
billing_info = BillingService.get_info(tenant_id, exclude_vector_space=True)
if not billing_info["enabled"]:
return CloudPlan.SANDBOX
return CloudPlan(billing_info["subscription"]["plan"])
@classmethod
def get_features(cls, tenant_id: str, exclude_vector_space: bool = False) -> feature_entities.FeatureModel:
features = feature_entities.FeatureModel()
@ -76,11 +86,8 @@ class FeatureService:
if dify_config.DEPLOYMENT_EDITION != DeploymentEdition.CLOUD or not tenant_id:
return default_limit
billing_info = BillingService.get_info(tenant_id, exclude_vector_space=True)
if billing_info["enabled"] and billing_info["subscription"]["plan"] in (
CloudPlan.PROFESSIONAL,
CloudPlan.TEAM,
):
subscription_plan = cls.get_workspace_plan(tenant_id)
if subscription_plan.is_paid:
return max(default_limit, dify_config.KNOWLEDGE_UPLOAD_FILE_SIZE_LIMIT_FOR_PAID_PLAN)
return default_limit
@ -93,10 +100,7 @@ class FeatureService:
return True
if not tenant_id:
return False
return features.billing.enabled and features.billing.subscription.plan in (
CloudPlan.PROFESSIONAL,
CloudPlan.TEAM,
)
return features.billing.enabled and features.billing.subscription.plan.is_paid
@classmethod
def get_system_features(cls) -> feature_entities.SystemFeatureModel:
@ -183,20 +187,26 @@ class FeatureService:
system_features.knowledge_fs_enabled = dify_config.KNOWLEDGE_FS_ENABLED
@classmethod
def _fulfill_trial_models_from_env(cls) -> list[str]:
def _fulfill_trial_models_from_env(cls, quota_types: tuple[str, ...] | None = None) -> list[str]:
allowed_quota_types = quota_types or ("PAID", "TRIAL")
return [
provider.value
for provider in HostedTrialProvider
if (
getattr(dify_config, f"HOSTED_{provider.config_key}_PAID_ENABLED", False)
and getattr(dify_config, f"HOSTED_{provider.config_key}_TRIAL_ENABLED", False)
if any(
getattr(dify_config, f"HOSTED_{provider.config_key}_{quota_type}_ENABLED", False)
for quota_type in allowed_quota_types
)
]
@classmethod
def get_trial_models(cls) -> list[str]:
"""Return hosted trial provider ids without requiring the full system-features payload."""
return cls._fulfill_trial_models_from_env()
def get_trial_models(cls, tenant_id: str) -> list[str]:
"""Return hosted credit providers filtered by the workspace subscription plan."""
if dify_config.DEPLOYMENT_EDITION != DeploymentEdition.CLOUD:
return cls._fulfill_trial_models_from_env()
subscription_plan = cls.get_workspace_plan(tenant_id)
quota_types = ("PAID", "TRIAL") if subscription_plan.is_paid else ("TRIAL",)
return cls._fulfill_trial_models_from_env(quota_types)
@classmethod
def _fulfill_params_from_env(cls, features: feature_entities.FeatureModel):

View File

@ -19,6 +19,10 @@ class FeatureServiceGateway(FeatureQueryGateway):
def get_workspace_features(self, workspace_id: str) -> FeatureModel:
return FeatureService.get_features(workspace_id, exclude_vector_space=True)
@override
def get_trial_models(self, workspace_id: str) -> list[str]:
return FeatureService.get_trial_models(workspace_id)
@override
def get_vector_space(self, workspace_id: str) -> VectorSpaceLimitationModel:
return FeatureService.get_vector_space(workspace_id)

View File

@ -5,7 +5,7 @@ from typing import Literal, NamedTuple, Protocol
from machinery.context import RequestContext
type TagKind = Literal["knowledge", "app", "snippet"]
type TagKind = Literal["knowledge", "app", "snippet", "skill"]
class TagSummary(NamedTuple):
@ -62,7 +62,7 @@ class TagNameConflictError(TagApplicationError):
class TagBindingTargetNotFoundError(TagApplicationError):
def __init__(self, target_type: TagKind) -> None:
target_name = {"knowledge": "Dataset", "app": "App", "snippet": "Snippet"}[target_type]
target_name = {"knowledge": "Dataset", "app": "App", "snippet": "Snippet", "skill": "Skill"}[target_type]
super().__init__(f"{target_name} not found")

View File

@ -80,8 +80,8 @@ def enable_annotation_reply_task(
)
try:
old_vector.delete()
except Exception as e:
logger.info(click.style(f"Delete annotation index error: {str(e)}", fg="red"))
except Exception:
logger.info("Delete annotation index error", exc_info=True)
annotation_setting.score_threshold = score_threshold
annotation_setting.collection_binding_id = dataset_collection_binding.id
annotation_setting.updated_user_id = user_id
@ -116,8 +116,8 @@ def enable_annotation_reply_task(
vector = Vector(dataset, attributes=["doc_id", "annotation_id", "app_id"], session=session)
try:
vector.delete_by_metadata_field("app_id", app_id)
except Exception as e:
logger.info(click.style(f"Delete annotation index error: {str(e)}", fg="red"))
except Exception:
logger.info("Delete annotation index error", exc_info=True)
vector.create(documents)
session.commit()
redis_client.setex(enable_app_annotation_job_key, 600, "completed")

View File

@ -1,36 +0,0 @@
"""
Utilities and helpers for Redis broadcast channel integration tests.
This module provides utility classes and functions for testing
Redis broadcast channel functionality.
"""
from .test_data import (
LARGE_MESSAGES,
SMALL_MESSAGES,
SPECIAL_MESSAGES,
BufferTestConfig,
ConcurrencyTestConfig,
ErrorTestConfig,
)
from .test_helpers import (
ConcurrentPublisher,
SubscriptionMonitor,
assert_message_order,
measure_throughput,
wait_for_condition,
)
__all__ = [
"LARGE_MESSAGES",
"SMALL_MESSAGES",
"SPECIAL_MESSAGES",
"BufferTestConfig",
"ConcurrencyTestConfig",
"ConcurrentPublisher",
"ErrorTestConfig",
"SubscriptionMonitor",
"assert_message_order",
"measure_throughput",
"wait_for_condition",
]

View File

@ -1,315 +0,0 @@
"""
Test data and configuration classes for Redis broadcast channel integration tests.
This module provides dataclasses and constants for test configurations,
message sets, and test scenarios.
"""
import dataclasses
from typing import Any
from libs.broadcast_channel.channel import Overflow
@dataclasses.dataclass(frozen=True)
class BufferTestConfig:
"""Configuration for buffer management tests."""
buffer_size: int
overflow_strategy: Overflow
message_count: int
expected_behavior: str
description: str
@dataclasses.dataclass(frozen=True)
class ConcurrencyTestConfig:
"""Configuration for concurrency tests."""
publisher_count: int
subscriber_count: int
messages_per_publisher: int
test_duration: float
description: str
@dataclasses.dataclass(frozen=True)
class ErrorTestConfig:
"""Configuration for error handling tests."""
error_type: str
test_input: Any
expected_exception: type[Exception]
description: str
# Test message sets for different scenarios
SMALL_MESSAGES = [
b"msg_1",
b"msg_2",
b"msg_3",
b"msg_4",
b"msg_5",
]
MEDIUM_MESSAGES = [
b"medium_message_1_with_more_content",
b"medium_message_2_with_more_content",
b"medium_message_3_with_more_content",
b"medium_message_4_with_more_content",
b"medium_message_5_with_more_content",
]
LARGE_MESSAGES = [
b"large_message_" + b"x" * 1000,
b"large_message_" + b"y" * 1000,
b"large_message_" + b"z" * 1000,
]
VERY_LARGE_MESSAGES = [
b"very_large_message_" + b"x" * 10000, # ~10KB
b"very_large_message_" + b"y" * 50000, # ~50KB
b"very_large_message_" + b"z" * 100000, # ~100KB
]
SPECIAL_MESSAGES = [
b"", # Empty message
b"\x00\x01\x02", # Binary data with null bytes
"unicode_test_你好".encode(), # Unicode
b"special_chars_!@#$%^&*()_+-=[]{}|;':\",./<>?", # Special characters
b"newlines\n\r\t", # Control characters
]
BINARY_MESSAGES = [
bytes(range(256)), # All possible byte values
b"\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8", # High byte values
b"\x00\x01\x02\x03\x04\x05\x06\x07", # Low byte values
]
# Buffer test configurations
BUFFER_TEST_CONFIGS = [
BufferTestConfig(
buffer_size=3,
overflow_strategy=Overflow.DROP_OLDEST,
message_count=5,
expected_behavior="drop_oldest",
description="Drop oldest messages when buffer is full",
),
BufferTestConfig(
buffer_size=3,
overflow_strategy=Overflow.DROP_NEWEST,
message_count=5,
expected_behavior="drop_newest",
description="Drop newest messages when buffer is full",
),
BufferTestConfig(
buffer_size=3,
overflow_strategy=Overflow.BLOCK,
message_count=5,
expected_behavior="block",
description="Block when buffer is full",
),
]
# Concurrency test configurations
CONCURRENCY_TEST_CONFIGS = [
ConcurrencyTestConfig(
publisher_count=1,
subscriber_count=1,
messages_per_publisher=10,
test_duration=5.0,
description="Single publisher, single subscriber",
),
ConcurrencyTestConfig(
publisher_count=3,
subscriber_count=1,
messages_per_publisher=10,
test_duration=5.0,
description="Multiple publishers, single subscriber",
),
ConcurrencyTestConfig(
publisher_count=1,
subscriber_count=3,
messages_per_publisher=10,
test_duration=5.0,
description="Single publisher, multiple subscribers",
),
ConcurrencyTestConfig(
publisher_count=3,
subscriber_count=3,
messages_per_publisher=10,
test_duration=5.0,
description="Multiple publishers, multiple subscribers",
),
]
# Error test configurations
ERROR_TEST_CONFIGS = [
ErrorTestConfig(
error_type="invalid_buffer_size",
test_input=0,
expected_exception=ValueError,
description="Zero buffer size should raise ValueError",
),
ErrorTestConfig(
error_type="invalid_buffer_size",
test_input=-1,
expected_exception=ValueError,
description="Negative buffer size should raise ValueError",
),
ErrorTestConfig(
error_type="invalid_buffer_size",
test_input=1.5,
expected_exception=TypeError,
description="Float buffer size should raise TypeError",
),
ErrorTestConfig(
error_type="invalid_buffer_size",
test_input="invalid",
expected_exception=TypeError,
description="String buffer size should raise TypeError",
),
]
# Topic name test cases
TOPIC_NAME_TEST_CASES = [
"simple_topic",
"topic_with_underscores",
"topic-with-dashes",
"topic.with.dots",
"topic_with_numbers_123",
"UPPERCASE_TOPIC",
"mixed_Case_Topic",
"topic_with_symbols_!@#$%",
"very_long_topic_name_" + "x" * 100,
"unicode_topic_你好",
"topic:with:colons",
"topic/with/slashes",
"topic\\with\\backslashes",
]
# Performance test configurations
PERFORMANCE_TEST_CONFIGS = [
{
"name": "small_messages_high_frequency",
"message_size": 50,
"message_count": 1000,
"description": "Many small messages",
},
{
"name": "medium_messages_medium_frequency",
"message_size": 500,
"message_count": 100,
"description": "Medium messages",
},
{
"name": "large_messages_low_frequency",
"message_size": 5000,
"message_count": 10,
"description": "Large messages",
},
]
# Stress test configurations
STRESS_TEST_CONFIGS = [
{
"name": "high_frequency_publishing",
"publisher_count": 5,
"messages_per_publisher": 100,
"subscriber_count": 3,
"description": "High frequency publishing with multiple publishers",
},
{
"name": "many_subscribers",
"publisher_count": 1,
"messages_per_publisher": 50,
"subscriber_count": 10,
"description": "Many subscribers to single publisher",
},
{
"name": "mixed_load",
"publisher_count": 3,
"messages_per_publisher": 100,
"subscriber_count": 5,
"description": "Mixed load with multiple publishers and subscribers",
},
]
# Edge case test data
EDGE_CASE_MESSAGES = [
b"", # Empty message
b"\x00", # Single null byte
b"\xff", # Single max byte value
b"a", # Single ASCII character
"ä".encode(), # Single unicode character (2 bytes)
"𐍈".encode(), # Unicode character outside BMP (4 bytes)
b"\x00" * 1000, # 1000 null bytes
b"\xff" * 1000, # 1000 max byte values
]
# Message validation test data
MESSAGE_VALIDATION_TEST_CASES = [
{
"name": "valid_bytes",
"input": b"valid_message",
"should_pass": True,
"description": "Valid bytes message",
},
{
"name": "empty_bytes",
"input": b"",
"should_pass": True,
"description": "Empty bytes message",
},
{
"name": "binary_data",
"input": bytes(range(256)),
"should_pass": True,
"description": "Binary data with all byte values",
},
{
"name": "large_message",
"input": b"x" * 1000000, # 1MB
"should_pass": True,
"description": "Large message (1MB)",
},
]
# Redis connection test scenarios
REDIS_CONNECTION_TEST_SCENARIOS = [
{
"name": "normal_connection",
"should_fail": False,
"description": "Normal Redis connection",
},
{
"name": "connection_timeout",
"should_fail": True,
"description": "Connection timeout scenario",
},
{
"name": "connection_refused",
"should_fail": True,
"description": "Connection refused scenario",
},
]
# Test constants
DEFAULT_TIMEOUT = 10.0
SHORT_TIMEOUT = 2.0
LONG_TIMEOUT = 30.0
# Message size limits for testing
MAX_SMALL_MESSAGE_SIZE = 100
MAX_MEDIUM_MESSAGE_SIZE = 1000
MAX_LARGE_MESSAGE_SIZE = 10000
# Thread counts for concurrency testing
MIN_THREAD_COUNT = 1
MAX_THREAD_COUNT = 10
DEFAULT_THREAD_COUNT = 3
# Buffer sizes for testing
MIN_BUFFER_SIZE = 1
MAX_BUFFER_SIZE = 1000
DEFAULT_BUFFER_SIZE = 10

View File

@ -1,396 +0,0 @@
"""
Test helper utilities for Redis broadcast channel integration tests.
This module provides utility classes and functions for testing concurrent
operations, monitoring subscriptions, and measuring performance.
"""
import logging
import threading
import time
from collections.abc import Callable
from typing import Any
_logger = logging.getLogger(__name__)
class ConcurrentPublisher:
"""
Utility class for publishing messages concurrently from multiple threads.
This class manages multiple publisher threads that can publish messages
to the same or different topics concurrently, useful for stress testing
and concurrency validation.
"""
def __init__(self, producer, message_count: int = 10, delay: float = 0.0):
"""
Initialize the concurrent publisher.
Args:
producer: The producer instance to publish with
message_count: Number of messages to publish per thread
delay: Delay between messages in seconds
"""
self.producer = producer
self.message_count = message_count
self.delay = delay
self.threads: list[threading.Thread] = []
self.published_messages: list[list[bytes]] = []
self._lock = threading.Lock()
self._started = False
def start_publishers(self, thread_count: int = 3) -> None:
"""
Start multiple publisher threads.
Args:
thread_count: Number of publisher threads to start
"""
if self._started:
raise RuntimeError("Publishers already started")
self._started = True
def _publisher(thread_id: int) -> None:
messages: list[bytes] = []
for i in range(self.message_count):
message = f"thread_{thread_id}_msg_{i}".encode()
try:
self.producer.publish(message)
messages.append(message)
if self.delay > 0:
time.sleep(self.delay)
except Exception:
_logger.exception("Pubmsg=lisher %s", thread_id)
with self._lock:
self.published_messages.append(messages)
for thread_id in range(thread_count):
thread = threading.Thread(
target=_publisher,
args=(thread_id,),
name=f"publisher-{thread_id}",
daemon=True,
)
thread.start()
self.threads.append(thread)
def wait_for_completion(self, timeout: float = 30.0) -> bool:
"""
Wait for all publisher threads to complete.
Args:
timeout: Maximum time to wait in seconds
Returns:
bool: True if all threads completed successfully
"""
for thread in self.threads:
thread.join(timeout)
if thread.is_alive():
return False
return True
def get_all_messages(self) -> list[bytes]:
"""
Get all messages published by all threads.
Returns:
list[bytes]: Flattened list of all published messages
"""
with self._lock:
all_messages = []
for thread_messages in self.published_messages:
all_messages.extend(thread_messages)
return all_messages
def get_thread_messages(self, thread_id: int) -> list[bytes]:
"""
Get messages published by a specific thread.
Args:
thread_id: ID of the thread
Returns:
list[bytes]: Messages published by the specified thread
"""
with self._lock:
if 0 <= thread_id < len(self.published_messages):
return self.published_messages[thread_id].copy()
return []
class SubscriptionMonitor:
"""
Utility class for monitoring subscription activity in tests.
This class monitors a subscription and tracks message reception,
errors, and completion status for testing purposes.
"""
def __init__(self, subscription, timeout: float = 10.0):
"""
Initialize the subscription monitor.
Args:
subscription: The subscription to monitor
timeout: Default timeout for operations
"""
self.subscription = subscription
self.timeout = timeout
self.messages: list[bytes] = []
self.errors: list[Exception] = []
self.completed = False
self._lock = threading.Lock()
self._condition = threading.Condition(self._lock)
self._monitor_thread: threading.Thread | None = None
self._start_time: float | None = None
def start_monitoring(self) -> None:
"""Start monitoring the subscription in a separate thread."""
if self._monitor_thread is not None:
raise RuntimeError("Monitoring already started")
self._start_time = time.time()
def _monitor():
try:
for message in self.subscription:
with self._lock:
self.messages.append(message)
self._condition.notify_all()
except Exception as e:
with self._lock:
self.errors.append(e)
self._condition.notify_all()
finally:
with self._lock:
self.completed = True
self._condition.notify_all()
self._monitor_thread = threading.Thread(
target=_monitor,
name="subscription-monitor",
daemon=True,
)
self._monitor_thread.start()
def wait_for_messages(self, count: int, timeout: float | None = None) -> bool:
"""
Wait for a specific number of messages.
Args:
count: Number of messages to wait for
timeout: Timeout in seconds (uses default if None)
Returns:
bool: True if expected messages were received
"""
if timeout is None:
timeout = self.timeout
deadline = time.time() + timeout
with self._condition:
while len(self.messages) < count and not self.completed:
remaining = deadline - time.time()
if remaining <= 0:
return False
self._condition.wait(remaining)
return len(self.messages) >= count
def wait_for_completion(self, timeout: float | None = None) -> bool:
"""
Wait for monitoring to complete.
Args:
timeout: Timeout in seconds (uses default if None)
Returns:
bool: True if monitoring completed successfully
"""
if timeout is None:
timeout = self.timeout
deadline = time.time() + timeout
with self._condition:
while not self.completed:
remaining = deadline - time.time()
if remaining <= 0:
return False
self._condition.wait(remaining)
return True
def get_messages(self) -> list[bytes]:
"""
Get all received messages.
Returns:
list[bytes]: Copy of received messages
"""
with self._lock:
return self.messages.copy()
def get_error_count(self) -> int:
"""
Get the number of errors encountered.
Returns:
int: Number of errors
"""
with self._lock:
return len(self.errors)
def get_elapsed_time(self) -> float:
"""
Get the elapsed monitoring time.
Returns:
float: Elapsed time in seconds
"""
if self._start_time is None:
return 0.0
return time.time() - self._start_time
def stop(self) -> None:
"""Stop monitoring and close the subscription."""
if self._monitor_thread is not None:
self.subscription.close()
self._monitor_thread.join(timeout=1.0)
def assert_message_order(received: list[bytes], expected: list[bytes]) -> bool:
"""
Assert that messages were received in the expected order.
Args:
received: List of received messages
expected: List of expected messages in order
Returns:
bool: True if order matches expected
"""
if len(received) != len(expected):
return False
for i, (recv_msg, exp_msg) in enumerate(zip(received, expected)):
if recv_msg != exp_msg:
_logger.error("Message order mismatch at index %s: expected %s, got %s", i, exp_msg, recv_msg)
return False
return True
def measure_throughput(
operation: Callable[[], Any],
duration: float = 1.0,
) -> tuple[float, int]:
"""
Measure the throughput of an operation over a specified duration.
Args:
operation: The operation to measure
duration: Duration to run the operation in seconds
Returns:
tuple[float, int]: (operations per second, total operations)
"""
start_time = time.time()
end_time = start_time + duration
count = 0
while time.time() < end_time:
try:
operation()
count += 1
except Exception:
_logger.exception("Operation failed")
break
elapsed = time.time() - start_time
ops_per_sec = count / elapsed if elapsed > 0 else 0.0
return ops_per_sec, count
def wait_for_condition(
condition: Callable[[], bool],
timeout: float = 10.0,
interval: float = 0.1,
) -> bool:
"""
Wait for a condition to become true.
Args:
condition: Function that returns True when condition is met
timeout: Maximum time to wait in seconds
interval: Check interval in seconds
Returns:
bool: True if condition was met within timeout
"""
deadline = time.time() + timeout
while time.time() < deadline:
if condition():
return True
time.sleep(interval)
return False
def create_stress_test_messages(
count: int,
size: int = 100,
) -> list[bytes]:
"""
Create messages for stress testing.
Args:
count: Number of messages to create
size: Size of each message in bytes
Returns:
list[bytes]: List of test messages
"""
messages = []
for i in range(count):
message = f"stress_test_msg_{i:06d}_".ljust(size, "x").encode()
messages.append(message)
return messages
def validate_message_integrity(
original_messages: list[bytes],
received_messages: list[bytes],
) -> dict[str, Any]:
"""
Validate the integrity of received messages.
Args:
original_messages: Messages that were sent
received_messages: Messages that were received
Returns:
dict[str, Any]: Validation results
"""
original_set = set(original_messages)
received_set = set(received_messages)
missing_messages = original_set - received_set
extra_messages = received_set - original_set
return {
"total_sent": len(original_messages),
"total_received": len(received_messages),
"missing_count": len(missing_messages),
"extra_count": len(extra_messages),
"missing_messages": list(missing_messages),
"extra_messages": list(extra_messages),
"integrity_ok": len(missing_messages) == 0 and len(extra_messages) == 0,
}

View File

@ -1,38 +0,0 @@
from textwrap import dedent
from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage
from core.helper.code_executor.javascript.javascript_code_provider import JavascriptCodeProvider
from core.helper.code_executor.javascript.javascript_transformer import NodeJsTemplateTransformer
CODE_LANGUAGE = CodeLanguage.JAVASCRIPT
def test_javascript_plain():
code = 'console.log("Hello World")'
result_message = CodeExecutor.execute_code(language=CODE_LANGUAGE, preload="", code=code)
assert result_message == "Hello World\n"
def test_javascript_json():
code = dedent("""
obj = {'Hello': 'World'}
console.log(JSON.stringify(obj))
""")
result = CodeExecutor.execute_code(language=CODE_LANGUAGE, preload="", code=code)
assert result == '{"Hello":"World"}\n'
def test_javascript_with_code_template():
result = CodeExecutor.execute_workflow_code_template(
language=CODE_LANGUAGE,
code=JavascriptCodeProvider.get_default_code(),
inputs={"arg1": "Hello", "arg2": "World"},
)
assert result == {"result": "HelloWorld"}
def test_javascript_get_runner_script():
runner_script = NodeJsTemplateTransformer.get_runner_script()
assert runner_script.count(NodeJsTemplateTransformer._code_placeholder) == 1
assert runner_script.count(NodeJsTemplateTransformer._inputs_placeholder) == 1
assert runner_script.count(NodeJsTemplateTransformer._result_tag) == 2

View File

@ -1,69 +0,0 @@
"""
Integration tests for KnowledgeIndexNode.
This module provides integration tests for KnowledgeIndexNode with real database interactions.
Note: These tests require database setup and are more complex than unit tests.
For now, we focus on unit tests which provide better coverage for the node logic.
"""
import pytest
class TestKnowledgeIndexNodeIntegration:
"""
Integration test suite for KnowledgeIndexNode.
Note: Full integration tests require:
- Database setup with datasets and documents
- Vector store for embeddings
- Model providers for indexing and summarization
- IndexProcessor and SummaryIndexService implementations
For now, unit tests provide comprehensive coverage of the node logic.
"""
@pytest.mark.skip(reason="Integration tests require full database and vector store setup")
def test_end_to_end_knowledge_index_preview(self):
"""Test end-to-end knowledge index workflow in preview mode."""
# TODO: Implement with real database
# 1. Create a dataset
# 2. Create a document
# 3. Prepare chunks
# 4. Run KnowledgeIndexNode in preview mode
# 5. Verify preview output
pass
@pytest.mark.skip(reason="Integration tests require full database and vector store setup")
def test_end_to_end_knowledge_index_production(self):
"""Test end-to-end knowledge index workflow in production mode."""
# TODO: Implement with real database
# 1. Create a dataset
# 2. Create a document
# 3. Prepare chunks
# 4. Run KnowledgeIndexNode in production mode
# 5. Verify indexing and summary generation
pass
@pytest.mark.skip(reason="Integration tests require full database and vector store setup")
def test_knowledge_index_with_summary_enabled(self):
"""Test knowledge index with summary index setting enabled."""
# TODO: Implement with real database
# 1. Create a dataset
# 2. Create a document
# 3. Prepare chunks
# 4. Configure summary index setting
# 5. Run KnowledgeIndexNode
# 6. Verify summaries are generated and indexed
pass
@pytest.mark.skip(reason="Integration tests require full database and vector store setup")
def test_knowledge_index_parent_child_structure(self):
"""Test knowledge index with parent-child chunk structure."""
# TODO: Implement with real database
# 1. Create a dataset
# 2. Create a document
# 3. Prepare parent-child chunks
# 4. Run KnowledgeIndexNode
# 5. Verify parent-child indexing
pass

View File

@ -1,29 +0,0 @@
"""
Integration tests for KnowledgeRetrievalNode.
This module provides integration tests for KnowledgeRetrievalNode with real database interactions.
Note: These tests require database setup and are more complex than unit tests.
For now, we focus on unit tests which provide better coverage for the node logic.
"""
import pytest
class TestKnowledgeRetrievalNodeIntegration:
"""
Integration test suite for KnowledgeRetrievalNode.
Note: Full integration tests require:
- Database setup with datasets and documents
- Vector store for embeddings
- Model providers for retrieval
For now, unit tests provide comprehensive coverage of the node logic.
"""
@pytest.mark.skip(reason="Integration tests require full database and vector store setup")
def test_end_to_end_knowledge_retrieval(self):
"""Test end-to-end knowledge retrieval workflow."""
# TODO: Implement with real database
pass

View File

@ -102,6 +102,22 @@ class TestTagListApi:
assert status == 200
assert result == [{"id": "tag-1", "name": "Tag", "type": "knowledge", "binding_count": "2"}]
def test_get_skill_tags_uses_same_query_boundary(
self, app: Flask, request_context: RequestContext, tags_service: MagicMock
) -> None:
tags_service.list_tags.return_value = (TagSummary("tag-1", "Skill", "skill", 1),)
with app.test_request_context("/?type=skill"):
result, status = unwrap(TagListApi().get)(
TagListApi(),
TagListQueryParam(type="skill"),
request_context,
)
tags_service.list_tags.assert_called_once_with(request_context, "skill", None)
assert status == 200
assert result[0]["type"] == "skill"
def test_get_snippet_tags_uses_same_query_boundary(
self, app: Flask, request_context: RequestContext, tags_service: MagicMock
) -> None:
@ -378,6 +394,25 @@ class TestTagBindings:
)
assert (result, status) == ({"result": "success"}, 200)
def test_create_passes_skill_binding_input(
self, app: Flask, request_context: RequestContext, tags_service: MagicMock
) -> None:
owner = _account(TenantAccountRole.OWNER)
payload = TagBindingPayload(tag_ids=["tag-1"], target_id="skill-1", type=TagType.SKILL)
with (
app.test_request_context("/"),
patch.object(module.dify_config, "RBAC_ENABLED", False),
patch.object(module, "current_account_with_tenant", return_value=(owner, "tenant-1")),
):
result, status = unwrap(TagBindingCollectionApi().post)(TagBindingCollectionApi(), payload, request_context)
tags_service.create_bindings.assert_called_once_with(
request_context,
TagBindingInput(("tag-1",), "skill-1", "skill"),
)
assert (result, status) == ({"result": "success"}, 200)
def test_create_maps_missing_target_to_not_found(
self, app: Flask, request_context: RequestContext, tags_service: MagicMock
) -> None:

View File

@ -107,10 +107,11 @@ class TestTrialModelsApi:
api = TrialModelsApi()
raw_get = unwrap(TrialModelsApi.get)
result = raw_get(api, _request_context())
request_context = _request_context()
result = raw_get(api, request_context)
assert result == {"trial_models": ["langgenius/openai/openai"]}
get_trial_models.assert_called_once_with()
get_trial_models.assert_called_once_with(request_context)
class TestAppDslVersionApi:

View File

@ -8,7 +8,7 @@ from flask import Flask
from sqlalchemy.orm import Session
from controllers.console.auth.error import InvalidTokenError
from controllers.console.error import EmailDomainSuspendedError
from controllers.console.error import EducationActivateLimitError, EducationVerifyLimitError, EmailDomainSuspendedError
from controllers.console.workspace.account import (
AccountDeleteUpdateFeedbackApi,
ChangeEmailCheckApi,
@ -16,13 +16,14 @@ from controllers.console.workspace.account import (
ChangeEmailSendEmailApi,
CheckEmailUnique,
EducationApi,
EducationVerifyApi,
)
from machinery.context import RequestContext
from models import Account, AccountStatus, Tenant, TenantAccountJoin
from models.account import TenantAccountRole
from services import account_errors
from services.account_service import AccountService
from services.entities.account_entities import ChangeEmailVerification
from services.entities.account_entities import AccountEducationActivation, ChangeEmailVerification
from services.entities.auth_entities import (
ChangeEmailNewEmailToken,
ChangeEmailNewEmailVerifiedToken,
@ -105,7 +106,7 @@ def _build_change_email_token(
class TestEducationApi:
def test_post_activates_education_discount(self, app: Flask):
education = MagicMock()
education.activate.return_value = {"message": "success"}
education.activate.return_value = AccountEducationActivation(message="success")
request_context = RequestContext(
request_id="request-1",
trace_id=None,
@ -136,6 +137,52 @@ class TestEducationApi:
role="Student",
)
def test_verify_maps_rate_limit_error(self, app: Flask):
education = MagicMock()
education.verify.side_effect = account_errors.EducationRateLimitExceededError
request_context = RequestContext(
request_id="request-1",
trace_id=None,
account_id="account-1",
active_workspace_id="workspace-1",
)
with (
app.test_request_context("/account/education/verify", method="GET"),
patch(
"controllers.console.workspace.account.application_services",
return_value=SimpleNamespace(accounts=SimpleNamespace(education=education)),
),
):
api = EducationVerifyApi()
with pytest.raises(EducationVerifyLimitError):
inspect.unwrap(api.get)(api, request_context)
def test_post_maps_rate_limit_error(self, app: Flask):
education = MagicMock()
education.activate.side_effect = account_errors.EducationRateLimitExceededError
request_context = RequestContext(
request_id="request-1",
trace_id=None,
account_id="account-1",
active_workspace_id="workspace-1",
)
with (
app.test_request_context(
"/account/education",
method="POST",
json={"token": "education-token", "institution": "Dify University", "role": "Student"},
),
patch(
"controllers.console.workspace.account.application_services",
return_value=SimpleNamespace(accounts=SimpleNamespace(education=education)),
),
):
api = EducationApi()
with pytest.raises(EducationActivateLimitError):
inspect.unwrap(api.post)(api, request_context)
def _change_email_context(account_id: str = "acc") -> RequestContext:
return RequestContext(

View File

@ -88,10 +88,12 @@ def _tool_file(*, name: str = "test.txt", mimetype: str = "text/plain") -> ToolF
class TestPluginUploadFileApi:
@patch.object(module, "verify_plugin_file_signature", return_value=True)
@patch.object(module, "get_user", return_value=_end_user())
@patch.object(module, "sign_tool_file", return_value="signed-url")
@patch.object(module, "ToolFileManager")
def test_success_upload(
self,
mock_tool_file_manager,
mock_sign_tool_file,
mock_get_user,
mock_verify_signature,
):
@ -115,8 +117,6 @@ class TestPluginUploadFileApi:
mimetype="application/octet-stream",
)
mock_tool_file_manager.sign_file.return_value = "signed-url"
api = module.PluginUploadFileApi()
post_fn = unwrap(api.post)
@ -131,7 +131,11 @@ class TestPluginUploadFileApi:
assert mock_verify_signature.call_args.kwargs["conversation_id"] == "conversation-1"
tool_file_manager_instance.create_file_by_raw.assert_called_once()
assert tool_file_manager_instance.create_file_by_raw.call_args.kwargs["conversation_id"] == "conversation-1"
mock_tool_file_manager.sign_file.assert_called_once_with(tool_file_id="file-id", extension=".docx")
mock_sign_tool_file.assert_called_once_with(
tool_file_id="file-id",
extension=".docx",
for_external=True,
)
@patch.object(module, "get_user")
@patch.object(module, "ToolFileManager")

View File

@ -5,6 +5,8 @@ import pytest
from werkzeug.exceptions import NotFound, RequestEntityTooLarge
import controllers.trigger.webhook as module
from models.trigger import WorkflowWebhookTrigger
from models.workflow import Workflow
from services.errors.app import QuotaExceededError
@ -22,29 +24,39 @@ def mock_jsonify():
module.jsonify = lambda payload: payload
class DummyWebhookTrigger:
webhook_id = "wh-1"
webhook_url = "http://localhost:5001/triggers/webhook/wh-1"
tenant_id = "tenant-1"
app_id = "app-1"
node_id = "node-1"
def _webhook_trigger() -> WorkflowWebhookTrigger:
return WorkflowWebhookTrigger(
webhook_id="wh-1",
tenant_id="tenant-1",
app_id="app-1",
node_id="node-1",
created_by="account-1",
)
def _workflow() -> Workflow:
return Workflow(id="workflow-1")
class TestPrepareWebhookExecution:
@patch.object(module.WebhookService, "get_webhook_trigger_and_workflow")
@patch.object(module.WebhookService, "extract_and_validate_webhook_data")
def test_prepare_success(self, mock_extract, mock_get):
mock_get.return_value = ("trigger", "workflow", "node_config")
webhook_trigger = _webhook_trigger()
workflow = _workflow()
mock_get.return_value = (webhook_trigger, workflow, "node_config")
mock_extract.return_value = {"data": "ok"}
result = module._prepare_webhook_execution("wh-1")
assert result == ("trigger", "workflow", "node_config", {"data": "ok"}, None)
assert result == (webhook_trigger, workflow, "node_config", {"data": "ok"}, None)
@patch.object(module.WebhookService, "get_webhook_trigger_and_workflow")
@patch.object(module.WebhookService, "extract_and_validate_webhook_data", side_effect=ValueError("bad"))
def test_prepare_validation_error(self, mock_extract, mock_get):
mock_get.return_value = ("trigger", "workflow", "node_config")
webhook_trigger = _webhook_trigger()
workflow = _workflow()
mock_get.return_value = (webhook_trigger, workflow, "node_config")
trigger, workflow, node_config, webhook_data, error = module._prepare_webhook_execution("wh-1")
@ -64,7 +76,7 @@ class TestHandleWebhook:
mock_extract,
mock_get,
):
mock_get.return_value = (DummyWebhookTrigger(), "workflow", "node_config")
mock_get.return_value = (_webhook_trigger(), _workflow(), "node_config")
mock_extract.return_value = {"input": "x"}
mock_generate.return_value = ({"ok": True}, 200)
@ -77,7 +89,7 @@ class TestHandleWebhook:
@patch.object(module.WebhookService, "get_webhook_trigger_and_workflow")
@patch.object(module.WebhookService, "extract_and_validate_webhook_data", side_effect=ValueError("bad"))
def test_bad_request(self, mock_extract, mock_get):
mock_get.return_value = (DummyWebhookTrigger(), "workflow", "node_config")
mock_get.return_value = (_webhook_trigger(), _workflow(), "node_config")
response, status = module.handle_webhook("wh-1")
@ -92,7 +104,7 @@ class TestHandleWebhook:
side_effect=QuotaExceededError(feature="trigger", tenant_id="tenant-1", required=1),
)
def test_quota_exceeded(self, mock_trigger, mock_extract, mock_get):
mock_get.return_value = (DummyWebhookTrigger(), "workflow", "node_config")
mock_get.return_value = (_webhook_trigger(), _workflow(), "node_config")
mock_extract.return_value = {"input": "x"}
response, status = module.handle_webhook("wh-1")
@ -111,7 +123,7 @@ class TestHandleWebhook:
side_effect=QuotaExceededError(feature="workflow", tenant_id="tenant-1", required=1),
)
def test_workflow_quota_exceeded(self, mock_trigger, mock_extract, mock_get):
mock_get.return_value = (DummyWebhookTrigger(), "workflow", "node_config")
mock_get.return_value = (_webhook_trigger(), _workflow(), "node_config")
mock_extract.return_value = {"input": "x"}
response, status = module.handle_webhook("wh-1")
@ -166,7 +178,8 @@ class TestHandleWebhookDebug:
mock_extract,
mock_get,
):
mock_get.return_value = (DummyWebhookTrigger(), None, "node_config")
webhook_trigger = _webhook_trigger()
mock_get.return_value = (webhook_trigger, None, "node_config")
mock_extract.return_value = {"method": "POST"}
response, status = module.handle_webhook_debug("wh-1")
@ -177,7 +190,7 @@ class TestHandleWebhookDebug:
"The webhook debug URL only works while the Variable Inspector is listening. "
"Use the published webhook URL to execute the workflow in Celery."
)
assert response["execution_url"] == DummyWebhookTrigger.webhook_url
assert response["execution_url"] == webhook_trigger.webhook_url
mock_dispatch.assert_called_once()
@patch.object(module.WebhookService, "get_webhook_trigger_and_workflow")
@ -193,7 +206,7 @@ class TestHandleWebhookDebug:
mock_extract,
mock_get,
):
mock_get.return_value = (DummyWebhookTrigger(), None, "node_config")
mock_get.return_value = (_webhook_trigger(), None, "node_config")
mock_extract.return_value = {"method": "POST"}
mock_generate.return_value = ({"ok": True}, 200)
@ -206,7 +219,7 @@ class TestHandleWebhookDebug:
@patch.object(module.WebhookService, "get_webhook_trigger_and_workflow")
@patch.object(module.WebhookService, "extract_and_validate_webhook_data", side_effect=ValueError("bad"))
def test_debug_bad_request(self, mock_extract, mock_get):
mock_get.return_value = (DummyWebhookTrigger(), None, "node_config")
mock_get.return_value = (_webhook_trigger(), None, "node_config")
response, status = module.handle_webhook_debug("wh-1")

View File

@ -29,6 +29,14 @@ from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom
from models.agent_config_entities import AgentSoulConfig
@pytest.fixture(autouse=True)
def _no_runtime_agent_skills(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(
"core.app.apps.agent_app.runtime_request_builder.load_runtime_agent_skill_configs",
lambda **_kwargs: [],
)
@pytest.fixture(autouse=True)
def model_context_window_calls(monkeypatch: pytest.MonkeyPatch) -> list[tuple[object, str, str]]:
calls: list[tuple[object, str, str]] = []

View File

@ -7,6 +7,7 @@ and select_trigger_debug_events orchestrator.
from __future__ import annotations
import json
from datetime import datetime
from typing import Any
from unittest.mock import MagicMock, patch
@ -27,7 +28,9 @@ from core.trigger.debug.event_selectors import (
select_trigger_debug_events,
)
from core.trigger.debug.events import PluginTriggerDebugEvent, WebhookDebugEvent
from graphon.enums import BuiltinNodeTypes, NodeType
from graphon.enums import BuiltinNodeTypes, NodeType, WorkflowType
from models.model import App, AppMode
from models.workflow import Workflow
from tests.unit_tests.core.trigger.conftest import VALID_PROVIDER_ID
@ -55,6 +58,35 @@ def _plugin_node_config(provider_id: str = VALID_PROVIDER_ID) -> dict:
}
def _workflow_with_node(node_type: NodeType | None) -> Workflow:
nodes = [] if node_type is None else [{"id": "n1", "data": {"type": node_type, "title": "Trigger"}}]
return Workflow.new(
tenant_id="t1",
app_id="a1",
type=WorkflowType.WORKFLOW.value,
version=Workflow.VERSION_DRAFT,
graph=json.dumps({"nodes": nodes, "edges": []}),
features="{}",
created_by="u1",
environment_variables=[],
conversation_variables=[],
rag_pipeline_variables=[],
)
def _app() -> App:
return App(
id="a1",
tenant_id="t1",
name="Trigger App",
description="",
mode=AppMode.WORKFLOW,
enable_site=True,
enable_api=True,
max_active_requests=0,
)
class TestPluginTriggerDebugEventPoller:
@patch("core.trigger.debug.event_selectors.TriggerDebugEventBus")
def test_returns_workflow_args_on_success(self, mock_bus):
@ -214,51 +246,41 @@ class TestScheduleTriggerDebugEventPoller:
class TestCreateEventPoller:
def _workflow_with_node(self, node_type: NodeType):
wf = MagicMock()
wf.get_node_config_by_id.return_value = {"data": {}}
wf.get_node_type_from_node_config.return_value = node_type
return wf
def test_creates_plugin_poller(self):
wf = self._workflow_with_node(TRIGGER_PLUGIN_NODE_TYPE)
wf = _workflow_with_node(TRIGGER_PLUGIN_NODE_TYPE)
poller = create_event_poller(wf, "t1", "u1", "a1", "n1")
assert isinstance(poller, PluginTriggerDebugEventPoller)
def test_creates_webhook_poller(self):
wf = self._workflow_with_node(TRIGGER_WEBHOOK_NODE_TYPE)
wf = _workflow_with_node(TRIGGER_WEBHOOK_NODE_TYPE)
poller = create_event_poller(wf, "t1", "u1", "a1", "n1")
assert isinstance(poller, WebhookTriggerDebugEventPoller)
def test_creates_schedule_poller(self):
wf = self._workflow_with_node(TRIGGER_SCHEDULE_NODE_TYPE)
wf = _workflow_with_node(TRIGGER_SCHEDULE_NODE_TYPE)
poller = create_event_poller(wf, "t1", "u1", "a1", "n1")
assert isinstance(poller, ScheduleTriggerDebugEventPoller)
def test_raises_for_unknown_type(self):
wf = MagicMock()
wf.get_node_config_by_id.return_value = {"data": {}}
wf.get_node_type_from_node_config.return_value = BuiltinNodeTypes.START
wf = _workflow_with_node(BuiltinNodeTypes.START)
with pytest.raises(ValueError):
create_event_poller(wf, "t1", "u1", "a1", "n1")
def test_raises_when_node_config_missing(self):
wf = MagicMock()
wf.get_node_config_by_id.return_value = None
wf = _workflow_with_node(None)
with pytest.raises(ValueError):
with (
patch.object(Workflow, "get_node_config_by_id", return_value=None),
pytest.raises(ValueError),
):
create_event_poller(wf, "t1", "u1", "a1", "n1")
class TestSelectTriggerDebugEvents:
def test_returns_first_non_none_event(self):
wf = MagicMock()
wf.get_node_config_by_id.return_value = {"data": {}}
wf.get_node_type_from_node_config.return_value = TRIGGER_WEBHOOK_NODE_TYPE
app_model = MagicMock()
app_model.tenant_id = "t1"
app_model.id = "a1"
wf = _workflow_with_node(TRIGGER_WEBHOOK_NODE_TYPE)
app_model = _app()
with patch.object(WebhookTriggerDebugEventPoller, "poll") as mock_poll:
expected = MagicMock()
@ -269,12 +291,8 @@ class TestSelectTriggerDebugEvents:
assert result is expected
def test_returns_none_when_no_events(self):
wf = MagicMock()
wf.get_node_config_by_id.return_value = {"data": {}}
wf.get_node_type_from_node_config.return_value = TRIGGER_WEBHOOK_NODE_TYPE
app_model = MagicMock()
app_model.tenant_id = "t1"
app_model.id = "a1"
wf = _workflow_with_node(TRIGGER_WEBHOOK_NODE_TYPE)
app_model = _app()
with patch.object(WebhookTriggerDebugEventPoller, "poll", return_value=None):
result = select_trigger_debug_events(wf, app_model, "u1", ["n1"])

View File

@ -41,6 +41,21 @@ from models.agent_config_entities import (
)
@pytest.fixture(autouse=True)
def _no_runtime_agent_skills(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(
"core.workflow.nodes.agent_v2.runtime_request_builder.load_runtime_agent_skill_configs",
lambda **_kwargs: [],
)
class FakeCredentialsProvider:
def fetch(self, provider_name: str, model_name: str) -> dict[str, object]:
assert provider_name == "openai"
assert model_name == "gpt-test"
return {"api_key": "secret-key"}
@pytest.fixture(autouse=True)
def model_context_window_calls(monkeypatch: pytest.MonkeyPatch) -> list[tuple[object, str, str]]:
calls: list[tuple[object, str, str]] = []

View File

@ -248,6 +248,24 @@ def test_draft_validation_allows_non_upstream_previous_output_ref():
)
def test_draft_validation_allows_missing_agent_soul_model():
node_job = WorkflowNodeJobConfig.model_validate({})
snapshot = AgentConfigSnapshot(
id="snapshot-1",
tenant_id="tenant-1",
agent_id="agent-1",
version=1,
config_snapshot=AgentSoulConfig(),
)
session = Mock()
session.scalar.side_effect = [_binding(node_job), _agent(), snapshot]
WorkflowAgentNodeValidator.validate_draft_workflow(
session=session,
workflow=_workflow(_graph([{"source": "start", "target": "agent-node"}])),
)
def test_draft_validation_rejects_incomplete_previous_output_ref():
node_job = WorkflowNodeJobConfig.model_validate({"previous_node_output_refs": [{"selector": ["previous-node"]}]})
session = Mock()

View File

@ -0,0 +1,56 @@
"""Test that the AliyunLogStorePG port-connectivity logger captures the traceback.
Cycle 21 (sibling of #41066 / merged in #41068): `logger.debug("...: %s", str(e))`
sites in `extensions/logstore/aliyun_logstore_pg.py` were converted to
`logger.debug("...", exc_info=True)` so the traceback is captured at the
same log level rather than being silently dropped. This test exercises one
of the modified sites `_check_port_connectivity` to confirm the fix
shape is correct: the captured log record has a non-empty traceback.
"""
from __future__ import annotations
from unittest.mock import patch
import pytest
from extensions.logstore.aliyun_logstore_pg import AliyunLogStorePG
def test_check_port_connectivity_captures_traceback_on_exception(caplog: pytest.LogCaptureFixture) -> None:
"""A socket failure during the port check must produce a log record with exc_info set.
Before cycle 21: the record was logged as
`Port connectivity check failed for host:port: <str(exception)>` with no
traceback. After cycle 21: the record is logged as
`Port connectivity check failed for host:port` with `exc_info=True`, so
the traceback is part of the log record (and the host/port are still
included via the format args).
"""
store = AliyunLogStorePG(
access_key_id="ak",
access_key_secret="sk",
endpoint="https://example.com",
project_name="p",
)
with (
patch("extensions.logstore.aliyun_logstore_pg.socket.socket", side_effect=OSError("boom")),
caplog.at_level("DEBUG", logger="extensions.logstore.aliyun_logstore_pg"),
):
result = store._check_port_connectivity("example.invalid", 9999)
assert result is False
matching_records = [r for r in caplog.records if r.name == "extensions.logstore.aliyun_logstore_pg"]
assert len(matching_records) == 1
record = matching_records[0]
assert record.levelname == "DEBUG"
assert record.exc_info is not None
formatted = caplog.text
assert "Port connectivity check failed for example.invalid:9999" in formatted
assert "Traceback (most recent call last)" in formatted
assert "OSError: boom" in formatted
# The exception is captured via the traceback, not interpolated into the
# format string — so the message text is the short form, not the long one.
assert "boom" not in record.getMessage()

View File

@ -233,6 +233,32 @@ def test_build_application_services_wires_billing_service(
sync_partner_tenants_bindings.assert_called_once_with("account-1", "partner-key", "click-1")
def test_build_application_services_wires_education_rate_limiters(
sqlite_session_factory: sessionmaker[Session],
) -> None:
redis = MagicMock(spec=RedisClientWrapper)
with patch("extensions.ext_application_services.RateLimiter") as rate_limiter_type:
ext_application_services.build_application_services(
database_client=sqlite_session_factory,
deployment_edition=DeploymentEdition.COMMUNITY,
initialization_password="",
redis=redis,
)
rate_limiter_type.assert_any_call(
prefix="edu_verification_rate_limit",
max_attempts=10,
time_window=60,
redis_client=redis,
)
rate_limiter_type.assert_any_call(
prefix="edu_activation_rate_limit",
max_attempts=10,
time_window=60,
redis_client=redis,
)
def test_build_application_services_wires_account_profile_repository(
sqlite_session_factory: sessionmaker[Session],
) -> None:

View File

@ -165,3 +165,55 @@ def test_message_inputs_resolve_file_tenant_with_caller_session(sqlite_session:
inputs = message.inputs_with_session(session=sqlite_session)
assert inputs["file"] == "tenant-1"
# session.scalar.assert_called_once() # removed: see #40799 conftest note
def test_file_url_bare_url_re_signed():
"""#40788: bare tool file URL (no markdown wrapping) must be re-signed.
Bare URLs are produced by the agent runtime when the model returns a
non-linkified file reference. The previous regex only matched
[text](url) markdown form, so the bare URL kept INTERNAL_FILES_URL.
"""
upload_id = "bare-1"
url = f"/files/{upload_id}/file-preview?timestamp=1&nonce=2&sign=3"
msg = Message(answer=url) # bare, no markdown wrapping
out = msg.re_sign_file_url_answer
assert f"https://signed.example/{upload_id}" in out
assert url not in out
def test_file_url_backticked_url_re_signed():
"""#40788: backticked tool file URL must be re-signed.
Backticks are used by the runtime to quote file references inline.
The previous regex only matched [text](url), so backticked URLs
stayed on INTERNAL_FILES_URL.
"""
upload_id = "tick-2"
url = f"/files/{upload_id}/file-preview?timestamp=10&nonce=20&sign=30"
msg = Message(answer=f"see `{url}` for details")
out = msg.re_sign_file_url_answer
assert f"https://signed.example/{upload_id}" in out
assert url not in out
def test_file_url_mixed_formats_all_re_signed():
"""#40788: all three URL shapes in the same answer are re-signed.
Sanity check that the three patterns cooperate bare, backticked,
and markdown without double-signing or skipping any.
"""
upload_ids = ["mix-a", "mix-b", "mix-c"]
bare = f"/files/{upload_ids[0]}/file-preview?timestamp=1&nonce=2&sign=3"
tick = f"/files/{upload_ids[1]}/file-preview?timestamp=10&nonce=20&sign=30"
md = f"/files/{upload_ids[2]}/file-preview?timestamp=100&nonce=200&sign=300"
msg = Message(answer=f"raw {bare} ` {tick} ` and [file]({md})")
out = msg.re_sign_file_url_answer
for uid in upload_ids:
assert f"https://signed.example/{uid}" in out
for url in (bare, tick, md):
assert url not in out

View File

@ -4,6 +4,7 @@ from sqlalchemy.orm import Session, sessionmaker
from models.enums import TagType
from models.model import Tag, TagBinding
from models.skill import Skill
from models.snippet import CustomizedSnippet, SnippetType
from repositories.tag_repository import TagRepository
from services.tag_application_service import (
@ -35,6 +36,18 @@ def _snippet(snippet_id: str, *, workspace_id: str) -> CustomizedSnippet:
return snippet
def _skill(skill_id: str, *, workspace_id: str) -> Skill:
skill = Skill(
tenant_id=workspace_id,
name=f"skill-{skill_id}",
display_name="Skill",
created_by="account-1",
updated_by="account-1",
)
skill.id = skill_id
return skill
def test_list_tags_scopes_binding_counts_and_escapes_keyword(
sqlite_session_factory: sessionmaker[Session],
) -> None:
@ -127,3 +140,38 @@ def test_binding_mutation_rejects_missing_target(sqlite_session_factory: session
"account-1",
TagBindingInput(("tag-1",), "missing", "snippet"),
)
def test_binding_mutations_validate_skill_target(
sqlite_session_factory: sessionmaker[Session],
) -> None:
with sqlite_session_factory.begin() as session:
session.add_all(
[
_skill("skill-1", workspace_id="workspace-1"),
_tag("tag-1", workspace_id="workspace-1", tag_type=TagType.SKILL, name="Skill"),
_tag("tag-2", workspace_id="workspace-1", tag_type=TagType.APP, name="Wrong type"),
_tag("tag-3", workspace_id="workspace-2", tag_type=TagType.SKILL, name="Wrong workspace"),
]
)
repository = TagRepository(sqlite_session_factory)
binding = TagBindingInput(("tag-1", "tag-2", "tag-3"), "skill-1", "skill")
repository.create_bindings("workspace-1", "account-1", binding)
with sqlite_session_factory() as session:
bindings = session.scalars(select(TagBinding).where(TagBinding.target_id == "skill-1")).all()
assert len(bindings) == 1
assert bindings[0].tag_id == "tag-1"
assert bindings[0].tenant_id == "workspace-1"
repository.delete_bindings("workspace-1", binding)
with sqlite_session_factory() as session:
assert session.scalars(select(TagBinding).where(TagBinding.target_id == "skill-1")).all() == []
with pytest.raises(TagBindingTargetNotFoundError, match="Skill not found"):
repository.create_bindings(
"workspace-1",
"account-1",
TagBindingInput(("tag-1",), "missing", "skill"),
)

View File

@ -246,6 +246,7 @@ def test_export_agent_app_uses_draft_or_active_snapshot(use_draft: bool) -> None
draft = SimpleNamespace(config_snapshot_dict=AgentSoulConfig(config_note="draft").model_dump(mode="json"))
session = Mock()
session.scalar.side_effect = [agent, draft if use_draft else None]
session.execute.return_value = []
service = AgentDslService(session)
require_snapshot = Mock(return_value=_snapshot(soul=AgentSoulConfig(config_note="snapshot")))
service._require_snapshot = require_snapshot
@ -271,6 +272,7 @@ def test_export_workflow_packages_deduplicates_shared_agent() -> None:
]
session = Mock()
session.scalars.return_value.all.return_value = bindings
session.execute.return_value = []
service = AgentDslService(session)
service._require_agent = Mock(return_value=_agent())
service._require_snapshot = Mock(return_value=_snapshot())

View File

@ -3481,7 +3481,7 @@ def test_composer_current_version_and_error_paths(monkeypatch: pytest.MonkeyPatc
monkeypatch.setattr(
AgentComposerService,
"_require_agent",
lambda **kwargs: SimpleNamespace(updated_by=None, active_config_is_published=False),
lambda **kwargs: SimpleNamespace(id="agent-1", updated_by=None, active_config_is_published=False),
)
result = AgentComposerService._save_to_current_version(
session=session,
@ -5159,7 +5159,12 @@ class TestAgentAppBackingAgent:
use_icon_as_answer_icon=False,
tracing=None,
)
source_agent = SimpleNamespace(id="source-agent", role="Analyst")
source_agent = SimpleNamespace(
id="source-agent",
role="Analyst",
active_config_snapshot_id="source-snapshot",
active_config_is_published=True,
)
target_app = SimpleNamespace(id="target-app")
session = sqlite_session
service = AgentRosterService(session)
@ -5225,7 +5230,12 @@ class TestAgentAppBackingAgent:
use_icon_as_answer_icon=False,
tracing=None,
)
source_agent = SimpleNamespace(id="source-agent", role="Analyst")
source_agent = SimpleNamespace(
id="source-agent",
role="Analyst",
active_config_snapshot_id="source-snapshot",
active_config_is_published=True,
)
target_app = SimpleNamespace(id="target-app")
session = sqlite_session
service = AgentRosterService(session)

View File

@ -2,7 +2,11 @@ from datetime import UTC, datetime
from unittest.mock import patch
from services.account_billing_adapters import BillingAccountEducationGateway
from services.entities.account_entities import AccountEducationAutocomplete, AccountEducationStatus
from services.entities.account_entities import (
AccountEducationActivation,
AccountEducationAutocomplete,
AccountEducationStatus,
)
def test_education_gateway_normalizes_billing_status_timestamp() -> None:
@ -36,19 +40,20 @@ def test_education_gateway_activates_with_primitive_account_context() -> None:
) as activate:
result = gateway.activate(
account_id="account-1",
email="student@example.edu",
tenant_id="workspace-1",
token="education-token",
institution="Dify University",
role="Student",
)
assert result == {"message": "success"}
account = activate.call_args.args[0]
assert account.id == "account-1"
assert account.email == "student@example.edu"
assert account.current_tenant_id == "workspace-1"
assert activate.call_args.args[1:] == ("education-token", "Dify University", "Student")
assert result == AccountEducationActivation(message="success")
activate.assert_called_once_with(
account_id="account-1",
tenant_id="workspace-1",
token="education-token",
institution="Dify University",
role="Student",
)
def test_education_gateway_normalizes_autocomplete_defaults() -> None:

View File

@ -3,10 +3,18 @@ from __future__ import annotations
from datetime import UTC, datetime
from unittest.mock import Mock
import pytest
from machinery.context import RequestContext
from services.account_education_service import AccountEducationGateway, AccountEducationService
from services.account_education_service import (
AccountEducationGateway,
AccountEducationRateLimiter,
AccountEducationService,
)
from services.account_errors import EducationRateLimitExceededError
from services.account_ports import AccountRepository
from services.entities.account_entities import (
AccountEducationActivation,
AccountEducationAutocomplete,
AccountEducationStatus,
AccountEducationVerification,
@ -41,18 +49,40 @@ def _account() -> AccountSnapshot:
)
def _rate_limiter() -> Mock:
rate_limiter = Mock(spec=AccountEducationRateLimiter)
rate_limiter.is_rate_limited.return_value = False
return rate_limiter
def test_verify_reads_account_before_billing_gateway_call() -> None:
accounts = Mock(spec=AccountRepository)
accounts.get.return_value = _account()
education = Mock(spec=AccountEducationGateway)
education.verify.return_value = AccountEducationVerification(token="education-token")
service = AccountEducationService(accounts=accounts, education=education)
verification_rate_limiter = _rate_limiter()
activation_rate_limiter = _rate_limiter()
events: list[str] = []
verification_rate_limiter.is_rate_limited.side_effect = lambda _key: events.append("check") or False
verification_rate_limiter.increment_rate_limit.side_effect = lambda _key: events.append("increment")
education.verify.side_effect = lambda **_kwargs: (
events.append("verify") or AccountEducationVerification(token="education-token")
)
service = AccountEducationService(
accounts=accounts,
education=education,
verification_rate_limiter=verification_rate_limiter,
activation_rate_limiter=activation_rate_limiter,
)
result = service.verify(_context())
assert result == AccountEducationVerification(token="education-token")
assert events == ["check", "increment", "verify"]
accounts.get.assert_called_once_with("account-1")
education.verify.assert_called_once_with(account_id="account-1", email="student@example.edu")
verification_rate_limiter.is_rate_limited.assert_called_once_with("student@example.edu")
verification_rate_limiter.increment_rate_limit.assert_called_once_with("student@example.edu")
education.verify.assert_called_once_with(account_id="account-1")
activation_rate_limiter.is_rate_limited.assert_not_called()
def test_status_and_autocomplete_delegate_framework_neutral_contracts() -> None:
@ -70,6 +100,8 @@ def test_status_and_autocomplete_delegate_framework_neutral_contracts() -> None:
service = AccountEducationService(
accounts=accounts,
education=education,
verification_rate_limiter=_rate_limiter(),
activation_rate_limiter=_rate_limiter(),
)
assert service.status(_context()) == status
@ -82,10 +114,18 @@ def test_activate_delegates_account_and_workspace_context() -> None:
accounts = Mock(spec=AccountRepository)
accounts.get.return_value = _account()
education = Mock(spec=AccountEducationGateway)
education.activate.return_value = {"message": "success"}
activation = AccountEducationActivation(message="success")
verification_rate_limiter = _rate_limiter()
activation_rate_limiter = _rate_limiter()
events: list[str] = []
activation_rate_limiter.is_rate_limited.side_effect = lambda _key: events.append("check") or False
activation_rate_limiter.increment_rate_limit.side_effect = lambda _key: events.append("increment")
education.activate.side_effect = lambda **_kwargs: events.append("activate") or activation
service = AccountEducationService(
accounts=accounts,
education=education,
verification_rate_limiter=verification_rate_limiter,
activation_rate_limiter=activation_rate_limiter,
)
result = service.activate(
@ -95,12 +135,60 @@ def test_activate_delegates_account_and_workspace_context() -> None:
role="Student",
)
assert result == {"message": "success"}
assert result == activation
assert events == ["check", "increment", "activate"]
activation_rate_limiter.is_rate_limited.assert_called_once_with("student@example.edu")
activation_rate_limiter.increment_rate_limit.assert_called_once_with("student@example.edu")
verification_rate_limiter.is_rate_limited.assert_not_called()
education.activate.assert_called_once_with(
account_id="account-1",
email="student@example.edu",
tenant_id="workspace-1",
token="education-token",
institution="Dify University",
role="Student",
)
def test_verify_rejects_rate_limited_request() -> None:
accounts = Mock(spec=AccountRepository)
accounts.get.return_value = _account()
education = Mock(spec=AccountEducationGateway)
verification_rate_limiter = _rate_limiter()
verification_rate_limiter.is_rate_limited.return_value = True
service = AccountEducationService(
accounts=accounts,
education=education,
verification_rate_limiter=verification_rate_limiter,
activation_rate_limiter=_rate_limiter(),
)
with pytest.raises(EducationRateLimitExceededError):
service.verify(_context())
verification_rate_limiter.increment_rate_limit.assert_not_called()
education.verify.assert_not_called()
def test_activate_rejects_rate_limited_request() -> None:
accounts = Mock(spec=AccountRepository)
accounts.get.return_value = _account()
education = Mock(spec=AccountEducationGateway)
activation_rate_limiter = _rate_limiter()
activation_rate_limiter.is_rate_limited.return_value = True
service = AccountEducationService(
accounts=accounts,
education=education,
verification_rate_limiter=_rate_limiter(),
activation_rate_limiter=activation_rate_limiter,
)
with pytest.raises(EducationRateLimitExceededError):
service.activate(
_context(),
token="education-token",
institution="Dify University",
role="Student",
)
activation_rate_limiter.increment_rate_limit.assert_not_called()
education.activate.assert_not_called()

View File

@ -697,7 +697,7 @@ class TestBillingServiceUsageCalculation:
"""Unit tests for usage calculation and credit management.
Tests cover:
- Feature plan usage information retrieval
- Quota information retrieval
- Credit addition (positive delta)
- Credit consumption (negative delta)
- Usage refunds
@ -710,20 +710,6 @@ class TestBillingServiceUsageCalculation:
with patch.object(BillingService, "_send_request") as mock:
yield mock
def test_get_tenant_feature_plan_usage_info(self, mock_send_request):
"""Test retrieval of tenant feature plan usage information (legacy endpoint)."""
# Arrange
tenant_id = "tenant-123"
expected_response = {"features": {"trigger": {"used": 50, "limit": 100}, "workflow": {"used": 20, "limit": 50}}}
mock_send_request.return_value = expected_response
# Act
result = BillingService.get_tenant_feature_plan_usage_info(tenant_id)
# Assert
assert result == expected_response
mock_send_request.assert_called_once_with("GET", "/tenant-feature-usage/info", params={"tenant_id": tenant_id})
def test_get_quota_info(self):
"""Test retrieval of quota info from new endpoint."""
# Arrange
@ -1043,15 +1029,7 @@ class TestBillingServiceQuotaOperations:
class TestBillingServiceRateLimitEnforcement:
"""Unit tests for rate limit enforcement mechanisms.
Tests cover:
- Compliance download rate limiting (4 requests per 60 seconds)
- Education verification rate limiting (10 requests per 60 seconds)
- Education activation rate limiting (10 requests per 60 seconds)
- Rate limit increment after successful operations
- Proper exception raising when limits are exceeded
"""
"""Unit tests for compliance download rate-limit enforcement."""
@pytest.fixture
def mock_send_request(self):
@ -1121,109 +1099,6 @@ class TestBillingServiceRateLimitEnforcement:
mock_is_limited.assert_called_once_with(f"{account_id}:{tenant_id}")
mock_send_request.assert_not_called()
def test_education_verify_rate_limit_not_exceeded(self, mock_send_request):
"""Test education verification when rate limit is not exceeded."""
# Arrange
account_id = "account-123"
account_email = "student@university.edu"
expected_response = {"verified": True, "institution": "University"}
# Mock the rate limiter to return False (not limited)
with (
patch.object(
BillingService.EducationIdentity.verification_rate_limit, "is_rate_limited", return_value=False
) as mock_is_limited,
patch.object(
BillingService.EducationIdentity.verification_rate_limit, "increment_rate_limit"
) as mock_increment,
):
mock_send_request.return_value = expected_response
# Act
result = BillingService.EducationIdentity.verify(account_id, account_email)
# Assert
assert result == expected_response
mock_is_limited.assert_called_once_with(account_email)
mock_send_request.assert_called_once_with("GET", "/education/verify", params={"account_id": account_id})
mock_increment.assert_called_once_with(account_email)
def test_education_verify_rate_limit_exceeded(self, mock_send_request):
"""Test education verification when rate limit is exceeded."""
# Arrange
account_id = "account-123"
account_email = "student@university.edu"
# Import the error class to properly catch it
from controllers.console.error import EducationVerifyLimitError
# Mock the rate limiter to return True (rate limited)
with patch.object(
BillingService.EducationIdentity.verification_rate_limit, "is_rate_limited", return_value=True
) as mock_is_limited:
# Act & Assert
with pytest.raises(EducationVerifyLimitError):
BillingService.EducationIdentity.verify(account_id, account_email)
mock_is_limited.assert_called_once_with(account_email)
mock_send_request.assert_not_called()
def test_education_activate_rate_limit_not_exceeded(self, mock_send_request):
"""Test education activation when rate limit is not exceeded."""
# Arrange
account = _account(email="student@university.edu")
token = "verification-token"
institution = "MIT"
role = "student"
expected_response = {"result": "success", "activated": True}
# Mock the rate limiter to return False (not limited)
with (
patch.object(
BillingService.EducationIdentity.activation_rate_limit, "is_rate_limited", return_value=False
) as mock_is_limited,
patch.object(
BillingService.EducationIdentity.activation_rate_limit, "increment_rate_limit"
) as mock_increment,
):
mock_send_request.return_value = expected_response
# Act
result = BillingService.EducationIdentity.activate(account, token, institution, role)
# Assert
assert result == expected_response
mock_is_limited.assert_called_once_with(account.email)
mock_send_request.assert_called_once_with(
"POST",
"/education/",
json={"institution": institution, "token": token, "role": role},
params={"account_id": account.id, "curr_tenant_id": account.current_tenant_id},
)
mock_increment.assert_called_once_with(account.email)
def test_education_activate_rate_limit_exceeded(self, mock_send_request):
"""Test education activation when rate limit is exceeded."""
# Arrange
account = _account(email="student@university.edu")
token = "verification-token"
institution = "MIT"
role = "student"
# Import the error class to properly catch it
from controllers.console.error import EducationActivateLimitError
# Mock the rate limiter to return True (rate limited)
with patch.object(
BillingService.EducationIdentity.activation_rate_limit, "is_rate_limited", return_value=True
) as mock_is_limited:
# Act & Assert
with pytest.raises(EducationActivateLimitError):
BillingService.EducationIdentity.activate(account, token, institution, role)
mock_is_limited.assert_called_once_with(account.email)
mock_send_request.assert_not_called()
class TestBillingServiceEducationIdentity:
"""Unit tests for education identity verification and management.
@ -1240,11 +1115,46 @@ class TestBillingServiceEducationIdentity:
with patch.object(BillingService, "_send_request") as mock:
yield mock
def test_education_verify(self, mock_send_request):
account_id = "account-123"
expected_response = {"token": "education-token"}
mock_send_request.return_value = expected_response
result = BillingService.EducationIdentity.verify(account_id)
assert result == expected_response
mock_send_request.assert_called_once_with("GET", "/education/verify", params={"account_id": account_id})
def test_education_activate(self, mock_send_request):
expected_response = {"message": "success"}
mock_send_request.return_value = expected_response
result = BillingService.EducationIdentity.activate(
account_id="account-123",
tenant_id="tenant-456",
token="verification-token",
institution="MIT",
role="student",
)
assert result == expected_response
mock_send_request.assert_called_once_with(
"POST",
"/education/",
json={"institution": "MIT", "token": "verification-token", "role": "student"},
params={"account_id": "account-123", "curr_tenant_id": "tenant-456"},
)
def test_education_status(self, mock_send_request):
"""Test checking education verification status."""
# Arrange
account_id = "account-123"
expected_response = {"verified": True, "institution": "MIT", "role": "student"}
expected_response = {
"result": True,
"is_student": True,
"expire_at": "2027-01-01T00:00:00Z",
"allow_refresh": False,
}
mock_send_request.return_value = expected_response
# Act
@ -1261,10 +1171,9 @@ class TestBillingServiceEducationIdentity:
page = 0
limit = 20
expected_response = {
"institutions": [
{"name": "Massachusetts Institute of Technology", "domain": "mit.edu"},
{"name": "University of Massachusetts", "domain": "umass.edu"},
]
"data": ["Massachusetts Institute of Technology", "University of Massachusetts"],
"curr_page": 0,
"has_next": False,
}
mock_send_request.return_value = expected_response
@ -1281,7 +1190,7 @@ class TestBillingServiceEducationIdentity:
"""Test education institution autocomplete with default parameters."""
# Arrange
keywords = "Stanford"
expected_response = {"institutions": [{"name": "Stanford University", "domain": "stanford.edu"}]}
expected_response = {"data": ["Stanford University"], "curr_page": 0, "has_next": False}
mock_send_request.return_value = expected_response
# Act
@ -1315,7 +1224,7 @@ class TestBillingServiceAccountManagement:
"""Test account deletion."""
# Arrange
account_id = "account-123"
expected_response = {"result": "success", "deleted": True}
expected_response = {"message": "Account deleted successfully."}
mock_send_request.return_value = expected_response
# Act
@ -1377,7 +1286,7 @@ class TestBillingServiceAccountManagement:
# Arrange
email = "user@example.com"
feedback = "Service was too expensive"
expected_response = {"result": "success"}
expected_response = {"message": "Reason added successfully."}
mock_send_request.return_value = expected_response
# Act
@ -1437,7 +1346,7 @@ class TestBillingServicePartnerIntegration:
account_id = "account-123"
partner_key = "partner-xyz"
click_id = "click-789"
expected_response = {"result": "success", "synced": True}
expected_response = {"message": "Successfully synced partner tenants"}
mock_send_request.return_value = expected_response
# Act
@ -1933,49 +1842,6 @@ class TestBillingServiceIntegrationScenarios:
assert mock_is_limited.call_count == 3
assert mock_increment.call_count == 3
def test_education_verification_and_activation_flow(self, mock_send_request):
"""Test complete education verification and activation flow."""
# Arrange
account = _account(email="student@mit.edu")
# Step 1: Search for institution
with (
patch.object(
BillingService.EducationIdentity.verification_rate_limit, "is_rate_limited", return_value=False
),
patch.object(BillingService.EducationIdentity.verification_rate_limit, "increment_rate_limit"),
):
mock_send_request.return_value = {
"institutions": [{"name": "Massachusetts Institute of Technology", "domain": "mit.edu"}]
}
institutions = BillingService.EducationIdentity.autocomplete("MIT")
assert len(institutions["institutions"]) > 0
# Step 2: Verify email
with (
patch.object(
BillingService.EducationIdentity.verification_rate_limit, "is_rate_limited", return_value=False
),
patch.object(BillingService.EducationIdentity.verification_rate_limit, "increment_rate_limit"),
):
mock_send_request.return_value = {"verified": True, "institution": "MIT"}
verify_result = BillingService.EducationIdentity.verify(account.id, account.email)
assert verify_result["verified"] is True
# Step 3: Check status
mock_send_request.return_value = {"verified": True, "institution": "MIT", "role": "student"}
status = BillingService.EducationIdentity.status(account.id)
assert status["verified"] is True
# Step 4: Activate education benefits
with (
patch.object(BillingService.EducationIdentity.activation_rate_limit, "is_rate_limited", return_value=False),
patch.object(BillingService.EducationIdentity.activation_rate_limit, "increment_rate_limit"),
):
mock_send_request.return_value = {"result": "success", "activated": True}
activate_result = BillingService.EducationIdentity.activate(account, "token-123", "MIT", "student")
assert activate_result["activated"] is True
class TestBillingServiceSubscriptionInfoDataType:
"""Unit tests for data type coercion in BillingService.get_info

View File

@ -28,13 +28,16 @@ def test_workspace_queries_use_workspace_from_request_context() -> None:
vector_space = VectorSpaceLimitationModel(size=1, limit=5)
gateway.get_workspace_features.return_value = features
gateway.get_vector_space.return_value = vector_space
service = FeatureQueryService(features=gateway, trial_models=(), app_dsl_version="0.7.0")
gateway.get_trial_models.return_value = ["langgenius/openai/openai"]
service = FeatureQueryService(features=gateway, app_dsl_version="0.7.0")
context = _request_context()
assert service.get_features(context) is features
assert service.get_vector_space(context) is vector_space
assert service.get_trial_models(context) == ["langgenius/openai/openai"]
gateway.get_workspace_features.assert_called_once_with("workspace_123")
gateway.get_vector_space.assert_called_once_with("workspace_123")
gateway.get_trial_models.assert_called_once_with("workspace_123")
def test_deployment_queries_delegate_without_request_context() -> None:
@ -45,11 +48,9 @@ def test_deployment_queries_delegate_without_request_context() -> None:
gateway.get_license.return_value = license_model
service = FeatureQueryService(
features=gateway,
trial_models=["langgenius/openai/openai"],
app_dsl_version="0.6.0",
)
assert service.get_trial_models() == ["langgenius/openai/openai"]
assert service.get_app_dsl_version() == "0.6.0"
assert service.get_system_features() is system_features
assert service.get_license() is license_model
@ -57,7 +58,7 @@ def test_deployment_queries_delegate_without_request_context() -> None:
def test_workspace_queries_require_active_workspace() -> None:
gateway = create_autospec(FeatureQueryGateway, instance=True, spec_set=True)
service = FeatureQueryService(features=gateway, trial_models=(), app_dsl_version="0.7.0")
service = FeatureQueryService(features=gateway, app_dsl_version="0.7.0")
with pytest.raises(RuntimeError, match="did not resolve an active workspace"):
service.get_features(_request_context(active_workspace_id=None))

View File

@ -24,3 +24,13 @@ def test_workspace_features_exclude_independently_queried_vector_space(mocker: M
assert result is features
get_features.assert_called_once_with("workspace_123", exclude_vector_space=True)
def test_trial_models_delegate_to_workspace_aware_feature_service(mocker: MockerFixture) -> None:
trial_models = ["langgenius/openai/openai"]
get_trial_models = mocker.patch.object(FeatureService, "get_trial_models", return_value=trial_models)
result = FeatureServiceGateway().get_trial_models("workspace_123")
assert result == trial_models
get_trial_models.assert_called_once_with("workspace_123")

View File

@ -1,6 +1,8 @@
from unittest.mock import Mock
import pytest
from enums import HostedTrialProvider
from enums import CloudPlan, DeploymentEdition, HostedTrialProvider
from services import feature_service as feature_service_module
from services.feature_service import FeatureService
@ -11,7 +13,7 @@ def test_get_system_features_excludes_trial_models():
assert "trial_models" not in result
def test_get_trial_models_returns_providers_enabled_for_paid_and_trial(monkeypatch: pytest.MonkeyPatch):
def test_get_trial_models_returns_providers_with_paid_or_trial_enabled(monkeypatch: pytest.MonkeyPatch):
for provider in HostedTrialProvider:
monkeypatch.setattr(
feature_service_module.dify_config,
@ -28,11 +30,50 @@ def test_get_trial_models_returns_providers_enabled_for_paid_and_trial(monkeypat
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_OPENAI_PAID_ENABLED", True, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_OPENAI_TRIAL_ENABLED", True, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_ANTHROPIC_PAID_ENABLED", True, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_ANTHROPIC_TRIAL_ENABLED", False, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_GEMINI_PAID_ENABLED", False, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_GEMINI_TRIAL_ENABLED", True, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_XAI_PAID_ENABLED", True, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "DEPLOYMENT_EDITION", DeploymentEdition.COMMUNITY)
result = FeatureService.get_trial_models()
result = FeatureService.get_trial_models("tenant_1")
assert result == [HostedTrialProvider.OPENAI.value]
assert result == [
HostedTrialProvider.OPENAI.value,
HostedTrialProvider.X.value,
]
@pytest.mark.parametrize(
("plan", "expected"),
[
(CloudPlan.SANDBOX, [HostedTrialProvider.OPENAI.value]),
(CloudPlan.PROFESSIONAL, [HostedTrialProvider.OPENAI.value, HostedTrialProvider.X.value]),
],
)
def test_get_trial_models_filters_providers_by_workspace_plan(
monkeypatch: pytest.MonkeyPatch,
plan: CloudPlan,
expected: list[str],
) -> None:
for provider in HostedTrialProvider:
monkeypatch.setattr(
feature_service_module.dify_config,
f"HOSTED_{provider.config_key}_PAID_ENABLED",
False,
raising=False,
)
monkeypatch.setattr(
feature_service_module.dify_config,
f"HOSTED_{provider.config_key}_TRIAL_ENABLED",
False,
raising=False,
)
monkeypatch.setattr(feature_service_module.dify_config, "DEPLOYMENT_EDITION", DeploymentEdition.CLOUD)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_OPENAI_TRIAL_ENABLED", True, raising=False)
monkeypatch.setattr(feature_service_module.dify_config, "HOSTED_XAI_PAID_ENABLED", True, raising=False)
get_workspace_plan = Mock(return_value=plan)
monkeypatch.setattr(feature_service_module.FeatureService, "get_workspace_plan", get_workspace_plan)
result = FeatureService.get_trial_models("tenant_1")
assert result == expected
get_workspace_plan.assert_called_once_with("tenant_1")

View File

@ -7,6 +7,7 @@ from services.tag_application_service import (
CreateTagInput,
TagApplicationService,
TagBindingInput,
TagBindingTargetNotFoundError,
TagSummary,
UpdateTagInput,
)
@ -47,3 +48,7 @@ def test_service_rejects_context_without_active_workspace() -> None:
with pytest.raises(RuntimeError, match="active workspace"):
service.list_tags(context, "app")
def test_skill_binding_target_error_message() -> None:
assert str(TagBindingTargetNotFoundError("skill")) == "Skill not found"

View File

@ -1,4 +1,5 @@
import logging
from datetime import UTC, datetime
from io import BytesIO
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
@ -9,11 +10,216 @@ from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session
from werkzeug.datastructures import FileStorage
from graphon.enums import WorkflowType
from models.enums import AppTriggerStatus, AppTriggerType, EndUserType
from models.model import App, AppMode, EndUser
from models.tools import ToolFile
from models.trigger import AppTrigger, WorkflowWebhookTrigger
from models.workflow import Workflow
from services.errors.app import QuotaExceededError
from services.trigger import webhook_service as webhook_service_module
from services.trigger.webhook_service import WebhookService
def _webhook_trigger(
*,
webhook_id: str = "webhook-123",
tenant_id: str = "tenant-123",
app_id: str = "app-123",
node_id: str = "node-123",
created_by: str = "account-123",
) -> WorkflowWebhookTrigger:
return WorkflowWebhookTrigger(
webhook_id=webhook_id,
tenant_id=tenant_id,
app_id=app_id,
node_id=node_id,
created_by=created_by,
)
def _tool_file() -> ToolFile:
tool_file = ToolFile(
user_id="user-123",
tenant_id="test_tenant",
conversation_id=None,
file_key="webhook/test.txt",
mimetype="text/plain",
name="test.txt",
size=7,
)
tool_file.id = "test_file_id"
return tool_file
def _workflow(
*,
workflow_id: str = "workflow-123",
tenant_id: str = "tenant-123",
app_id: str = "app-123",
version: str = Workflow.VERSION_DRAFT,
) -> Workflow:
workflow = Workflow.new(
tenant_id=tenant_id,
app_id=app_id,
type=WorkflowType.WORKFLOW.value,
version=version,
graph='{"nodes": [], "edges": []}',
features="{}",
created_by="account-123",
environment_variables=[],
conversation_variables=[],
rag_pipeline_variables=[],
)
workflow.id = workflow_id
return workflow
def _app(*, tenant_id: str = "tenant-123", app_id: str = "app-123") -> App:
return App(
id=app_id,
tenant_id=tenant_id,
name="Webhook App",
description="",
mode=AppMode.WORKFLOW,
enable_site=True,
enable_api=True,
max_active_requests=0,
)
def _app_trigger(*, status: AppTriggerStatus = AppTriggerStatus.ENABLED) -> AppTrigger:
return AppTrigger(
tenant_id="tenant-123",
app_id="app-123",
node_id="node-123",
trigger_type=AppTriggerType.TRIGGER_WEBHOOK,
title="Webhook",
status=status,
)
def _end_user() -> EndUser:
return EndUser(
id="end-user-123",
tenant_id="tenant-123",
app_id="app-123",
type=EndUserType.TRIGGER,
session_id="webhook-session",
)
class TestWebhookServiceLookup:
def test_debug_lookup_scopes_draft_workflow_to_trigger_owner(
self,
sqlite_session: Session,
sqlite_engine: Engine,
monkeypatch: pytest.MonkeyPatch,
) -> None:
target_workflow = _workflow()
target_workflow.created_at = datetime(2025, 1, 1, tzinfo=UTC).replace(tzinfo=None)
tenant_decoy = _workflow(
workflow_id="workflow-decoy",
tenant_id="tenant-other",
app_id="app-123",
)
tenant_decoy.created_at = datetime(2026, 1, 1, tzinfo=UTC).replace(tzinfo=None)
sqlite_session.add_all([_webhook_trigger(), target_workflow, tenant_decoy])
sqlite_session.commit()
monkeypatch.setattr(webhook_service_module, "db", SimpleNamespace(engine=sqlite_engine))
node_config = {"id": "node-123", "data": {}}
with patch.object(Workflow, "get_node_config_by_id", autospec=True, return_value=node_config) as get_node:
webhook_trigger, workflow, result_node_config = WebhookService.get_webhook_trigger_and_workflow(
"webhook-123", is_debug=True
)
assert webhook_trigger.tenant_id == "tenant-123"
assert workflow.id == target_workflow.id
assert result_node_config is node_config
get_node.assert_called_once_with(workflow, "node-123")
def test_published_lookup_uses_persisted_owner_chain(
self,
sqlite_session: Session,
sqlite_engine: Engine,
monkeypatch: pytest.MonkeyPatch,
) -> None:
sqlite_session.add_all([_webhook_trigger(), _app_trigger(), _app()])
sqlite_session.commit()
monkeypatch.setattr(webhook_service_module, "db", SimpleNamespace(engine=sqlite_engine))
published_workflow = _workflow(version="published")
node_config = {"id": "node-123", "data": {}}
with (
patch.object(webhook_service_module, "WorkflowService") as workflow_service_class,
patch.object(Workflow, "get_node_config_by_id", autospec=True, return_value=node_config),
):
get_published_workflow = workflow_service_class.return_value.get_published_workflow
get_published_workflow.return_value = published_workflow
webhook_trigger, workflow, result_node_config = WebhookService.get_webhook_trigger_and_workflow(
"webhook-123"
)
assert webhook_trigger.app_id == "app-123"
assert workflow is published_workflow
assert result_node_config is node_config
persisted_app = get_published_workflow.call_args.args[0]
assert isinstance(persisted_app, App)
assert persisted_app.id == "app-123"
assert persisted_app.tenant_id == "tenant-123"
def test_published_lookup_rejects_cross_tenant_app_decoy(
self,
sqlite_session: Session,
sqlite_engine: Engine,
monkeypatch: pytest.MonkeyPatch,
) -> None:
sqlite_session.add_all(
[
_webhook_trigger(),
_app_trigger(),
_app(tenant_id="tenant-other"),
]
)
sqlite_session.commit()
monkeypatch.setattr(webhook_service_module, "db", SimpleNamespace(engine=sqlite_engine))
with pytest.raises(ValueError, match="App not found"):
WebhookService.get_webhook_trigger_and_workflow("webhook-123")
@pytest.mark.parametrize(
"status",
[AppTriggerStatus.DISABLED, AppTriggerStatus.UNAUTHORIZED],
)
def test_published_lookup_rejects_inactive_trigger(
self,
status: AppTriggerStatus,
sqlite_session: Session,
sqlite_engine: Engine,
monkeypatch: pytest.MonkeyPatch,
) -> None:
sqlite_session.add_all([_webhook_trigger(), _app_trigger(status=status)])
sqlite_session.commit()
monkeypatch.setattr(webhook_service_module, "db", SimpleNamespace(engine=sqlite_engine))
with pytest.raises(ValueError, match="disabled"):
WebhookService.get_webhook_trigger_and_workflow("webhook-123")
def test_published_lookup_reports_rate_limited_trigger(
self,
sqlite_session: Session,
sqlite_engine: Engine,
monkeypatch: pytest.MonkeyPatch,
) -> None:
sqlite_session.add_all([_webhook_trigger(), _app_trigger(status=AppTriggerStatus.RATE_LIMITED)])
sqlite_session.commit()
monkeypatch.setattr(webhook_service_module, "db", SimpleNamespace(engine=sqlite_engine))
with pytest.raises(QuotaExceededError):
WebhookService.get_webhook_trigger_and_workflow("webhook-123")
class TestWebhookServiceUnit:
"""Webhook business-logic tests with isolated sessions where the service owns their lifecycle."""
@ -25,13 +231,8 @@ class TestWebhookServiceUnit:
) -> None:
"""Quota failures refund the charge and close the real service-owned session."""
monkeypatch.setattr(webhook_service_module, "db", SimpleNamespace(engine=sqlite_engine))
webhook_trigger = MagicMock(
webhook_id="webhook-123",
tenant_id="tenant-123",
app_id="app-123",
node_id="node-123",
)
workflow = MagicMock(id="workflow-123")
webhook_trigger = _webhook_trigger()
workflow = _workflow()
quota_charge = MagicMock()
quota_error = QuotaExceededError(feature="workflow", tenant_id="tenant-123", required=1)
@ -39,7 +240,7 @@ class TestWebhookServiceUnit:
with (
patch(
"services.trigger.webhook_service.EndUserService.get_or_create_end_user_by_type",
return_value=MagicMock(id="end-user-123"),
return_value=_end_user(),
),
patch("services.trigger.webhook_service.QuotaService.reserve", return_value=quota_charge),
patch(
@ -71,14 +272,9 @@ class TestWebhookServiceUnit:
sqlite_engine: Engine,
) -> None:
monkeypatch.setattr(webhook_service_module, "db", SimpleNamespace(engine=sqlite_engine))
webhook_trigger = MagicMock(
webhook_id="webhook-123",
tenant_id="tenant-123",
app_id="app-123",
node_id="node-123",
)
workflow = MagicMock(id="workflow-123")
end_user = MagicMock(id="end-user-123")
webhook_trigger = _webhook_trigger()
workflow = _workflow()
end_user = _end_user()
quota_charge = MagicMock()
webhook_data = {
"method": "POST",
@ -108,13 +304,8 @@ class TestWebhookServiceUnit:
quota_charge.refund.assert_not_called()
def test_trigger_workflow_execution_end_user_service_failure(self) -> None:
webhook_trigger = MagicMock(
webhook_id="webhook-123",
tenant_id="tenant-123",
app_id="app-123",
node_id="node-123",
)
workflow = MagicMock(id="workflow-123")
webhook_trigger = _webhook_trigger()
workflow = _workflow()
webhook_data = {"method": "POST", "headers": {}, "query_params": {}, "body": {}, "files": {}}
with patch.object(
@ -136,7 +327,7 @@ class TestWebhookServiceUnit:
query_string="version=1&format=json",
json={"message": "hello", "count": 42},
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_data = WebhookService.extract_webhook_data(webhook_trigger)
assert webhook_data["method"] == "POST"
@ -158,7 +349,7 @@ class TestWebhookServiceUnit:
headers={"Content-Type": "application/json"},
query_string="count=42&threshold=3.14&enabled=true&note=text",
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_data = WebhookService.extract_webhook_data(webhook_trigger)
# After refactoring, raw extraction keeps query params as strings
@ -177,7 +368,7 @@ class TestWebhookServiceUnit:
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={"username": "test", "password": "secret"},
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_data = WebhookService.extract_webhook_data(webhook_trigger)
assert webhook_data["method"] == "POST"
@ -198,7 +389,7 @@ class TestWebhookServiceUnit:
headers={"Content-Type": "multipart/form-data"},
data={"message": "test", "file": file_storage},
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_trigger.tenant_id = "test_tenant"
with patch.object(WebhookService, "_process_file_uploads", autospec=True) as mock_process_files:
@ -218,7 +409,7 @@ class TestWebhookServiceUnit:
with app.test_request_context(
"/webhook", method="POST", headers={"Content-Type": "text/plain"}, data="raw text content"
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_data = WebhookService.extract_webhook_data(webhook_trigger)
assert webhook_data["method"] == "POST"
@ -232,7 +423,7 @@ class TestWebhookServiceUnit:
with app.test_request_context(
"/webhook", method="POST", headers={"Content-Type": "application/octet-stream"}, data=binary_content
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
mock_file = MagicMock()
mock_file.to_dict.return_value = {"file": "data"}
@ -299,7 +490,7 @@ class TestWebhookServiceUnit:
with app.test_request_context(
"/webhook", method="POST", headers={"Content-Type": "application/json"}, data="invalid json"
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
with pytest.raises(ValueError, match="Invalid JSON body"):
WebhookService.extract_webhook_data(webhook_trigger)
@ -369,9 +560,7 @@ class TestWebhookServiceUnit:
"""Test successful file upload processing."""
# Mock ToolFileManager
mock_tool_file_instance = mock_tool_file_manager.return_value # Mock file creation
mock_tool_file = MagicMock()
mock_tool_file.id = "test_file_id"
mock_tool_file_instance.create_file_by_raw.return_value = mock_tool_file
mock_tool_file_instance.create_file_by_raw.return_value = _tool_file()
# Mock file factory
mock_file_obj = MagicMock()
@ -387,7 +576,7 @@ class TestWebhookServiceUnit:
files["file1"].stream.read.return_value = b"content1"
files["file2"].stream.read.return_value = b"content2"
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_trigger.tenant_id = "test_tenant"
result = WebhookService._process_file_uploads(files, webhook_trigger)
@ -406,9 +595,7 @@ class TestWebhookServiceUnit:
"""Test file upload processing with errors."""
# Mock ToolFileManager
mock_tool_file_instance = mock_tool_file_manager.return_value # Mock file creation
mock_tool_file = MagicMock()
mock_tool_file.id = "test_file_id"
mock_tool_file_instance.create_file_by_raw.return_value = mock_tool_file
mock_tool_file_instance.create_file_by_raw.return_value = _tool_file()
# Mock file factory
mock_file_obj = MagicMock()
@ -423,7 +610,7 @@ class TestWebhookServiceUnit:
files["good_file"].stream.read.return_value = b"content"
files["bad_file"].stream.read.side_effect = Exception("Read error")
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_trigger.tenant_id = "test_tenant"
result = WebhookService._process_file_uploads(files, webhook_trigger)
@ -440,7 +627,7 @@ class TestWebhookServiceUnit:
"none_filename": MagicMock(filename=None, content_type="text/plain"),
}
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
webhook_trigger.tenant_id = "test_tenant"
result = WebhookService._process_file_uploads(files, webhook_trigger)
@ -585,7 +772,7 @@ class TestWebhookServiceUnit:
query_string="count=42&enabled=true",
json={"message": "hello", "age": 25},
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
node_config = {
"data": {
"method": "post",
@ -619,7 +806,7 @@ class TestWebhookServiceUnit:
headers={"Content-Type": "application/json"},
data='{"invalid": }',
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
node_config = {
"data": {
"method": "post",
@ -639,7 +826,7 @@ class TestWebhookServiceUnit:
method="GET", # Wrong method
headers={"Content-Type": "application/json"},
):
webhook_trigger = MagicMock()
webhook_trigger = _webhook_trigger()
node_config = {
"data": {
"method": "post", # Expects POST
@ -666,7 +853,7 @@ class TestWebhookServiceUnit:
}
with pytest.raises(ValueError, match="Required header missing: Authorization"):
WebhookService.extract_and_validate_webhook_data(MagicMock(), node_config)
WebhookService.extract_and_validate_webhook_data(_webhook_trigger(), node_config)
def test_extract_and_validate_webhook_request_case_insensitive_headers(self) -> None:
app = Flask(__name__)
@ -685,7 +872,7 @@ class TestWebhookServiceUnit:
}
}
result = WebhookService.extract_and_validate_webhook_data(MagicMock(), node_config)
result = WebhookService.extract_and_validate_webhook_data(_webhook_trigger(), node_config)
assert result["headers"].get("Authorization") == "Bearer token"
@ -707,7 +894,7 @@ class TestWebhookServiceUnit:
}
with pytest.raises(ValueError, match="Required parameter missing: version"):
WebhookService.extract_and_validate_webhook_data(MagicMock(), node_config)
WebhookService.extract_and_validate_webhook_data(_webhook_trigger(), node_config)
def test_extract_and_validate_webhook_request_missing_required_body_param(self) -> None:
app = Flask(__name__)
@ -726,7 +913,7 @@ class TestWebhookServiceUnit:
}
with pytest.raises(ValueError, match="Required body parameter missing: message"):
WebhookService.extract_and_validate_webhook_data(MagicMock(), node_config)
WebhookService.extract_and_validate_webhook_data(_webhook_trigger(), node_config)
def test_extract_and_validate_webhook_request_missing_required_file(self) -> None:
app = Flask(__name__)
@ -744,7 +931,7 @@ class TestWebhookServiceUnit:
}
}
result = WebhookService.extract_and_validate_webhook_data(MagicMock(), node_config)
result = WebhookService.extract_and_validate_webhook_data(_webhook_trigger(), node_config)
assert result["files"] == {}
@ -757,19 +944,19 @@ class TestWebhookServiceUnit:
patch.object(WebhookService, "get_webhook_trigger_and_workflow", autospec=True) as mock_get_trigger,
patch.object(WebhookService, "extract_and_validate_webhook_data", autospec=True) as mock_extract,
):
mock_trigger = MagicMock()
mock_workflow = MagicMock()
webhook_trigger = _webhook_trigger()
workflow = _workflow()
mock_config = {"data": {"test": "config"}}
mock_data = {"test": "data"}
mock_get_trigger.return_value = (mock_trigger, mock_workflow, mock_config)
mock_get_trigger.return_value = (webhook_trigger, workflow, mock_config)
mock_extract.return_value = mock_data
result = _prepare_webhook_execution("test_webhook", is_debug=False)
assert result == (mock_trigger, mock_workflow, mock_config, mock_data, None)
assert result == (webhook_trigger, workflow, mock_config, mock_data, None)
# Reset mock
mock_get_trigger.reset_mock()
result = _prepare_webhook_execution("test_webhook", is_debug=True)
assert result == (mock_trigger, mock_workflow, mock_config, mock_data, None)
assert result == (webhook_trigger, workflow, mock_config, mock_data, None)

View File

@ -14,6 +14,7 @@ from core.workflow.nodes.trigger_webhook.entities import (
WebhookParameter,
)
from graphon.variables.types import SegmentType
from models.trigger import WorkflowWebhookTrigger
from services.trigger import webhook_service as service_module
from services.trigger.webhook_service import WebhookService
@ -23,8 +24,16 @@ def flask_app() -> Flask:
return Flask(__name__)
def _workflow_trigger(**kwargs: Any) -> Any:
return SimpleNamespace(**kwargs)
def _workflow_trigger(**kwargs: Any) -> WorkflowWebhookTrigger:
values = {
"webhook_id": "webhook-123",
"tenant_id": "tenant-1",
"app_id": "app-1",
"node_id": "node-1",
"created_by": "user-1",
}
values.update(kwargs)
return WorkflowWebhookTrigger(**values)
class TestWebhookServiceExtractionFallbacks:
@ -34,7 +43,7 @@ class TestWebhookServiceExtractionFallbacks:
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
) -> None:
webhook_trigger = MagicMock()
webhook_trigger = _workflow_trigger()
with caplog.at_level(logging.WARNING, logger="services.trigger.webhook_service"):
with flask_app.test_request_context(
@ -57,10 +66,10 @@ class TestWebhookServiceExtractionFallbacks:
with flask_app.test_request_context("/webhook", method="POST", data="ab"):
with pytest.raises(RequestEntityTooLarge):
WebhookService.extract_webhook_data(MagicMock())
WebhookService.extract_webhook_data(_workflow_trigger())
def test_extract_octet_stream_body_should_return_none_when_empty_payload(self, flask_app: Flask) -> None:
webhook_trigger = MagicMock()
webhook_trigger = _workflow_trigger()
with flask_app.test_request_context("/webhook", method="POST", data=b""):
body, files = WebhookService._extract_octet_stream_body(webhook_trigger)
@ -73,7 +82,7 @@ class TestWebhookServiceExtractionFallbacks:
flask_app: Flask,
monkeypatch: pytest.MonkeyPatch,
) -> None:
webhook_trigger = MagicMock()
webhook_trigger = _workflow_trigger()
monkeypatch.setattr(
WebhookService, "_detect_binary_mimetype", MagicMock(return_value="application/octet-stream")
)

View File

@ -1,12 +1,56 @@
import json
from unittest.mock import MagicMock, patch
import pytest
import tasks.trigger_processing_tasks as trigger_processing_tasks_module
from core.plugin.entities.plugin_daemon import CredentialType
from graphon.enums import WorkflowType
from models.enums import EndUserType
from models.model import EndUser
from models.trigger import TriggerSubscription, WorkflowPluginTrigger
from models.workflow import Workflow
from services.errors.app import QuotaExceededError
from tasks.trigger_processing_tasks import dispatch_triggered_workflow
def _workflow(*, app_id: str = "app-123") -> Workflow:
workflow = Workflow.new(
tenant_id="tenant-123",
app_id=app_id,
type=WorkflowType.WORKFLOW.value,
version="published",
graph=json.dumps(
{
"nodes": [
{
"id": "node-123",
"data": {"type": trigger_processing_tasks_module.TRIGGER_PLUGIN_NODE_TYPE},
}
],
"edges": [],
}
),
features="{}",
created_by="user-123",
environment_variables=[],
conversation_variables=[],
rag_pipeline_variables=[],
)
workflow.id = "workflow-123"
return workflow
def _end_user() -> EndUser:
return EndUser(
id="end-user-123",
tenant_id="tenant-123",
app_id="app-123",
type=EndUserType.TRIGGER,
session_id="trigger-session",
)
class TestDispatchTriggeredWorkflow:
"""Unit tests covering branch behaviours of ``dispatch_triggered_workflow``.
@ -20,21 +64,31 @@ class TestDispatchTriggeredWorkflow:
@pytest.fixture
def subscription(self):
sub = MagicMock()
sub.id = "subscription-123"
sub.tenant_id = "tenant-123"
sub.provider_id = "langgenius/test_plugin/test_plugin"
sub.endpoint_id = "endpoint-123"
sub.credentials = {}
sub.credential_type = "api_key"
return sub
subscription = TriggerSubscription(
tenant_id="tenant-123",
user_id="user-123",
name="Test Subscription",
endpoint_id="endpoint-123",
provider_id="langgenius/test_plugin/test_plugin",
parameters={},
properties={},
credentials={},
credential_type=CredentialType.API_KEY,
)
subscription.id = "subscription-123"
return subscription
@pytest.fixture
def plugin_trigger(self):
trigger = MagicMock()
trigger = WorkflowPluginTrigger(
app_id="app-123",
node_id="node-123",
tenant_id="tenant-123",
provider_id="langgenius/test_plugin/test_plugin",
event_name="test_event",
subscription_id="subscription-123",
)
trigger.id = "plugin-trigger-123"
trigger.app_id = "app-123"
trigger.node_id = "node-123"
return trigger
@pytest.fixture
@ -144,11 +198,8 @@ class TestDispatchTriggeredWorkflow:
def test_dispatch_marks_rate_limited_when_quota_exceeded(self, subscription, plugin_trigger, dispatch_mocks):
"""Covers QuotaExceededError → mark rate-limited + early return."""
workflow_mock = MagicMock()
workflow_mock.walk_nodes.return_value = iter(
[(plugin_trigger.node_id, {"type": trigger_processing_tasks_module.TRIGGER_PLUGIN_NODE_TYPE})]
)
dispatch_mocks["get_workflows"].return_value = {plugin_trigger.app_id: workflow_mock}
workflow = _workflow()
dispatch_mocks["get_workflows"].return_value = {plugin_trigger.app_id: workflow}
dispatch_mocks["reserve"].side_effect = QuotaExceededError(
feature="trigger", tenant_id=subscription.tenant_id, required=1
)
@ -169,15 +220,11 @@ class TestDispatchTriggeredWorkflow:
self, subscription, plugin_trigger, dispatch_mocks
):
"""Happy path: end user exists and async trigger succeeds."""
workflow_mock = MagicMock()
workflow_mock.id = "workflow-123"
workflow_mock.walk_nodes.return_value = iter(
[(plugin_trigger.node_id, {"type": trigger_processing_tasks_module.TRIGGER_PLUGIN_NODE_TYPE})]
)
dispatch_mocks["get_workflows"].return_value = {plugin_trigger.app_id: workflow_mock}
workflow = _workflow()
dispatch_mocks["get_workflows"].return_value = {plugin_trigger.app_id: workflow}
end_user_mock = MagicMock()
dispatch_mocks["create_end_user_batch"].return_value = {plugin_trigger.app_id: end_user_mock}
end_user = _end_user()
dispatch_mocks["create_end_user_batch"].return_value = {plugin_trigger.app_id: end_user}
dispatched = dispatch_triggered_workflow(
user_id="user-123",
@ -189,7 +236,7 @@ class TestDispatchTriggeredWorkflow:
assert dispatched == 1
dispatch_mocks["trigger_workflow_async"].assert_called_once()
_, kwargs = dispatch_mocks["trigger_workflow_async"].call_args
assert kwargs["user"] is end_user_mock
assert kwargs["user"] is end_user
dispatch_mocks["quota_charge"].commit.assert_called_once()
dispatch_mocks["quota_charge"].refund.assert_not_called()
dispatch_mocks["mark_rate_limited"].assert_not_called()

14
api/uv.lock generated
View File

@ -613,16 +613,16 @@ wheels = [
[[package]]
name = "boto3"
version = "1.43.71"
version = "1.43.76"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore" },
{ name = "jmespath" },
{ name = "s3transfer" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e0/ed/3220ca6396a7ce00a49aa6f43f20cae05ec6ac386e2b5d668adfad0b3eed/boto3-1.43.71.tar.gz", hash = "sha256:e3eddb6346ee23c895dd98e5c2cfc72cb5e5ed111aa6d4331f9e903a3e3bf4ee", size = 112630, upload-time = "2026-08-13T19:20:35.332Z" }
sdist = { url = "https://files.pythonhosted.org/packages/4f/6c/0cc8c0fe7b6564a3dd3b8066a2a262ee9874272defb517f461677a8c76c8/boto3-1.43.76.tar.gz", hash = "sha256:fa4dc85f735bb67d8f82e42ac50af7e1d2c039aeacf40c878067c9046464b6ad", size = 112656, upload-time = "2026-08-20T19:25:46.007Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4f/16/7b54e7f5271756ab13beaaf6da03f62edcc3ee5ded651c2b17297e107040/boto3-1.43.71-py3-none-any.whl", hash = "sha256:3bb46661dc33121e56e3706753069d18f8dbcb644599cf098206546338602e16", size = 140024, upload-time = "2026-08-13T19:20:32.542Z" },
{ url = "https://files.pythonhosted.org/packages/db/81/5179853e14d7068e02d2f535ca620d98d1a95249ccfc2933f414ed51ded4/boto3-1.43.76-py3-none-any.whl", hash = "sha256:249f55274aef9fa3b9de6afb6f5b2effbbb15c5263d97c13e7b691f479a62c04", size = 140025, upload-time = "2026-08-20T19:25:44.427Z" },
]
[[package]]
@ -645,16 +645,16 @@ bedrock-runtime = [
[[package]]
name = "botocore"
version = "1.43.72"
version = "1.43.78"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "python-dateutil" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a1/99/a8cfeaea98d5085a493af909d09d174466482235a7fda291be18c9a5a76e/botocore-1.43.72.tar.gz", hash = "sha256:1b878c69081e8e9d55aa4c0d85683e7b07f0e274a5554662f9507a46641be3d2", size = 15949280, upload-time = "2026-08-14T19:24:48.841Z" }
sdist = { url = "https://files.pythonhosted.org/packages/67/71/490aaa384855bf3b69405ded52ae77a0e5f4eeb2165044fa65466c4d3a73/botocore-1.43.78.tar.gz", hash = "sha256:e8238d22c1e1342025d75d2e33d154a375e7caad0fc67f77d77faa2d82668b94", size = 15982895, upload-time = "2026-08-21T19:37:32.402Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/27/35814fd8701a6b8be0aa47a7fcbd1ea0706189410a47d71ff29ddcc3ad4d/botocore-1.43.72-py3-none-any.whl", hash = "sha256:de5a1bcf8d7602c6cefc15016f15dad82981e339192531f31fa9483e11feea47", size = 15641880, upload-time = "2026-08-14T19:24:45.882Z" },
{ url = "https://files.pythonhosted.org/packages/c5/5b/b7c5c22767c0e8eb4bb2bd0a972d77bd0be3dd9908e15d4487094a31fa4b/botocore-1.43.78-py3-none-any.whl", hash = "sha256:ddd020493235e264b3bd12606f239a3d3b2dd7cfb1d25a0691061183c290c228", size = 15677214, upload-time = "2026-08-21T19:37:28.894Z" },
]
[[package]]
@ -1642,7 +1642,7 @@ requires-dist = [
{ name = "aliyun-log-python-sdk", specifier = "==0.9.44" },
{ name = "azure-identity", specifier = ">=1.25.3,<2.0.0" },
{ name = "bleach", specifier = ">=6.4.0,<7.0.0" },
{ name = "boto3", specifier = ">=1.43.71,<2.0.0" },
{ name = "boto3", specifier = ">=1.43.76,<2.0.0" },
{ name = "celery", specifier = ">=5.6.3,<6.0.0" },
{ name = "croniter", specifier = ">=6.2.2,<7.0.0" },
{ name = "dify-agent", editable = "../dify-agent" },

59
docs/ADVANCED_SETUP.md Normal file
View File

@ -0,0 +1,59 @@
# Advanced Setup
## Custom configurations
If you need to customize the configuration, edit `docker/.env`. The essential startup defaults live in [`docker/.env.example`](../docker/.env.example), and optional advanced variables are split under `docker/envs/` by theme. After making any changes, re-run `docker compose up -d` from the `docker` directory. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## Metrics Monitoring with Grafana
Import the dashboard to Grafana, using Dify's PostgreSQL database as data source, to monitor metrics in granularity of apps, tenants, messages, and more.
- [Grafana Dashboard by @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Deployment with Kubernetes
If you'd like to configure a highly available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Using Terraform for Deployment
Deploy Dify to Cloud Platform with a single click using [terraform](https://www.terraform.io/)
#### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Using AWS CDK for Deployment
Deploy Dify to AWS with [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Using Alibaba Cloud Computing Nest
Quickly deploy Dify to Alibaba cloud with [Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Using Alibaba Cloud Data Management
One-Click deploy Dify to Alibaba Cloud with [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Deploy to AKS with Azure Devops Pipeline
One-Click deploy Dify to AKS with [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### Using Sealos for Deployment
Deploy Dify with one click using the [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -0,0 +1,57 @@
# الإعداد المتقدم
إذا كنت بحاجة إلى تخصيص الإعدادات، فيرجى الرجوع إلى التعليقات في ملف [.env.example](../../docker/.env.example) وتحديث القيم المقابلة في ملف `.env`. بالإضافة إلى ذلك، قد تحتاج إلى إجراء تعديلات على ملف `docker-compose.yaml` نفسه، مثل تغيير إصدارات الصور أو تعيينات المنافذ أو نقاط تحميل وحدات التخزين، بناءً على بيئة النشر ومتطلباتك الخاصة. بعد إجراء أي تغييرات، يرجى إعادة تشغيل `docker-compose up -d`. يمكنك العثور على قائمة كاملة بمتغيرات البيئة المتاحة [هنا](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## مراقبة المقاييس باستخدام Grafana
استيراد لوحة التحكم إلى Grafana، باستخدام قاعدة بيانات PostgreSQL الخاصة بـ Dify كمصدر للبيانات، لمراقبة المقاييس بدقة للتطبيقات والمستأجرين والرسائل وغير ذلك.
- [لوحة تحكم Grafana بواسطة @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## النشر باستخدام Kubernetes
يوجد مجتمع خاص بـ [Helm Charts](https://helm.sh/) وملفات YAML التي تسمح بتنفيذ Dify على Kubernetes للنظام من الإيجابيات العلوية.
- [رسم بياني Helm من قبل @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [رسم بياني Helm من قبل @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [رسم بياني Helm من قبل @magicsong](https://github.com/magicsong/ai-charts)
- [ملف YAML من قبل @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [ملف YAML من قبل @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 جديد! ملفات YAML (تدعم Dify v1.6.0) بواسطة @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### استخدام Terraform للتوزيع
انشر Dify إلى منصة السحابة بنقرة واحدة باستخدام [terraform](https://www.terraform.io/)
#### Azure Global
- [Azure Terraform بواسطة @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform بواسطة @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### استخدام AWS CDK للنشر
انشر Dify على AWS باستخدام [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK بواسطة @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK بواسطة @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### استخدام Alibaba Cloud للنشر
[بسرعة نشر Dify إلى سحابة علي بابا مع عش الحوسبة السحابية علي بابا](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### استخدام Alibaba Cloud Data Management للنشر
انشر Dify على علي بابا كلاود بنقرة واحدة باستخدام [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### استخدام Azure Devops Pipeline للنشر على AKS
انشر Dify على AKS بنقرة واحدة باستخدام [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### استخدام Sealos للنشر
انشر Dify بنقرة واحدة باستخدام [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -96,7 +96,7 @@
قم بإضافة نجمة إلى Dify على GitHub وتلق تنبيهًا فوريًا بالإصدارات الجديدة.
![نجمنا](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## البداية السريعة
@ -121,82 +121,31 @@ docker compose up -d
## الخطوات التالية
إذا كنت بحاجة إلى تخصيص الإعدادات، فيرجى الرجوع إلى التعليقات في ملف [.env.example](../../docker/.env.example) وتحديث القيم المقابلة في ملف `.env`. بالإضافة إلى ذلك، قد تحتاج إلى إجراء تعديلات على ملف `docker-compose.yaml` نفسه، مثل تغيير إصدارات الصور أو تعيينات المنافذ أو نقاط تحميل وحدات التخزين، بناءً على بيئة النشر ومتطلباتك الخاصة. بعد إجراء أي تغييرات، يرجى إعادة تشغيل `docker-compose up -d`. يمكنك العثور على قائمة كاملة بمتغيرات البيئة المتاحة [هنا](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### مراقبة المقاييس باستخدام Grafana
استيراد لوحة التحكم إلى Grafana، باستخدام قاعدة بيانات PostgreSQL الخاصة بـ Dify كمصدر للبيانات، لمراقبة المقاييس بدقة للتطبيقات والمستأجرين والرسائل وغير ذلك.
- [لوحة تحكم Grafana بواسطة @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### النشر باستخدام Kubernetes
يوجد مجتمع خاص بـ [Helm Charts](https://helm.sh/) وملفات YAML التي تسمح بتنفيذ Dify على Kubernetes للنظام من الإيجابيات العلوية.
- [رسم بياني Helm من قبل @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [رسم بياني Helm من قبل @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [رسم بياني Helm من قبل @magicsong](https://github.com/magicsong/ai-charts)
- [ملف YAML من قبل @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [ملف YAML من قبل @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 جديد! ملفات YAML (تدعم Dify v1.6.0) بواسطة @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### استخدام Terraform للتوزيع
انشر Dify إلى منصة السحابة بنقرة واحدة باستخدام [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform بواسطة @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform بواسطة @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### استخدام AWS CDK للنشر
انشر Dify على AWS باستخدام [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK بواسطة @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK بواسطة @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### استخدام Alibaba Cloud للنشر
[بسرعة نشر Dify إلى سحابة علي بابا مع عش الحوسبة السحابية علي بابا](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### استخدام Alibaba Cloud Data Management للنشر
انشر Dify على علي بابا كلاود بنقرة واحدة باستخدام [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### استخدام Azure Devops Pipeline للنشر على AKS
انشر Dify على AKS بنقرة واحدة باستخدام [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### استخدام Sealos للنشر
انشر Dify بنقرة واحدة باستخدام [Sealos App Store](https://sealos.io/products/app-store/dify/)
لخيارات التكوين المخصص والمراقبة والنشر، راجع [الإعداد المتقدم](ADVANCED_SETUP.md).
## المساهمة
لأولئك الذين يرغبون في المساهمة، انظر إلى [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) لدينا.
في الوقت نفسه، يرجى النظر في دعم Dify عن طريق مشاركته على وسائل التواصل الاجتماعي وفي الفعاليات والمؤتمرات.
يرحب Dify بالمساهمات بجميع أشكالها:
> نحن نبحث عن مساهمين لمساعدة في ترجمة Dify إلى لغات أخرى غير اللغة الصينية المندرين أو الإنجليزية. إذا كنت مهتمًا بالمساعدة، يرجى الاطلاع على [README للترجمة](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) لمزيد من المعلومات، واترك لنا تعليقًا في قناة `global-users` على [خادم المجتمع على Discord](https://discord.gg/8Tpq4AcN9c).
- **الشفرة البرمجية**: اقرأ [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)، ثم تصفح [المشكلات المناسبة للمساهمين الجدد](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **الأفكار والملاحظات**: ابدأ أو انضم إلى [مناقشة على GitHub](https://github.com/langgenius/dify/discussions).
- **الترجمات**: اتبع [دليل التدويل](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) لإضافة لغة أو تحديثها.
- **المجتمع**: شارك التطبيقات التي تبنيها، وساعد المستخدمين الآخرين، وساهم في نشر Dify.
**المساهمون**
### المساهمون
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## المجتمع والاتصال
- [مناقشة GitHub](https://github.com/langgenius/dify/discussions). الأفضل لـ: مشاركة التعليقات وطرح الأسئلة.
- [المشكلات على GitHub](https://github.com/langgenius/dify/issues). الأفضل لـ: الأخطاء التي تواجهها في استخدام Dify.AI، واقتراحات الميزات. انظر [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). الأفضل لـ: مشاركة تطبيقاتك والترفيه مع المجتمع.
- [تويتر](https://twitter.com/dify_ai). الأفضل لـ: مشاركة تطبيقاتك والترفيه مع المجتمع.
اختر القناة الأنسب لسؤالك:
- [مناقشات GitHub](https://github.com/langgenius/dify/discussions): للحصول على المساعدة ومشاركة الملاحظات واقتراح الأفكار.
- [مشكلات GitHub](https://github.com/langgenius/dify/issues): للإبلاغ عن الأخطاء القابلة لإعادة الإنتاج ومتابعة العمل الهندسي. اقرأ [دليل المساهمة](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) قبل فتح مشكلة.
- [Discord](https://discord.gg/FngNHpbcY7): للدردشة الفورية ومشاركة تطبيقاتك والتواصل مع مستخدمي Dify الآخرين.
- [X](https://x.com/dify_ai): لمتابعة أخبار الإصدارات وتحديثات المشروع.
## تاريخ النجمة
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,58 @@
# উন্নত সেটআপ
যদি আপনার কনফিগারেশনটি কাস্টমাইজ করার প্রয়োজন হয়, তাহলে অনুগ্রহ করে আমাদের [.env.example](../../docker/.env.example) ফাইল দেখুন এবং আপনার `.env` ফাইলে সংশ্লিষ্ট মানগুলি আপডেট করুন। এছাড়াও, আপনার নির্দিষ্ট এনভায়রনমেন্ট এবং প্রয়োজনীয়তার উপর ভিত্তি করে আপনাকে `docker-compose.yaml` ফাইলে সমন্বয় করতে হতে পারে, যেমন ইমেজ ভার্সন পরিবর্তন করা, পোর্ট ম্যাপিং করা, অথবা ভলিউম মাউন্ট করা।
যেকোনো পরিবর্তন করার পর, অনুগ্রহ করে `docker-compose up -d` পুনরায় চালান। ভেরিয়েবলের সম্পূর্ণ তালিকা [এখানে] (https://docs.dify.ai/getting-started/install-self-hosted/environments) খুঁজে পেতে পারেন।
## Grafana দিয়ে মেট্রিক্স মনিটরিং
Dify-এর PostgreSQL ডাটাবেসকে ডেটা সোর্স হিসাবে ব্যবহার করে, অ্যাপ, টেন্যান্ট, মেসেজ ইত্যাদির গ্র্যানুলারিটিতে মেট্রিক্স মনিটর করার জন্য Grafana-তে ড্যাশবোর্ড ইম্পোর্ট করুন।
- [@bowenliang123 কর্তৃক Grafana ড্যাশবোর্ড](https://github.com/bowenliang123/dify-grafana-dashboard)
## Kubernetes এর সাথে ডেপ্লয়মেন্ট
যদি আপনি একটি হাইলি এভেইলেবল সেটআপ কনফিগার করতে চান, তাহলে কমিউনিটি [Helm Charts](https://helm.sh/) এবং YAML ফাইল রয়েছে যা Dify কে Kubernetes-এ ডিপ্লয় করার প্রক্রিয়া বর্ণনা করে।
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 নতুন! YAML ফাইলসমূহ (Dify v1.6.0 সমর্থিত) তৈরি করেছেন @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### টেরাফর্ম ব্যবহার করে ডিপ্লয়
[terraform](https://www.terraform.io/) ব্যবহার করে এক ক্লিকেই ক্লাউড প্ল্যাটফর্মে Dify ডিপ্লয় করুন।
#### অ্যাজুর গ্লোবাল
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### গুগল ক্লাউড
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### AWS CDK ব্যবহার করে ডিপ্লয়
[CDK](https://aws.amazon.com/cdk/) দিয়ে AWS-এ Dify ডিপ্লয় করুন
#### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud ব্যবহার করে ডিপ্লয়
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management ব্যবহার করে ডিপ্লয়
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### AKS-এ ডিপ্লয় করার জন্য Azure Devops Pipeline ব্যবহার
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) ব্যবহার করে Dify কে AKS-এ এক ক্লিকে ডিপ্লয় করুন
### Sealos ব্যবহার করে ডিপ্লয়
[Sealos App Store](https://sealos.io/products/app-store/dify/) ব্যবহার করে Dify-কে এক ক্লিকে ডিপ্লয় করুন

View File

@ -133,87 +133,35 @@ LLM ফাংশন কলিং বা ReAct উপর ভিত্তি ক
GitHub-এ ডিফাইকে স্টার দিয়ে রাখুন এবং নতুন রিলিজের খবর তাৎক্ষণিকভাবে পান।
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Advanced Setup
যদি আপনার কনফিগারেশনটি কাস্টমাইজ করার প্রয়োজন হয়, তাহলে অনুগ্রহ করে আমাদের [.env.example](../../docker/.env.example) ফাইল দেখুন এবং আপনার `.env` ফাইলে সংশ্লিষ্ট মানগুলি আপডেট করুন। এছাড়াও, আপনার নির্দিষ্ট এনভায়রনমেন্ট এবং প্রয়োজনীয়তার উপর ভিত্তি করে আপনাকে `docker-compose.yaml` ফাইলে সমন্বয় করতে হতে পারে, যেমন ইমেজ ভার্সন পরিবর্তন করা, পোর্ট ম্যাপিং করা, অথবা ভলিউম মাউন্ট করা।
যেকোনো পরিবর্তন করার পর, অনুগ্রহ করে `docker-compose up -d` পুনরায় চালান। ভেরিয়েবলের সম্পূর্ণ তালিকা [এখানে] (https://docs.dify.ai/getting-started/install-self-hosted/environments) খুঁজে পেতে পারেন।
কাস্টম কনফিগারেশন, পর্যবেক্ষণ এবং ডেপ্লয়মেন্ট বিকল্পের জন্য, [উন্নত সেটআপ](ADVANCED_SETUP.md) দেখুন।
### Grafana দিয়ে মেট্রিক্স মনিটরিং
## অবদান
Dify-এর PostgreSQL ডাটাবেসকে ডেটা সোর্স হিসাবে ব্যবহার করে, অ্যাপ, টেন্যান্ট, মেসেজ ইত্যাদির গ্র্যানুলারিটিতে মেট্রিক্স মনিটর করার জন্য Grafana-তে ড্যাশবোর্ড ইম্পোর্ট করুন।
Dify সব ধরনের অবদানকে স্বাগত জানায়:
- [@bowenliang123 কর্তৃক Grafana ড্যাশবোর্ড](https://github.com/bowenliang123/dify-grafana-dashboard)
- **কোড**: [অবদান নির্দেশিকা](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) পড়ুন, তারপর [নতুন অবদানকারীদের উপযোগী সমস্যাগুলো](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) দেখুন।
- **ধারণা ও প্রতিক্রিয়া**: একটি [GitHub আলোচনা](https://github.com/langgenius/dify/discussions) শুরু করুন বা বিদ্যমান আলোচনায় যোগ দিন।
- **অনুবাদ**: কোনো লোকেল যোগ বা হালনাগাদ করতে [আন্তর্জাতিকীকরণ নির্দেশিকা](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) অনুসরণ করুন।
- **কমিউনিটি**: আপনার তৈরি অ্যাপ শেয়ার করুন, অন্য ব্যবহারকারীদের সাহায্য করুন এবং Dify সম্পর্কে সবাইকে জানান।
### Kubernetes এর সাথে ডেপ্লয়মেন্ট
### অবদানকারীরা
যদি আপনি একটি হাইলি এভেইলেবল সেটআপ কনফিগার করতে চান, তাহলে কমিউনিটি [Helm Charts](https://helm.sh/) এবং YAML ফাইল রয়েছে যা Dify কে Kubernetes-এ ডিপ্লয় করার প্রক্রিয়া বর্ণনা করে।
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 নতুন! YAML ফাইলসমূহ (Dify v1.6.0 সমর্থিত) তৈরি করেছেন @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### টেরাফর্ম ব্যবহার করে ডিপ্লয়
[terraform](https://www.terraform.io/) ব্যবহার করে এক ক্লিকেই ক্লাউড প্ল্যাটফর্মে Dify ডিপ্লয় করুন।
##### অ্যাজুর গ্লোবাল
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### গুগল ক্লাউড
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### AWS CDK ব্যবহার করে ডিপ্লয়
[CDK](https://aws.amazon.com/cdk/) দিয়ে AWS-এ Dify ডিপ্লয় করুন
##### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud ব্যবহার করে ডিপ্লয়
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management ব্যবহার করে ডিপ্লয়
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### AKS-এ ডিপ্লয় করার জন্য Azure Devops Pipeline ব্যবহার
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) ব্যবহার করে Dify কে AKS-এ এক ক্লিকে ডিপ্লয় করুন
#### Sealos ব্যবহার করে ডিপ্লয়
[Sealos App Store](https://sealos.io/products/app-store/dify/) ব্যবহার করে Dify-কে এক ক্লিকে ডিপ্লয় করুন
## Contributing
যারা কোড অবদান রাখতে চান, তাদের জন্য আমাদের [অবদান নির্দেশিকা](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) দেখুন।
একই সাথে, সোশ্যাল মিডিয়া এবং ইভেন্ট এবং কনফারেন্সে এটি শেয়ার করে Dify কে সমর্থন করুন।
> আমরা ম্যান্ডারিন বা ইংরেজি ছাড়া অন্য ভাষায় Dify অনুবাদ করতে সাহায্য করার জন্য অবদানকারীদের খুঁজছি। আপনি যদি সাহায্য করতে আগ্রহী হন, তাহলে আরও তথ্যের জন্য [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) দেখুন এবং আমাদের [ডিসকর্ড কমিউনিটি সার্ভার](https://discord.gg/8Tpq4AcN9c) এর `গ্লোবাল-ইউজারস` চ্যানেলে আমাদের একটি মন্তব্য করুন।
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## কমিউনিটি এবং যোগাযোগ
- [GitHub Discussion](https://github.com/langgenius/dify/discussions) ফিডব্যাক এবং প্রতিক্রিয়া জানানোর মাধ্যম।
- [GitHub Issues](https://github.com/langgenius/dify/issues). Dify.AI ব্যবহার করে আপনি যেসব বাগের সম্মুখীন হন এবং ফিচার প্রস্তাবনা। আমাদের [অবদান নির্দেশিকা](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) দেখুন।
- [Discord](https://discord.gg/FngNHpbcY7) আপনার এপ্লিকেশন শেয়ার এবং কমিউনিটি আড্ডার মাধ্যম।
- [X(Twitter)](https://twitter.com/dify_ai) আপনার এপ্লিকেশন শেয়ার এবং কমিউনিটি আড্ডার মাধ্যম।
**অবদানকারীদের তালিকা**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
আপনার প্রশ্নের জন্য সবচেয়ে উপযুক্ত মাধ্যমটি বেছে নিন:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): সাহায্য নিন, প্রতিক্রিয়া জানান এবং ধারণা প্রস্তাব করুন।
- [GitHub Issues](https://github.com/langgenius/dify/issues): পুনরুৎপাদনযোগ্য বাগ রিপোর্ট করুন এবং ইঞ্জিনিয়ারিং কাজ অনুসরণ করুন। নতুন ইস্যু খোলার আগে [অবদান নির্দেশিকা](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) পড়ুন।
- [Discord](https://discord.gg/FngNHpbcY7): সরাসরি আলোচনা করুন, আপনার অ্যাপ শেয়ার করুন এবং অন্যান্য Dify ব্যবহারকারীর সঙ্গে যুক্ত হন।
- [X](https://x.com/dify_ai): রিলিজের খবর এবং প্রকল্পের হালনাগাদ পেতে Dify অনুসরণ করুন।
## স্টার হিস্ট্রি
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,57 @@
# Erweiterte Einstellungen
Falls Sie die Konfiguration anpassen müssen, lesen Sie bitte die Kommentare in unserer [.env.example](../../docker/.env.example)-Datei und aktualisieren Sie die entsprechenden Werte in Ihrer `.env`-Datei. Zusätzlich müssen Sie eventuell Anpassungen an der `docker-compose.yaml`-Datei vornehmen, wie zum Beispiel das Ändern von Image-Versionen, Portzuordnungen oder Volumen-Mounts, je nach Ihrer spezifischen Einsatzumgebung und Ihren Anforderungen. Nachdem Sie Änderungen vorgenommen haben, starten Sie `docker-compose up -d` erneut. Eine vollständige Liste der verfügbaren Umgebungsvariablen finden Sie [hier](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## Metriküberwachung mit Grafana
Importieren Sie das Dashboard in Grafana, wobei Sie die PostgreSQL-Datenbank von Dify als Datenquelle verwenden, um Metriken in der Granularität von Apps, Mandanten, Nachrichten und mehr zu überwachen.
- [Grafana-Dashboard von @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Bereitstellung mit Kubernetes
Falls Sie eine hochverfügbare Konfiguration einrichten möchten, gibt es von der Community bereitgestellte [Helm Charts](https://helm.sh/) und YAML-Dateien, die es ermöglichen, Dify auf Kubernetes bereitzustellen.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Terraform für die Bereitstellung verwenden
Stellen Sie Dify mit nur einem Klick mithilfe von [terraform](https://www.terraform.io/) auf einer Cloud-Plattform bereit.
#### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Verwendung von AWS CDK für die Bereitstellung
Bereitstellung von Dify auf AWS mit [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
Ein-Klick-Bereitstellung von Dify in der Alibaba Cloud mit [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Verwendung von Azure Devops Pipeline für AKS-Bereitstellung
Stellen Sie Dify mit einem Klick in AKS bereit, indem Sie [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) verwenden
### Bereitstellung mit Sealos
Stellen Sie Dify mit einem Klick über den [Sealos App Store](https://sealos.io/products/app-store/dify/) bereit

View File

@ -132,85 +132,35 @@ Alle Dify-Angebote kommen mit entsprechenden APIs, sodass Sie Dify mühelos in I
Star Dify auf GitHub und lassen Sie sich sofort über neue Releases benachrichtigen.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Erweiterte Einstellungen
Falls Sie die Konfiguration anpassen müssen, lesen Sie bitte die Kommentare in unserer [.env.example](../../docker/.env.example)-Datei und aktualisieren Sie die entsprechenden Werte in Ihrer `.env`-Datei. Zusätzlich müssen Sie eventuell Anpassungen an der `docker-compose.yaml`-Datei vornehmen, wie zum Beispiel das Ändern von Image-Versionen, Portzuordnungen oder Volumen-Mounts, je nach Ihrer spezifischen Einsatzumgebung und Ihren Anforderungen. Nachdem Sie Änderungen vorgenommen haben, starten Sie `docker-compose up -d` erneut. Eine vollständige Liste der verfügbaren Umgebungsvariablen finden Sie [hier](https://docs.dify.ai/getting-started/install-self-hosted/environments).
Optionen für benutzerdefinierte Konfiguration, Observability und Bereitstellung finden Sie unter [Erweiterte Einstellungen](ADVANCED_SETUP.md).
### Metriküberwachung mit Grafana
## Mitwirken
Importieren Sie das Dashboard in Grafana, wobei Sie die PostgreSQL-Datenbank von Dify als Datenquelle verwenden, um Metriken in der Granularität von Apps, Mandanten, Nachrichten und mehr zu überwachen.
Dify freut sich über Beiträge jeder Art:
- [Grafana-Dashboard von @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
- **Code**: Lesen Sie den [Contribution Guide](./CONTRIBUTING.md) und stöbern Sie anschließend in den [Good First Issues](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ideen und Feedback**: Starten Sie eine [GitHub-Diskussion](https://github.com/langgenius/dify/discussions) oder beteiligen Sie sich an einer bestehenden.
- **Übersetzungen**: Folgen Sie dem [Internationalisierungsleitfaden](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md), um eine Sprache hinzuzufügen oder zu aktualisieren.
- **Community**: Teilen Sie Ihre Apps, helfen Sie anderen Nutzern und machen Sie Dify bekannter.
### Bereitstellung mit Kubernetes
### Mitwirkende
Falls Sie eine hochverfügbare Konfiguration einrichten möchten, gibt es von der Community bereitgestellte [Helm Charts](https://helm.sh/) und YAML-Dateien, die es ermöglichen, Dify auf Kubernetes bereitzustellen.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Terraform für die Bereitstellung verwenden
Stellen Sie Dify mit nur einem Klick mithilfe von [terraform](https://www.terraform.io/) auf einer Cloud-Plattform bereit.
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Verwendung von AWS CDK für die Bereitstellung
Bereitstellung von Dify auf AWS mit [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
Ein-Klick-Bereitstellung von Dify in der Alibaba Cloud mit [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Verwendung von Azure Devops Pipeline für AKS-Bereitstellung
Stellen Sie Dify mit einem Klick in AKS bereit, indem Sie [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) verwenden
#### Bereitstellung mit Sealos
Stellen Sie Dify mit einem Klick über den [Sealos App Store](https://sealos.io/products/app-store/dify/) bereit
## Contributing
Falls Sie Code beitragen möchten, lesen Sie bitte unseren [Contribution Guide](./CONTRIBUTING.md). Gleichzeitig bitten wir Sie, Dify zu unterstützen, indem Sie es in den sozialen Medien teilen und auf Veranstaltungen und Konferenzen präsentieren.
> Wir suchen Mitwirkende, die dabei helfen, Dify in weitere Sprachen zu übersetzen außer Mandarin oder Englisch. Wenn Sie Interesse an einer Mitarbeit haben, lesen Sie bitte die [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) für weitere Informationen und hinterlassen Sie einen Kommentar im `global-users`-Kanal unseres [Discord Community Servers](https://discord.gg/8Tpq4AcN9c).
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Gemeinschaft & Kontakt
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Am besten geeignet für: den Austausch von Feedback und das Stellen von Fragen.
- [GitHub Issues](https://github.com/langgenius/dify/issues). Am besten für: Fehler, auf die Sie bei der Verwendung von Dify.AI stoßen, und Funktionsvorschläge. Siehe unseren [Contribution Guide](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Am besten geeignet für: den Austausch von Bewerbungen und den Austausch mit der Community.
- [X(Twitter)](https://twitter.com/dify_ai). Am besten geeignet für: den Austausch von Bewerbungen und den Austausch mit der Community.
**Mitwirkende**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
Wählen Sie den Kanal, der am besten zu Ihrem Anliegen passt:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): Hilfe erhalten, Feedback teilen und Ideen vorschlagen.
- [GitHub Issues](https://github.com/langgenius/dify/issues): Reproduzierbare Fehler melden und technische Arbeiten verfolgen. Lesen Sie vor dem Erstellen eines Issues den [Contribution Guide](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7): In Echtzeit chatten, Apps teilen und sich mit anderen Dify-Nutzern austauschen.
- [X](https://x.com/dify_ai): Neuigkeiten zu Releases und Projekt-Updates verfolgen.
## Star-Geschichte
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,59 @@
# Configuración avanzada
Si necesita personalizar la configuración, consulte los comentarios en nuestro archivo [.env.example](../../docker/.env.example) y actualice los valores correspondientes en su archivo `.env`. Además, es posible que deba realizar ajustes en el propio archivo `docker-compose.yaml`, como cambiar las versiones de las imágenes, las asignaciones de puertos o los montajes de volúmenes, según su entorno de implementación y requisitos específicos. Después de realizar cualquier cambio, vuelva a ejecutar `docker-compose up -d`. Puede encontrar la lista completa de variables de entorno disponibles [aquí](https://docs.dify.ai/getting-started/install-self-hosted/environments).
. Después de realizar los cambios, ejecuta `docker-compose up -d` nuevamente. Puedes ver la lista completa de variables de entorno [aquí](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## Monitorización de Métricas con Grafana
Importe el panel a Grafana, utilizando la base de datos PostgreSQL de Dify como fuente de datos, para monitorizar métricas en granularidad de aplicaciones, inquilinos, mensajes y más.
- [Panel de Grafana por @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Implementación con Kubernetes
Si desea configurar una configuración de alta disponibilidad, la comunidad proporciona [Gráficos Helm](https://helm.sh/) y archivos YAML, a través de los cuales puede desplegar Dify en Kubernetes.
- [Gráfico Helm por @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Gráfico Helm por @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Gráfico Helm por @magicsong](https://github.com/magicsong/ai-charts)
- [Ficheros YAML por @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Ficheros YAML por @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 ¡NUEVO! Archivos YAML (compatible con Dify v1.6.0) por @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Uso de Terraform para el despliegue
Despliega Dify en una plataforma en la nube con un solo clic utilizando [terraform](https://www.terraform.io/)
#### Azure Global
- [Azure Terraform por @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform por @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Usando AWS CDK para el Despliegue
Despliegue Dify en AWS usando [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK por @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK por @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
Despliega Dify en Alibaba Cloud con un solo clic con [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Uso de Azure Devops Pipeline para implementar en AKS
Implementa Dify en AKS con un clic usando [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### Implementación con Sealos
Despliega Dify con un solo clic desde [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -107,7 +107,7 @@ Todas las ofertas de Dify vienen con APIs correspondientes, por lo que podrías
Dale estrella a Dify en GitHub y serás notificado instantáneamente de las nuevas versiones.
![danos estrella](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Inicio Rápido
@ -132,84 +132,31 @@ Después de ejecutarlo, puedes acceder al panel de control de Dify en tu navegad
## Próximos pasos
Si necesita personalizar la configuración, consulte los comentarios en nuestro archivo [.env.example](../../docker/.env.example) y actualice los valores correspondientes en su archivo `.env`. Además, es posible que deba realizar ajustes en el propio archivo `docker-compose.yaml`, como cambiar las versiones de las imágenes, las asignaciones de puertos o los montajes de volúmenes, según su entorno de implementación y requisitos específicos. Después de realizar cualquier cambio, vuelva a ejecutar `docker-compose up -d`. Puede encontrar la lista completa de variables de entorno disponibles [aquí](https://docs.dify.ai/getting-started/install-self-hosted/environments).
. Después de realizar los cambios, ejecuta `docker-compose up -d` nuevamente. Puedes ver la lista completa de variables de entorno [aquí](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### Monitorización de Métricas con Grafana
Importe el panel a Grafana, utilizando la base de datos PostgreSQL de Dify como fuente de datos, para monitorizar métricas en granularidad de aplicaciones, inquilinos, mensajes y más.
- [Panel de Grafana por @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Implementación con Kubernetes
Si desea configurar una configuración de alta disponibilidad, la comunidad proporciona [Gráficos Helm](https://helm.sh/) y archivos YAML, a través de los cuales puede desplegar Dify en Kubernetes.
- [Gráfico Helm por @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Gráfico Helm por @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Gráfico Helm por @magicsong](https://github.com/magicsong/ai-charts)
- [Ficheros YAML por @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Ficheros YAML por @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 ¡NUEVO! Archivos YAML (compatible con Dify v1.6.0) por @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Uso de Terraform para el despliegue
Despliega Dify en una plataforma en la nube con un solo clic utilizando [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform por @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform por @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Usando AWS CDK para el Despliegue
Despliegue Dify en AWS usando [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK por @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK por @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
Despliega Dify en Alibaba Cloud con un solo clic con [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Uso de Azure Devops Pipeline para implementar en AKS
Implementa Dify en AKS con un clic usando [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### Implementación con Sealos
Despliega Dify con un solo clic desde [Sealos App Store](https://sealos.io/products/app-store/dify/)
Para opciones de configuración personalizada, observabilidad y despliegue, consulta la [Configuración avanzada](ADVANCED_SETUP.md).
## Contribuir
Para aquellos que deseen contribuir con código, consulten nuestra [Guía de contribución](./CONTRIBUTING.md).
Al mismo tiempo, considera apoyar a Dify compartiéndolo en redes sociales y en eventos y conferencias.
Dify acepta contribuciones de todo tipo:
> Estamos buscando colaboradores para ayudar con la traducción de Dify a idiomas que no sean el mandarín o el inglés. Si estás interesado en ayudar, consulta el [README de i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) para obtener más información y déjanos un comentario en el canal `global-users` de nuestro [Servidor de Comunidad en Discord](https://discord.gg/8Tpq4AcN9c).
- **Código**: Lee la [Guía de contribución](./CONTRIBUTING.md) y consulta las [incidencias para principiantes](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ideas y comentarios**: Inicia o únete a una [discusión en GitHub](https://github.com/langgenius/dify/discussions).
- **Traducciones**: Sigue la [guía de internacionalización](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) para añadir o actualizar un idioma.
- **Comunidad**: Comparte las aplicaciones que crees, ayuda a otros usuarios y da a conocer Dify.
**Contribuidores**
### Contribuidores
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Comunidad y Contacto
## Comunidad y contacto
- [Discusión en GitHub](https://github.com/langgenius/dify/discussions). Lo mejor para: compartir comentarios y hacer preguntas.
- [Reporte de problemas en GitHub](https://github.com/langgenius/dify/issues). Lo mejor para: errores que encuentres usando Dify.AI y propuestas de características. Consulta nuestra [Guía de contribución](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Lo mejor para: compartir tus aplicaciones y pasar el rato con la comunidad.
- [X(Twitter)](https://twitter.com/dify_ai). Lo mejor para: compartir tus aplicaciones y pasar el rato con la comunidad.
Elige el canal que mejor se adapte a tu consulta:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): Obtén ayuda, comparte comentarios y propón ideas.
- [GitHub Issues](https://github.com/langgenius/dify/issues): Informa de errores reproducibles y sigue el trabajo de ingeniería. Lee la [Guía de contribución](./CONTRIBUTING.md) antes de abrir una incidencia.
- [Discord](https://discord.gg/FngNHpbcY7): Chatea en tiempo real, comparte tus aplicaciones y conecta con otros usuarios de Dify.
- [X](https://x.com/dify_ai): Sigue las noticias de versiones y las actualizaciones del proyecto.
## Historial de Estrellas
[![Gráfico de Historial de Estrellas](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,57 @@
# Configuration avancée
Si vous devez personnaliser la configuration, veuillez vous référer aux commentaires dans notre fichier [.env.example](../../docker/.env.example) et mettre à jour les valeurs correspondantes dans votre fichier `.env`. De plus, vous devrez peut-être apporter des modifications au fichier `docker-compose.yaml` lui-même, comme changer les versions d'image, les mappages de ports ou les montages de volumes, en fonction de votre environnement de déploiement et de vos exigences spécifiques. Après avoir effectué des modifications, veuillez réexécuter `docker-compose up -d`. Vous pouvez trouver la liste complète des variables d'environnement disponibles [ici](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## Surveillance des Métriques avec Grafana
Importez le tableau de bord dans Grafana, en utilisant la base de données PostgreSQL de Dify comme source de données, pour surveiller les métriques avec une granularité d'applications, de locataires, de messages et plus.
- [Tableau de bord Grafana par @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Déploiement avec Kubernetes
Si vous souhaitez configurer une configuration haute disponibilité, la communauté fournit des [Helm Charts](https://helm.sh/) et des fichiers YAML, à travers lesquels vous pouvez déployer Dify sur Kubernetes.
- [Helm Chart par @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart par @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart par @magicsong](https://github.com/magicsong/ai-charts)
- [Fichier YAML par @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Fichier YAML par @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NOUVEAU ! Fichiers YAML (compatible avec Dify v1.6.0) par @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Utilisation de Terraform pour le déploiement
Déployez Dify sur une plateforme cloud en un clic en utilisant [terraform](https://www.terraform.io/)
#### Azure Global
- [Azure Terraform par @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform par @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Utilisation d'AWS CDK pour le déploiement
Déployez Dify sur AWS en utilisant [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK par @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK par @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
Déployez Dify en un clic sur Alibaba Cloud avec [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Utilisation d'Azure Devops Pipeline pour déployer sur AKS
Déployez Dify sur AKS en un clic en utilisant [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### Déploiement avec Sealos
Déployez Dify en un clic depuis le [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -107,7 +107,7 @@ Toutes les offres de Dify sont accompagnées d'API correspondantes, vous permett
Mettez une étoile à Dify sur GitHub et soyez instantanément informé des nouvelles versions.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Démarrage rapide
@ -132,82 +132,31 @@ Après l'exécution, vous pouvez accéder au tableau de bord Dify dans votre nav
## Prochaines étapes
Si vous devez personnaliser la configuration, veuillez vous référer aux commentaires dans notre fichier [.env.example](../../docker/.env.example) et mettre à jour les valeurs correspondantes dans votre fichier `.env`. De plus, vous devrez peut-être apporter des modifications au fichier `docker-compose.yaml` lui-même, comme changer les versions d'image, les mappages de ports ou les montages de volumes, en fonction de votre environnement de déploiement et de vos exigences spécifiques. Après avoir effectué des modifications, veuillez réexécuter `docker-compose up -d`. Vous pouvez trouver la liste complète des variables d'environnement disponibles [ici](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### Surveillance des Métriques avec Grafana
Importez le tableau de bord dans Grafana, en utilisant la base de données PostgreSQL de Dify comme source de données, pour surveiller les métriques avec une granularité d'applications, de locataires, de messages et plus.
- [Tableau de bord Grafana par @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Déploiement avec Kubernetes
Si vous souhaitez configurer une configuration haute disponibilité, la communauté fournit des [Helm Charts](https://helm.sh/) et des fichiers YAML, à travers lesquels vous pouvez déployer Dify sur Kubernetes.
- [Helm Chart par @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart par @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart par @magicsong](https://github.com/magicsong/ai-charts)
- [Fichier YAML par @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Fichier YAML par @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NOUVEAU ! Fichiers YAML (compatible avec Dify v1.6.0) par @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Utilisation de Terraform pour le déploiement
Déployez Dify sur une plateforme cloud en un clic en utilisant [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform par @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform par @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Utilisation d'AWS CDK pour le déploiement
Déployez Dify sur AWS en utilisant [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK par @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK par @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
Déployez Dify en un clic sur Alibaba Cloud avec [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Utilisation d'Azure Devops Pipeline pour déployer sur AKS
Déployez Dify sur AKS en un clic en utilisant [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### Déploiement avec Sealos
Déployez Dify en un clic depuis le [Sealos App Store](https://sealos.io/products/app-store/dify/)
Pour les options de configuration personnalisée, dobservabilité et de déploiement, consultez la page [Configuration avancée](ADVANCED_SETUP.md).
## Contribuer
Pour ceux qui souhaitent contribuer du code, consultez notre [Guide de contribution](./CONTRIBUTING.md).
Dans le même temps, veuillez envisager de soutenir Dify en le partageant sur les réseaux sociaux et lors d'événements et de conférences.
Dify accueille les contributions de toutes sortes :
> Nous recherchons des contributeurs pour aider à traduire Dify dans des langues autres que le mandarin ou l'anglais. Si vous êtes intéressé à aider, veuillez consulter le [README i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) pour plus d'informations, et laissez-nous un commentaire dans le canal `global-users` de notre [Serveur communautaire Discord](https://discord.gg/8Tpq4AcN9c).
- **Code** : consultez le [guide de contribution](./CONTRIBUTING.md), puis parcourez les [issues accessibles aux nouveaux contributeurs](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Idées et retours** : lancez ou rejoignez une [discussion GitHub](https://github.com/langgenius/dify/discussions).
- **Traductions** : suivez le [guide dinternationalisation](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) pour ajouter ou mettre à jour une langue.
- **Communauté** : partagez les applications que vous créez, aidez les autres utilisateurs et faites connaître Dify.
**Contributeurs**
### Contributeurs
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Communauté & Contact
## Communauté et contact
- [Discussion GitHub](https://github.com/langgenius/dify/discussions). Meilleur pour: partager des commentaires et poser des questions.
- [Problèmes GitHub](https://github.com/langgenius/dify/issues). Meilleur pour: les bogues que vous rencontrez en utilisant Dify.AI et les propositions de fonctionnalités. Consultez notre [Guide de contribution](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Meilleur pour: partager vos applications et passer du temps avec la communauté.
- [X(Twitter)](https://twitter.com/dify_ai). Meilleur pour: partager vos applications et passer du temps avec la communauté.
Choisissez le canal le plus adapté à votre demande :
- [GitHub Discussions](https://github.com/langgenius/dify/discussions) : obtenez de laide, partagez vos retours et proposez des idées.
- [GitHub Issues](https://github.com/langgenius/dify/issues) : signalez des bogues reproductibles et suivez les travaux dingénierie. Consultez le [guide de contribution](./CONTRIBUTING.md) avant douvrir une issue.
- [Discord](https://discord.gg/FngNHpbcY7) : échangez en temps réel, partagez vos applications et rencontrez dautres utilisateurs de Dify.
- [X](https://x.com/dify_ai) : suivez les nouvelles versions et les actualités du projet.
## Historique des étoiles
[![Graphique de l'historique des étoiles](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,62 @@
# उन्नत सेटअप
## कस्टम कॉन्फ़िगरेशन
यदि आपको कॉन्फ़िगरेशन को कस्टमाइज़ करने की आवश्यकता है, तो कृपया हमारी [.env.example](../../docker/.env.example) फ़ाइल में दिए गए टिप्पणियों (comments) को देखें और अपने `.env` फ़ाइल में संबंधित मानों को अपडेट करें।\
इसके अतिरिक्त, आपको अपने विशेष डिप्लॉयमेंट वातावरण और आवश्यकताओं के आधार पर `docker-compose.yaml` फ़ाइल में भी बदलाव करने की आवश्यकता हो सकती है, जैसे इमेज संस्करण, पोर्ट मैपिंग या वॉल्यूम माउंट्स बदलना।\
कोई भी बदलाव करने के बाद, कृपया `docker-compose up -d` कमांड को पुनः चलाएँ।\
उपलब्ध सभी environment variables की पूरी सूची [here](https://docs.dify.ai/getting-started/install-self-hosted/environments) पर पाई जा सकती है।
## Grafana के साथ मेट्रिक्स मॉनिटरिंग
Grafana में Dify के PostgreSQL डेटाबेस को डेटा स्रोत के रूप में उपयोग करते हुए डैशबोर्ड आयात करें, ताकि आप ऐप्स, टेनेंट्स, संदेशों आदि के स्तर पर मेट्रिक्स की निगरानी कर सकें।
- [Grafana Dashboard by @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Kubernetes के साथ डिप्लॉयमेंट
यदि आप उच्च उपलब्धता (high-availability) सेटअप कॉन्फ़िगर करना चाहते हैं, तो समुदाय द्वारा योगदान किए गए [Helm Charts](https://helm.sh/) और YAML फ़ाइलें उपलब्ध हैं जो Dify को Kubernetes पर डिप्लॉय करने की अनुमति देती हैं।
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### डिप्लॉयमेंट के लिए Terraform का उपयोग
[terraform](https://www.terraform.io/) का उपयोग करके एक क्लिक में Dify को क्लाउड प्लेटफ़ॉर्म पर डिप्लॉय करें।
#### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### डिप्लॉयमेंट के लिए AWS CDK का उपयोग
[CDK](https://aws.amazon.com/cdk/) का उपयोग करके Dify को AWS पर डिप्लॉय करें।
#### AWS
- [AWS CDK by @KevinZhao (EKS आधारित)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS आधारित)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud Computing Nest का उपयोग
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88) के साथ Dify को Alibaba Cloud पर तेज़ी से डिप्लॉय करें।
### Alibaba Cloud Data Management का उपयोग
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/) के साथ एक क्लिक में Dify को Alibaba Cloud पर डिप्लॉय करें।
### Azure Devops Pipeline के साथ AKS पर डिप्लॉय करें
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) के साथ एक क्लिक में Dify को AKS पर डिप्लॉय करें।
### Sealos के साथ डिप्लॉय करें
[Sealos App Store](https://sealos.io/products/app-store/dify/) के साथ एक क्लिक में Dify को डिप्लॉय करें।

View File

@ -134,92 +134,35 @@ Dify की सभी सेवाएँ संबंधित APIs के स
GitHub पर Dify को स्टार करें और नए रिलीज़ की सूचना तुरंत प्राप्त करें।
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## उन्नत सेटअप
### कस्टम कॉन्फ़िगरेशन
यदि आपको कॉन्फ़िगरेशन को कस्टमाइज़ करने की आवश्यकता है, तो कृपया हमारी [.env.example](../../docker/.env.example) फ़ाइल में दिए गए टिप्पणियों (comments) को देखें और अपने `.env` फ़ाइल में संबंधित मानों को अपडेट करें।\
इसके अतिरिक्त, आपको अपने विशेष डिप्लॉयमेंट वातावरण और आवश्यकताओं के आधार पर `docker-compose.yaml` फ़ाइल में भी बदलाव करने की आवश्यकता हो सकती है, जैसे इमेज संस्करण, पोर्ट मैपिंग या वॉल्यूम माउंट्स बदलना।\
कोई भी बदलाव करने के बाद, कृपया `docker-compose up -d` कमांड को पुनः चलाएँ।\
उपलब्ध सभी environment variables की पूरी सूची [here](https://docs.dify.ai/getting-started/install-self-hosted/environments) पर पाई जा सकती है।
### Grafana के साथ मेट्रिक्स मॉनिटरिंग
Grafana में Dify के PostgreSQL डेटाबेस को डेटा स्रोत के रूप में उपयोग करते हुए डैशबोर्ड आयात करें, ताकि आप ऐप्स, टेनेंट्स, संदेशों आदि के स्तर पर मेट्रिक्स की निगरानी कर सकें।
- [Grafana Dashboard by @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Kubernetes के साथ डिप्लॉयमेंट
यदि आप उच्च उपलब्धता (high-availability) सेटअप कॉन्फ़िगर करना चाहते हैं, तो समुदाय द्वारा योगदान किए गए [Helm Charts](https://helm.sh/) और YAML फ़ाइलें उपलब्ध हैं जो Dify को Kubernetes पर डिप्लॉय करने की अनुमति देती हैं।
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### डिप्लॉयमेंट के लिए Terraform का उपयोग
[terraform](https://www.terraform.io/) का उपयोग करके एक क्लिक में Dify को क्लाउड प्लेटफ़ॉर्म पर डिप्लॉय करें।
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### डिप्लॉयमेंट के लिए AWS CDK का उपयोग
[CDK](https://aws.amazon.com/cdk/) का उपयोग करके Dify को AWS पर डिप्लॉय करें।
##### AWS
- [AWS CDK by @KevinZhao (EKS आधारित)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS आधारित)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud Computing Nest का उपयोग
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88) के साथ Dify को Alibaba Cloud पर तेज़ी से डिप्लॉय करें।
#### Alibaba Cloud Data Management का उपयोग
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/) के साथ एक क्लिक में Dify को Alibaba Cloud पर डिप्लॉय करें।
#### Azure Devops Pipeline के साथ AKS पर डिप्लॉय करें
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) के साथ एक क्लिक में Dify को AKS पर डिप्लॉय करें।
#### Sealos के साथ डिप्लॉय करें
[Sealos App Store](https://sealos.io/products/app-store/dify/) के साथ एक क्लिक में Dify को डिप्लॉय करें।
कस्टम कॉन्फ़िगरेशन, ऑब्ज़र्वेबिलिटी और डिप्लॉयमेंट विकल्पों के लिए, [उन्नत सेटअप](ADVANCED_SETUP.md) देखें।
## योगदान (Contributing)
जो लोग कोड में योगदान देना चाहते हैं, वे हमारे [Contribution Guide](./CONTRIBUTING.md) को देखें।\
साथ ही, कृपया Dify को सोशल मीडिया, कार्यक्रमों और सम्मेलनों में साझा करके इसका समर्थन करने पर विचार करें।
Dify सभी प्रकार के योगदानों का स्वागत करता है:
> हम ऐसे योगदानकर्ताओं की तलाश कर रहे हैं जो Dify को मंदारिन या अंग्रेज़ी के अलावा अन्य भाषाओं में अनुवाद करने में मदद कर सकें।\
> यदि आप सहायता करने में रुचि रखते हैं, तो अधिक जानकारी के लिए [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) देखें, और हमारे [Discord Community Server](https://discord.gg/8Tpq4AcN9c) के `global-users` चैनल में हमें संदेश दें।
- **कोड**: [Contribution Guide](./CONTRIBUTING.md) पढ़ें, फिर [नए योगदानकर्ताओं के लिए उपयुक्त issues](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) देखें।
- **विचार और प्रतिक्रिया**: [GitHub Discussion](https://github.com/langgenius/dify/discussions) शुरू करें या किसी मौजूदा चर्चा में शामिल हों।
- **अनुवाद**: किसी locale को जोड़ने या अपडेट करने के लिए [internationalization guide](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) का पालन करें।
- **समुदाय**: अपने बनाए हुए ऐप साझा करें, अन्य उपयोगकर्ताओं की मदद करें और Dify के बारे में लोगों को बताएं।
### योगदानकर्ता
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## समुदाय और संपर्क (Community & contact)
- [GitHub Discussion](https://github.com/langgenius/dify/discussions) — सर्वोत्तम उपयोग के लिए: प्रतिक्रिया साझा करने और प्रश्न पूछने हेतु।
- [GitHub Issues](https://github.com/langgenius/dify/issues) — सर्वोत्तम उपयोग के लिए: Dify.AI का उपयोग करते समय आने वाली बग्स या फीचर सुझावों के लिए। देखें: [Contribution Guide](../../CONTRIBUTING.md)।
- [Discord](https://discord.gg/FngNHpbcY7) — सर्वोत्तम उपयोग के लिए: अपने एप्लिकेशन साझा करने और समुदाय के साथ जुड़ने के लिए।
- [X(Twitter)](https://twitter.com/dify_ai) — सर्वोत्तम उपयोग के लिए: अपने एप्लिकेशन साझा करने और समुदाय से जुड़े रहने के लिए।
**योगदानकर्ता**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
अपने प्रश्न के लिए सबसे उपयुक्त माध्यम चुनें:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): सहायता प्राप्त करें, प्रतिक्रिया साझा करें और विचार सुझाएँ।
- [GitHub Issues](https://github.com/langgenius/dify/issues): दोहराए जा सकने वाले bugs की रिपोर्ट करें और engineering work को ट्रैक करें। Issue खोलने से पहले [Contribution Guide](./CONTRIBUTING.md) पढ़ें।
- [Discord](https://discord.gg/FngNHpbcY7): रीयल टाइम में बातचीत करें, अपने ऐप साझा करें और अन्य Dify उपयोगकर्ताओं से जुड़ें।
- [X](https://x.com/dify_ai): रिलीज़ समाचार और प्रोजेक्ट अपडेट के लिए Dify को फ़ॉलो करें।
## स्टार इतिहास (Star history)
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,57 @@
# Configurazioni Avanzate
Se dovete personalizzare la configurazione, leggete i commenti nel nostro file [.env.example](../../docker/.env.example) e aggiornate i valori corrispondenti nel vostro file `.env`. Inoltre, potrebbe essere necessario apportare modifiche al file `docker-compose.yaml`, come cambiare le versioni delle immagini, le mappature delle porte o i mount dei volumi, a seconda del vostro ambiente di distribuzione specifico e dei vostri requisiti. Dopo aver apportato le modifiche, riavviate `docker-compose up -d`. L'elenco completo delle variabili d'ambiente disponibili è disponibile [qui](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## Monitoraggio delle Metriche con Grafana
Importate la dashboard in Grafana, utilizzando il database PostgreSQL di Dify come origine dati, per monitorare le metriche a livello di app, tenant, messaggi e altro ancora.
- [Dashboard Grafana di @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Distribuzione con Kubernetes
Se desiderate configurare un'installazione ad alta disponibilità, ci sono [Helm Charts](https://helm.sh/) e file YAML forniti dalla community che consentono di distribuire Dify su Kubernetes.
- [Helm Chart di @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart di @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart di @magicsong](https://github.com/magicsong/ai-charts)
- [File YAML di @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [File YAML di @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NUOVO! File YAML (Supporta Dify v1.6.0) di @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Utilizzo di Terraform per la Distribuzione
Distribuite Dify con un solo clic su una piattaforma cloud utilizzando [terraform](https://www.terraform.io/).
#### Azure Global
- [Azure Terraform di @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform di @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Utilizzo di AWS CDK per la Distribuzione
Distribuzione di Dify su AWS con [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK di @KevinZhao (basato su EKS)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK di @tmokmss (basato su ECS)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
Distribuzione con un clic di Dify su Alibaba Cloud con [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Utilizzo di Azure DevOps Pipeline per la Distribuzione su AKS
Distribuite Dify con un clic in AKS utilizzando [Azure DevOps Pipeline Helm Chart di @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### Distribuzione con Sealos
Distribuite Dify con un clic tramite [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -132,85 +132,35 @@ Tutte le offerte di Dify sono dotate di API corrispondenti, permettendovi di int
Mettete una stella a Dify su GitHub e ricevete notifiche immediate sui nuovi rilasci.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Configurazioni Avanzate
Se dovete personalizzare la configurazione, leggete i commenti nel nostro file [.env.example](../../docker/.env.example) e aggiornate i valori corrispondenti nel vostro file `.env`. Inoltre, potrebbe essere necessario apportare modifiche al file `docker-compose.yaml`, come cambiare le versioni delle immagini, le mappature delle porte o i mount dei volumi, a seconda del vostro ambiente di distribuzione specifico e dei vostri requisiti. Dopo aver apportato le modifiche, riavviate `docker-compose up -d`. L'elenco completo delle variabili d'ambiente disponibili è disponibile [qui](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### Monitoraggio delle Metriche con Grafana
Importate la dashboard in Grafana, utilizzando il database PostgreSQL di Dify come origine dati, per monitorare le metriche a livello di app, tenant, messaggi e altro ancora.
- [Dashboard Grafana di @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Distribuzione con Kubernetes
Se desiderate configurare un'installazione ad alta disponibilità, ci sono [Helm Charts](https://helm.sh/) e file YAML forniti dalla community che consentono di distribuire Dify su Kubernetes.
- [Helm Chart di @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart di @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart di @magicsong](https://github.com/magicsong/ai-charts)
- [File YAML di @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [File YAML di @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NUOVO! File YAML (Supporta Dify v1.6.0) di @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Utilizzo di Terraform per la Distribuzione
Distribuite Dify con un solo clic su una piattaforma cloud utilizzando [terraform](https://www.terraform.io/).
##### Azure Global
- [Azure Terraform di @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform di @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Utilizzo di AWS CDK per la Distribuzione
Distribuzione di Dify su AWS con [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK di @KevinZhao (basato su EKS)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK di @tmokmss (basato su ECS)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
Distribuzione con un clic di Dify su Alibaba Cloud con [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Utilizzo di Azure DevOps Pipeline per la Distribuzione su AKS
Distribuite Dify con un clic in AKS utilizzando [Azure DevOps Pipeline Helm Chart di @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### Distribuzione con Sealos
Distribuite Dify con un clic tramite [Sealos App Store](https://sealos.io/products/app-store/dify/)
Per le opzioni di configurazione personalizzata, osservabilità e distribuzione, consulta le [Configurazioni avanzate](ADVANCED_SETUP.md).
## Contribuire
Se desiderate contribuire con codice, leggete la nostra [Guida ai Contributi](../../CONTRIBUTING.md). Allo stesso tempo, vi chiediamo di supportare Dify condividendolo sui social media e presentandolo a eventi e conferenze.
Dify accoglie contributi di ogni tipo:
> Cerchiamo collaboratori che aiutino a tradurre Dify in altre lingue oltre al mandarino o all'inglese. Se siete interessati a collaborare, leggete il [README i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) per ulteriori informazioni e lasciate un commento nel canale `global-users` del nostro [server della community Discord](https://discord.gg/8Tpq4AcN9c).
- **Codice**: leggi la [Guida ai contributi](../../CONTRIBUTING.md), quindi consulta le [issue adatte ai nuovi contributori](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Idee e feedback**: avvia o partecipa a una [discussione su GitHub](https://github.com/langgenius/dify/discussions).
- **Traduzioni**: segui la [guida allinternazionalizzazione](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) per aggiungere o aggiornare una lingua.
- **Community**: condividi le app che crei, aiuta gli altri utenti e fai conoscere Dify.
## Community & Contatti
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Ideale per: condividere feedback e porre domande.
- [GitHub Issues](https://github.com/langgenius/dify/issues). Ideale per: bug che riscontrate durante l'utilizzo di Dify.AI e proposte di funzionalità. Consultate la nostra [Guida ai Contributi](../../CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Ideale per: condividere le vostre applicazioni e interagire con la community.
- [X(Twitter)](https://twitter.com/dify_ai). Ideale per: condividere le vostre applicazioni e interagire con la community.
**Collaboratori**
### Collaboratori
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Community e contatti
Scegli il canale più adatto alla tua richiesta:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): ricevi assistenza, condividi feedback e proponi idee.
- [GitHub Issues](https://github.com/langgenius/dify/issues): segnala bug riproducibili e segui il lavoro di sviluppo. Leggi la [Guida ai contributi](../../CONTRIBUTING.md) prima di aprire una issue.
- [Discord](https://discord.gg/FngNHpbcY7): chatta in tempo reale, condividi le tue app e incontra altri utenti Dify.
- [X](https://x.com/dify_ai): segui le notizie sulle release e gli aggiornamenti del progetto.
## Storia delle Stelle
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,57 @@
# 高度なセットアップ
設定をカスタマイズする必要がある場合は、[.env.example](../../docker/.env.example) ファイルのコメントを参照し、`.env` ファイルの対応する値を更新してください。さらに、デプロイ環境や要件に応じて、`docker-compose.yaml` ファイル自体を調整する必要がある場合があります。たとえば、イメージのバージョン、ポートのマッピング、ボリュームのマウントなどを変更します。変更を加えた後は、`docker-compose up -d` を再実行してください。利用可能な環境変数の全一覧は、[こちら](https://docs.dify.ai/getting-started/install-self-hosted/environments)で確認できます。
## Grafanaを使用したメトリクス監視
Grafanaにダッシュボードをインポートし、DifyのPostgreSQLデータベースをデータソースとして使用して、アプリ、テナント、メッセージなどの粒度でメトリクスを監視します。
- [@bowenliang123によるGrafanaダッシュボード](https://github.com/bowenliang123/dify-grafana-dashboard)
## Kubernetesでのデプロイ
高可用性設定を設定する必要がある場合、コミュニティは[Helm Charts](https://helm.sh/)とYAMLファイルにより、DifyをKubernetesにデプロイすることができます。
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 新着YAML ファイルDify v1.6.0 対応by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Terraformを使用したデプロイ
[terraform](https://www.terraform.io/) を使用して、ワンクリックでDifyをクラウドプラットフォームにデプロイします
#### Azure Global
- [@nikawangによるAzure Terraform](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [@sotazumによるGoogle Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform)
### AWS CDK を使用したデプロイ
[CDK](https://aws.amazon.com/cdk/) を使用して、DifyをAWSにデプロイします
#### AWS
- [@KevinZhaoによるAWS CDK (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [@tmokmssによるAWS CDK (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/) を利用して、DifyをAlibaba Cloudへワンクリックでデプロイできます
### AKSへのデプロイにAzure Devops Pipelineを使用
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)を使用してDifyをAKSにワンクリックでデプロイ
### Sealosを使用したデプロイ
[Sealos App Store](https://sealos.io/products/app-store/dify/)を使用してDifyをワンクリックでデプロイできます

View File

@ -108,7 +108,7 @@ LLM Function CallingやReActに基づくエージェントの定義が可能で
GitHub上でDifyにスターを付けることで、Difyに関する新しいニュースを受け取れます。
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## クイックスタート
@ -133,82 +133,31 @@ docker compose up -d
## 次のステップ
設定をカスタマイズする必要がある場合は、[.env.example](../../docker/.env.example) ファイルのコメントを参照し、`.env` ファイルの対応する値を更新してください。さらに、デプロイ環境や要件に応じて、`docker-compose.yaml` ファイル自体を調整する必要がある場合があります。たとえば、イメージのバージョン、ポートのマッピング、ボリュームのマウントなどを変更します。変更を加えた後は、`docker-compose up -d` を再実行してください。利用可能な環境変数の全一覧は、[こちら](https://docs.dify.ai/getting-started/install-self-hosted/environments)で確認できます。
### Grafanaを使用したメトリクス監視
Grafanaにダッシュボードをインポートし、DifyのPostgreSQLデータベースをデータソースとして使用して、アプリ、テナント、メッセージなどの粒度でメトリクスを監視します。
- [@bowenliang123によるGrafanaダッシュボード](https://github.com/bowenliang123/dify-grafana-dashboard)
### Kubernetesでのデプロイ
高可用性設定を設定する必要がある場合、コミュニティは[Helm Charts](https://helm.sh/)とYAMLファイルにより、DifyをKubernetesにデプロイすることができます。
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 新着YAML ファイルDify v1.6.0 対応by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Terraformを使用したデプロイ
[terraform](https://www.terraform.io/) を使用して、ワンクリックでDifyをクラウドプラットフォームにデプロイします
##### Azure Global
- [@nikawangによるAzure Terraform](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [@sotazumによるGoogle Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform)
#### AWS CDK を使用したデプロイ
[CDK](https://aws.amazon.com/cdk/) を使用して、DifyをAWSにデプロイします
##### AWS
- [@KevinZhaoによるAWS CDK (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [@tmokmssによるAWS CDK (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/) を利用して、DifyをAlibaba Cloudへワンクリックでデプロイできます
#### AKSへのデプロイにAzure Devops Pipelineを使用
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)を使用してDifyをAKSにワンクリックでデプロイ
#### Sealosを使用したデプロイ
[Sealos App Store](https://sealos.io/products/app-store/dify/)を使用してDifyをワンクリックでデプロイできます
カスタム構成、オブザーバビリティ、デプロイのオプションについては、[高度なセットアップ](ADVANCED_SETUP.md)を参照してください。
## 貢献
コードに貢献したい方は、[Contribution Guide](./CONTRIBUTING.md)を参照してください。
同時に、DifyをSNSやイベント、カンファレンスで共有してサポートしていただけると幸いです。
Difyでは、あらゆる形の貢献を歓迎しています。
> Difyを英語または中国語以外の言語に翻訳してくれる貢献者を募集しています。興味がある場合は、詳細については[i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)を参照してください。また、[Discordコミュニティサーバー](https://discord.gg/8Tpq4AcN9c)の`global-users`チャンネルにコメントを残してください。
- **コード**[コントリビューションガイド](./CONTRIBUTING.md)を読み、[good first issue](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)を確認してください。
- **アイデアとフィードバック**[GitHub Discussions](https://github.com/langgenius/dify/discussions)で新しい議論を始めるか、既存の議論に参加してください。
- **翻訳**:言語の追加や更新については、[国際化ガイド](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)に従ってください。
- **コミュニティ**作成したアプリを共有し、他のユーザーを支援し、Difyを広めてください。
**貢献者**
### 貢献者
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## コミュニティ & お問い合わせ
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). 主に: フィードバックの共有や質問。
- [GitHub Issues](https://github.com/langgenius/dify/issues). 主に: Dify.AIを使用する際に発生するエラーや問題については、[貢献ガイド](./CONTRIBUTING.md)を参照してください
- [Discord](https://discord.gg/FngNHpbcY7). 主に: アプリケーションの共有やコミュニティとの交流。
- [X(Twitter)](https://twitter.com/dify_ai). 主に: アプリケーションの共有やコミュニティとの交流。
目的に合った窓口を選んでください。
- [GitHub Discussions](https://github.com/langgenius/dify/discussions):質問、フィードバックの共有、アイデアの提案にご利用ください。
- [GitHub Issues](https://github.com/langgenius/dify/issues)再現可能なバグの報告と開発作業の追跡にご利用ください。Issueを作成する前に[コントリビューションガイド](./CONTRIBUTING.md)をお読みください。
- [Discord](https://discord.gg/FngNHpbcY7)リアルタイムで会話し、アプリを共有し、他のDifyユーザーと交流できます。
- [X](https://x.com/dify_ai):リリース情報やプロジェクトの最新情報を確認できます。
## ライセンス
このリポジトリは、Dify Open Source License にいくつかの追加制限を加えた[Difyオープンソースライセンス](../../LICENSE)の下で利用可能です。

View File

@ -0,0 +1,57 @@
# 고급 설정
구성을 사용자 정의해야 하는 경우 [.env.example](../../docker/.env.example) 파일의 주석을 참조하고 `.env` 파일에서 해당 값을 업데이트하십시오. 또한 특정 배포 환경 및 요구 사항에 따라 `docker-compose.yaml` 파일 자체를 조정해야 할 수도 있습니다. 예를 들어 이미지 버전, 포트 매핑 또는 볼륨 마운트를 변경합니다. 변경 한 후 `docker-compose up -d`를 다시 실행하십시오. 사용 가능한 환경 변수의 전체 목록은 [여기](https://docs.dify.ai/getting-started/install-self-hosted/environments)에서 찾을 수 있습니다.
## Grafana를 사용한 메트릭 모니터링
Dify의 PostgreSQL 데이터베이스를 데이터 소스로 사용하여 앱, 테넌트, 메시지 등에 대한 세분화된 메트릭을 모니터링하기 위해 대시보드를 Grafana로 가져옵니다.
- [@bowenliang123의 Grafana 대시보드](https://github.com/bowenliang123/dify-grafana-dashboard)
## Kubernetes를 통한 배포
Dify를 Kubernetes에 배포하고 프리미엄 스케일링 설정을 구성했다는 커뮤니티가 제공하는 [Helm Charts](https://helm.sh/)와 YAML 파일이 존재합니다.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Terraform을 사용한 배포
[terraform](https://www.terraform.io/)을 사용하여 단 한 번의 클릭으로 Dify를 클라우드 플랫폼에 배포하십시오
#### Azure Global
- [nikawang의 Azure Terraform](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [sotazum의 Google Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform)
### AWS CDK를 사용한 배포
[CDK](https://aws.amazon.com/cdk/)를 사용하여 AWS에 Dify 배포
#### AWS
- [KevinZhao의 AWS CDK (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [tmokmss의 AWS CDK (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)를 통해 원클릭으로 Dify를 Alibaba Cloud에 배포할 수 있습니다
### AKS에 배포하기 위해 Azure Devops Pipeline 사용
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)을 사용하여 Dify를 AKS에 원클릭으로 배포
### Sealos를 사용한 배포
[Sealos App Store](https://sealos.io/products/app-store/dify/)를 사용하여 Dify를 원클릭으로 배포할 수 있습니다

View File

@ -101,7 +101,7 @@ Dify의 모든 제품에는 해당 API가 함께 제공되므로 Dify를 자신
GitHub에서 Dify에 별표를 찍어 새로운 릴리스를 즉시 알림 받으세요.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## 빠른 시작
@ -126,82 +126,31 @@ docker compose up -d
## 다음 단계
구성을 사용자 정의해야 하는 경우 [.env.example](../../docker/.env.example) 파일의 주석을 참조하고 `.env` 파일에서 해당 값을 업데이트하십시오. 또한 특정 배포 환경 및 요구 사항에 따라 `docker-compose.yaml` 파일 자체를 조정해야 할 수도 있습니다. 예를 들어 이미지 버전, 포트 매핑 또는 볼륨 마운트를 변경합니다. 변경 한 후 `docker-compose up -d`를 다시 실행하십시오. 사용 가능한 환경 변수의 전체 목록은 [여기](https://docs.dify.ai/getting-started/install-self-hosted/environments)에서 찾을 수 있습니다.
### Grafana를 사용한 메트릭 모니터링
Dify의 PostgreSQL 데이터베이스를 데이터 소스로 사용하여 앱, 테넌트, 메시지 등에 대한 세분화된 메트릭을 모니터링하기 위해 대시보드를 Grafana로 가져옵니다.
- [@bowenliang123의 Grafana 대시보드](https://github.com/bowenliang123/dify-grafana-dashboard)
### Kubernetes를 통한 배포
Dify를 Kubernetes에 배포하고 프리미엄 스케일링 설정을 구성했다는 커뮤니티가 제공하는 [Helm Charts](https://helm.sh/)와 YAML 파일이 존재합니다.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Terraform을 사용한 배포
[terraform](https://www.terraform.io/)을 사용하여 단 한 번의 클릭으로 Dify를 클라우드 플랫폼에 배포하십시오
##### Azure Global
- [nikawang의 Azure Terraform](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [sotazum의 Google Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform)
#### AWS CDK를 사용한 배포
[CDK](https://aws.amazon.com/cdk/)를 사용하여 AWS에 Dify 배포
##### AWS
- [KevinZhao의 AWS CDK (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [tmokmss의 AWS CDK (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)를 통해 원클릭으로 Dify를 Alibaba Cloud에 배포할 수 있습니다
#### AKS에 배포하기 위해 Azure Devops Pipeline 사용
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)을 사용하여 Dify를 AKS에 원클릭으로 배포
#### Sealos를 사용한 배포
[Sealos App Store](https://sealos.io/products/app-store/dify/)를 사용하여 Dify를 원클릭으로 배포할 수 있습니다
사용자 지정 구성, 관측 가능성 및 배포 옵션은 [고급 설정](ADVANCED_SETUP.md)을 참조하세요.
## 기여
코드에 기여하고 싶은 분들은 [기여 가이드](./CONTRIBUTING.md)를 참조하세요.
동시에 Dify를 소셜 미디어와 행사 및 컨퍼런스에 공유하여 지원하는 것을 고려해 주시기 바랍니다.
Dify는 모든 형태의 기여를 환영합니다.
> 우리는 Dify를 중국어나 영어 이외의 언어로 번역하는 데 도움을 줄 수 있는 기여자를 찾고 있습니다. 도움을 주고 싶으시다면 [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)에서 더 많은 정보를 확인하시고 [Discord 커뮤니티 서버](https://discord.gg/8Tpq4AcN9c)의 `global-users` 채널에 댓글을 남겨주세요.
- **코드**: [기여 가이드](./CONTRIBUTING.md)를 읽고 [good first issue](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)를 살펴보세요.
- **아이디어 및 피드백**: [GitHub 토론](https://github.com/langgenius/dify/discussions)을 시작하거나 기존 토론에 참여하세요.
- **번역**: 언어를 추가하거나 업데이트하려면 [국제화 가이드](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)를 따르세요.
- **커뮤니티**: 만든 앱을 공유하고, 다른 사용자를 돕고, Dify를 널리 알려주세요.
**기여자**
### 기여자
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## 커뮤니티 & 연락처
- [GitHub 토론](https://github.com/langgenius/dify/discussions). 피드백 공유 및 질문하기에 적합합니다.
- [GitHub 이슈](https://github.com/langgenius/dify/issues). Dify.AI 사용 중 발견한 버그와 기능 제안에 적합합니다. [기여 가이드](./CONTRIBUTING.md)를 참조하세요.
- [디스코드](https://discord.gg/FngNHpbcY7). 애플리케이션 공유 및 커뮤니티와 소통하기에 적합합니다.
- [트위터](https://twitter.com/dify_ai). 애플리케이션 공유 및 커뮤니티와 소통하기에 적합합니다.
문의 내용에 가장 적합한 채널을 선택하세요.
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): 도움을 받고, 피드백을 공유하고, 아이디어를 제안하세요.
- [GitHub Issues](https://github.com/langgenius/dify/issues): 재현 가능한 버그를 보고하고 개발 작업을 추적하세요. 이슈를 열기 전에 [기여 가이드](./CONTRIBUTING.md)를 읽어주세요.
- [Discord](https://discord.gg/FngNHpbcY7): 실시간으로 대화하고, 앱을 공유하고, 다른 Dify 사용자와 교류하세요.
- [X](https://x.com/dify_ai): 릴리스 소식과 프로젝트 업데이트를 확인하세요.
## Star 히스토리
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,57 @@
# Configuração avançada
Se precisar personalizar a configuração, consulte os comentários no nosso arquivo [.env.example](../../docker/.env.example) e atualize os valores correspondentes no seu arquivo `.env`. Além disso, talvez seja necessário fazer ajustes no próprio arquivo `docker-compose.yaml`, como alterar versões de imagem, mapeamentos de portas ou montagens de volumes, com base no seu ambiente de implantação específico e nas suas necessidades. Após fazer quaisquer alterações, execute novamente `docker-compose up -d`. Você pode encontrar a lista completa de variáveis de ambiente disponíveis [aqui](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## Monitoramento de Métricas com Grafana
Importe o dashboard para o Grafana, usando o banco de dados PostgreSQL do Dify como fonte de dados, para monitorar métricas na granularidade de aplicativos, inquilinos, mensagens e muito mais.
- [Dashboard do Grafana por @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Implantação com Kubernetes
Se deseja configurar uma instalação de alta disponibilidade, há [Helm Charts](https://helm.sh/) e arquivos YAML contribuídos pela comunidade que permitem a implantação do Dify no Kubernetes.
- [Helm Chart de @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart de @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart de @magicsong](https://github.com/magicsong/ai-charts)
- [Arquivo YAML por @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Arquivo YAML por @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NOVO! Arquivos YAML (Compatível com Dify v1.6.0) por @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Usando o Terraform para Implantação
Implante o Dify na Plataforma Cloud com um único clique usando [terraform](https://www.terraform.io/)
#### Azure Global
- [Azure Terraform por @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform por @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Usando AWS CDK para Implantação
Implante o Dify na AWS usando [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK por @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK por @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
Implante o Dify na Alibaba Cloud com um clique usando o [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Usando Azure Devops Pipeline para Implantar no AKS
Implante o Dify no AKS com um clique usando [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### Implantação com Sealos
Implante o Dify com um clique usando a [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -106,7 +106,7 @@ Todas os recursos do Dify vêm com APIs correspondentes, permitindo que você in
Dê uma estrela no Dify no GitHub e seja notificado imediatamente sobre novos lançamentos.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Início rápido
@ -131,82 +131,31 @@ Após a execução, você pode acessar o painel do Dify no navegador em [http://
## Próximos passos
Se precisar personalizar a configuração, consulte os comentários no nosso arquivo [.env.example](../../docker/.env.example) e atualize os valores correspondentes no seu arquivo `.env`. Além disso, talvez seja necessário fazer ajustes no próprio arquivo `docker-compose.yaml`, como alterar versões de imagem, mapeamentos de portas ou montagens de volumes, com base no seu ambiente de implantação específico e nas suas necessidades. Após fazer quaisquer alterações, execute novamente `docker-compose up -d`. Você pode encontrar a lista completa de variáveis de ambiente disponíveis [aqui](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### Monitoramento de Métricas com Grafana
Importe o dashboard para o Grafana, usando o banco de dados PostgreSQL do Dify como fonte de dados, para monitorar métricas na granularidade de aplicativos, inquilinos, mensagens e muito mais.
- [Dashboard do Grafana por @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Implantação com Kubernetes
Se deseja configurar uma instalação de alta disponibilidade, há [Helm Charts](https://helm.sh/) e arquivos YAML contribuídos pela comunidade que permitem a implantação do Dify no Kubernetes.
- [Helm Chart de @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart de @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart de @magicsong](https://github.com/magicsong/ai-charts)
- [Arquivo YAML por @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Arquivo YAML por @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NOVO! Arquivos YAML (Compatível com Dify v1.6.0) por @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Usando o Terraform para Implantação
Implante o Dify na Plataforma Cloud com um único clique usando [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform por @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform por @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Usando AWS CDK para Implantação
Implante o Dify na AWS usando [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK por @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK por @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
Implante o Dify na Alibaba Cloud com um clique usando o [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Usando Azure Devops Pipeline para Implantar no AKS
Implante o Dify no AKS com um clique usando [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### Implantação com Sealos
Implante o Dify com um clique usando a [Sealos App Store](https://sealos.io/products/app-store/dify/)
Para opções de configuração personalizada, observabilidade e implantação, consulte a [Configuração avançada](ADVANCED_SETUP.md).
## Contribuindo
Para aqueles que desejam contribuir com código, veja nosso [Guia de Contribuição](./CONTRIBUTING.md).
Ao mesmo tempo, considere apoiar o Dify compartilhando-o nas redes sociais e em eventos e conferências.
O Dify aceita contribuições de todos os tipos:
> Estamos buscando contribuidores para ajudar na tradução do Dify para idiomas além de Mandarim e Inglês. Se você tiver interesse em ajudar, consulte o [README i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) para mais informações e deixe-nos um comentário no canal `global-users` em nosso [Servidor da Comunidade no Discord](https://discord.gg/8Tpq4AcN9c).
- **Código**: leia o [Guia de Contribuição](./CONTRIBUTING.md) e consulte as [issues para iniciantes](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ideias e feedback**: inicie ou participe de uma [discussão no GitHub](https://github.com/langgenius/dify/discussions).
- **Traduções**: siga o [guia de internacionalização](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) para adicionar ou atualizar um idioma.
- **Comunidade**: compartilhe os aplicativos que você criar, ajude outros usuários e divulgue o Dify.
**Contribuidores**
### Contribuidores
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Comunidade e contato
- [Discussões no GitHub](https://github.com/langgenius/dify/discussions). Melhor para: compartilhar feedback e fazer perguntas.
- [Problemas no GitHub](https://github.com/langgenius/dify/issues). Melhor para: relatar bugs encontrados no Dify.AI e propor novos recursos. Veja nosso [Guia de Contribuição](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Melhor para: compartilhar suas aplicações e interagir com a comunidade.
- [X(Twitter)](https://twitter.com/dify_ai). Melhor para: compartilhar suas aplicações e interagir com a comunidade.
Escolha o canal mais adequado para a sua dúvida:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): obtenha ajuda, compartilhe feedback e proponha ideias.
- [GitHub Issues](https://github.com/langgenius/dify/issues): relate bugs reproduzíveis e acompanhe o trabalho de engenharia. Leia o [Guia de Contribuição](./CONTRIBUTING.md) antes de abrir uma issue.
- [Discord](https://discord.gg/FngNHpbcY7): converse em tempo real, compartilhe seus aplicativos e conecte-se com outros usuários do Dify.
- [X](https://x.com/dify_ai): acompanhe notícias de versões e atualizações do projeto.
## Histórico de estrelas
[![Gráfico de Histórico de Estrelas](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,56 @@
# Napredne nastavitve
Če morate prilagoditi konfiguracijo, si oglejte komentarje v naši datoteki .env.example in posodobite ustrezne vrednosti v svoji .env datoteki. Poleg tega boste morda morali prilagoditi docker-compose.yamlsamo datoteko, na primer spremeniti različice slike, preslikave vrat ali namestitve nosilca, glede na vaše specifično okolje in zahteve za uvajanje. Po kakršnih koli spremembah ponovno zaženite docker-compose up -d. Celoten seznam razpoložljivih spremenljivk okolja najdete tukaj .
## Spremljanje metrik z Grafana
Uvoz nadzorne plošče v Grafana, z uporabo Difyjeve PostgreSQL baze podatkov kot vir podatkov, za spremljanje metrike glede na podrobnost aplikacij, najemnikov, sporočil in drugega.
- [Nadzorna plošča Grafana avtorja @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Namestitev s Kubernetes
Če želite konfigurirati visoko razpoložljivo nastavitev, so na voljo Helm Charts in datoteke YAML, ki jih prispeva skupnost, ki omogočajo uvedbo Difyja v Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Uporaba Terraform za uvajanje
namestite Dify v Cloud Platform z enim klikom z uporabo [terraform](https://www.terraform.io/)
#### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Uporaba AWS CDK za uvajanje
Uvedite Dify v AWS z uporabo [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
Z enim klikom namestite Dify na Alibaba Cloud z [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Uporaba Azure Devops Pipeline za uvajanje v AKS
Z enim klikom namestite Dify v AKS z uporabo [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### Namestitev s Sealos
Z enim klikom namestite Dify prek [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -129,84 +129,35 @@ AVse ponudbe Difyja so opremljene z ustreznimi API-ji, tako da lahko Dify brez t
Star Dify on GitHub and be instantly notified of new releases.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Napredne nastavitve
Če morate prilagoditi konfiguracijo, si oglejte komentarje v naši datoteki .env.example in posodobite ustrezne vrednosti v svoji .env datoteki. Poleg tega boste morda morali prilagoditi docker-compose.yamlsamo datoteko, na primer spremeniti različice slike, preslikave vrat ali namestitve nosilca, glede na vaše specifično okolje in zahteve za uvajanje. Po kakršnih koli spremembah ponovno zaženite docker-compose up -d. Celoten seznam razpoložljivih spremenljivk okolja najdete tukaj .
Za možnosti konfiguracije po meri, opazovanja in namestitve glejte [Napredne nastavitve](ADVANCED_SETUP.md).
### Spremljanje metrik z Grafana
## Prispevanje
Uvoz nadzorne plošče v Grafana, z uporabo Difyjeve PostgreSQL baze podatkov kot vir podatkov, za spremljanje metrike glede na podrobnost aplikacij, najemnikov, sporočil in drugega.
Dify pozdravlja vse vrste prispevkov:
- [Nadzorna plošča Grafana avtorja @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
- **Koda**: preberite [vodnik za prispevke](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md), nato pa preglejte [naloge za nove sodelavce](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ideje in povratne informacije**: začnite ali se pridružite [razpravi na GitHubu](https://github.com/langgenius/dify/discussions).
- **Prevodi**: sledite [vodniku za internacionalizacijo](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md), da dodate ali posodobite jezik.
- **Skupnost**: delite ustvarjene aplikacije, pomagajte drugim uporabnikom in razširite glas o Difyju.
### Namestitev s Kubernetes
### Sodelavci
Če želite konfigurirati visoko razpoložljivo nastavitev, so na voljo Helm Charts in datoteke YAML, ki jih prispeva skupnost, ki omogočajo uvedbo Difyja v Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Uporaba Terraform za uvajanje
namestite Dify v Cloud Platform z enim klikom z uporabo [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Uporaba AWS CDK za uvajanje
Uvedite Dify v AWS z uporabo [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
Z enim klikom namestite Dify na Alibaba Cloud z [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Uporaba Azure Devops Pipeline za uvajanje v AKS
Z enim klikom namestite Dify v AKS z uporabo [Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### Namestitev s Sealos
Z enim klikom namestite Dify prek [Sealos App Store](https://sealos.io/products/app-store/dify/)
## Prispevam
Za tiste, ki bi radi prispevali kodo, si oglejte naš [vodnik za prispevke](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). Hkrati vas prosimo, da podprete Dify tako, da ga delite na družbenih medijih ter na dogodkih in konferencah.
> Iščemo sodelavce za pomoč pri prevajanju Difyja v jezike, ki niso mandarinščina ali angleščina. Če želite pomagati, si oglejte i18n README za več informacij in nam pustite komentar v global-userskanalu našega strežnika skupnosti Discord .
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Skupnost in stik
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Najboljše za: izmenjavo povratnih informacij in postavljanje vprašanj.
- [GitHub Issues](https://github.com/langgenius/dify/issues). Najboljše za: hrošče, na katere naletite pri uporabi Dify.AI, in predloge funkcij. Oglejte si naš [vodnik za prispevke](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Najboljše za: deljenje vaših aplikacij in druženje s skupnostjo.
- [X(Twitter)](https://twitter.com/dify_ai). Najboljše za: deljenje vaših aplikacij in druženje s skupnostjo.
**Contributors**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
Izberite kanal, ki najbolj ustreza vašemu vprašanju:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): poiščite pomoč, delite povratne informacije in predlagajte ideje.
- [GitHub Issues](https://github.com/langgenius/dify/issues): prijavite ponovljive napake in spremljajte razvojno delo. Pred odprtjem težave preberite [vodnik za prispevke](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7): klepetajte v živo, delite svoje aplikacije in se povežite z drugimi uporabniki Difyja.
- [X](https://x.com/dify_ai): spremljajte novice o izdajah in posodobitve projekta.
## Star history
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,49 @@
# Advanced Setup
If you need to customize the configuration, please refer to the comments in our [.env.example](../../docker/.env.example) file and update the corresponding values in your `.env` file. Additionally, you might need to make adjustments to the `docker-compose.yaml` file itself, such as changing image versions, port mappings, or volume mounts, based on your specific deployment environment and requirements. After making any changes, please re-run `docker-compose up -d`. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
If you'd like to configure a highly-available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Terraform atorlugu pilersitsineq
wa'logh nIqHom neH ghun deployment toy'wI' [terraform](https://www.terraform.io/) lo'laH.
#### Azure Global
- [Azure Terraform mung @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform qachlot @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### AWS CDK atorlugh pilersitsineq
wa'logh nIqHom neH ghun deployment toy'wI' [CDK](https://aws.amazon.com/cdk/) lo'laH.
#### AWS
- [AWS CDK qachlot @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK qachlot @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### AKS 'e' Deploy je Azure Devops Pipeline lo'laH
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) lo'laH Dify AKS 'e' wa'DIch click 'e' Deploy
### Sealos lo'laH Deploy
[Sealos App Store](https://sealos.io/products/app-store/dify/) lo'laH Dify wa'DIch click 'e' Deploy

View File

@ -107,7 +107,7 @@ All of Dify's offerings come with corresponding APIs, so you could effortlessly
Star Dify on GitHub and be instantly notified of new releases.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Quick Start
@ -132,74 +132,31 @@ After running, you can access the Dify dashboard in your browser at [http://loca
## Next steps
If you need to customize the configuration, please refer to the comments in our [.env.example](../../docker/.env.example) file and update the corresponding values in your `.env` file. Additionally, you might need to make adjustments to the `docker-compose.yaml` file itself, such as changing image versions, port mappings, or volume mounts, based on your specific deployment environment and requirements. After making any changes, please re-run `docker-compose up -d`. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
If you'd like to configure a highly-available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML files (Supports Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Terraform atorlugu pilersitsineq
wa'logh nIqHom neH ghun deployment toy'wI' [terraform](https://www.terraform.io/) lo'laH.
##### Azure Global
- [Azure Terraform mung @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform qachlot @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### AWS CDK atorlugh pilersitsineq
wa'logh nIqHom neH ghun deployment toy'wI' [CDK](https://aws.amazon.com/cdk/) lo'laH.
##### AWS
- [AWS CDK qachlot @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK qachlot @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### AKS 'e' Deploy je Azure Devops Pipeline lo'laH
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) lo'laH Dify AKS 'e' wa'DIch click 'e' Deploy
#### Sealos lo'laH Deploy
[Sealos App Store](https://sealos.io/products/app-store/dify/) lo'laH Dify wa'DIch click 'e' Deploy
For custom configuration, observability, and deployment options, see [Advanced Setup](ADVANCED_SETUP.md).
## Contributing
For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
At the same time, please consider supporting Dify by sharing it on social media and at events and conferences.
Dify welcomes contributions of all kinds:
> We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c).
- **Code**: Read the [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md), then browse [good first issues](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ideas and feedback**: Start or join a [GitHub Discussion](https://github.com/langgenius/dify/discussions).
- **Translations**: Follow the [internationalization guide](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) to add or update a locale.
- **Community**: Share the apps you build, help other users, and spread the word about Dify.
**Contributors**
### Contributors
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Community & Contact
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and asking questions.
- [GitHub Issues](https://github.com/langgenius/dify/issues). Best for: bugs you encounter using Dify.AI, and feature proposals. See our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Best for: sharing your applications and hanging out with the community.
- [X(Twitter)](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
Choose the channel that best fits your question:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): Get help, share feedback, and propose ideas.
- [GitHub Issues](https://github.com/langgenius/dify/issues): Report reproducible bugs and track engineering work. Read the [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) before opening one.
- [Discord](https://discord.gg/FngNHpbcY7): Chat in real time, share your apps, and connect with other Dify users.
- [X](https://x.com/dify_ai): Follow Dify for release news and project updates.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,56 @@
# Gelişmiş Kurulum
Yapılandırmayı özelleştirmeniz gerekiyorsa, lütfen [.env.example](../../docker/.env.example) dosyamızdaki yorumlara bakın ve `.env` dosyanızdaki ilgili değerleri güncelleyin. Ayrıca, spesifik dağıtım ortamınıza ve gereksinimlerinize bağlı olarak `docker-compose.yaml` dosyasının kendisinde de, imaj sürümlerini, port eşlemelerini veya hacim bağlantılarını değiştirmek gibi ayarlamalar yapmanız gerekebilir. Herhangi bir değişiklik yaptıktan sonra, lütfen `docker-compose up -d` komutunu tekrar çalıştırın. Kullanılabilir tüm ortam değişkenlerinin tam listesini [burada](https://docs.dify.ai/getting-started/install-self-hosted/environments) bulabilirsiniz.
## Grafana ile Metrik İzleme
Uygulamalar, kiracılar, mesajlar ve daha fazlasının granularitesinde metrikleri izlemek için Dify'nin PostgreSQL veritabanını veri kaynağı olarak kullanarak panoyu Grafana'ya aktarın.
- [@bowenliang123 tarafından Grafana Panosu](https://github.com/bowenliang123/dify-grafana-dashboard)
## Kubernetes ile Dağıtım
Yüksek kullanılabilirliğe sahip bir kurulum yapılandırmak isterseniz, Dify'ın Kubernetes üzerine dağıtılmasına olanak tanıyan topluluk katkılı [Helm Charts](https://helm.sh/) ve YAML dosyaları mevcuttur.
- [@LeoQuote tarafından Helm Chart](https://github.com/douban/charts/tree/master/charts/dify)
- [@BorisPolonsky tarafından Helm Chart](https://github.com/BorisPolonsky/dify-helm)
- [@Winson-030 tarafından YAML dosyası](https://github.com/Winson-030/dify-kubernetes)
- [@wyy-holding tarafından YAML dosyası](https://github.com/wyy-holding/dify-k8s)
- [🚀 YENİ! YAML dosyaları (Dify v1.6.0 destekli) @Zhoneym tarafından](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Dağıtım için Terraform Kullanımı
Dify'ı bulut platformuna tek tıklamayla dağıtın [terraform](https://www.terraform.io/) kullanarak
#### Azure Global
- [Azure Terraform tarafından @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform tarafından @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### AWS CDK ile Dağıtım
[CDK](https://aws.amazon.com/cdk/) kullanarak Dify'ı AWS'ye dağıtın
#### AWS
- [AWS CDK tarafından @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK tarafından @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/) kullanarak Dify'ı tek tıkla Alibaba Cloud'a dağıtın
### AKS'ye Dağıtım için Azure Devops Pipeline Kullanımı
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) kullanarak Dify'ı tek tıkla AKS'ye dağıtın
### Sealos ile Dağıtım
[Sealos App Store](https://sealos.io/products/app-store/dify/) kullanarak Dify'ı tek tıkla dağıtın

View File

@ -102,7 +102,7 @@ Dify'ın tüm özellikleri ilgili API'lerle birlikte gelir, böylece Dify'ı ken
GitHub'da Dify'a yıldız verin ve yeni sürümlerden anında haberdar olun.
![bizi-yıldızlayın](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Hızlı başlangıç
@ -126,81 +126,31 @@ docker compose up -d
## Sonraki adımlar
Yapılandırmayı özelleştirmeniz gerekiyorsa, lütfen [.env.example](../../docker/.env.example) dosyamızdaki yorumlara bakın ve `.env` dosyanızdaki ilgili değerleri güncelleyin. Ayrıca, spesifik dağıtım ortamınıza ve gereksinimlerinize bağlı olarak `docker-compose.yaml` dosyasının kendisinde de, imaj sürümlerini, port eşlemelerini veya hacim bağlantılarını değiştirmek gibi ayarlamalar yapmanız gerekebilir. Herhangi bir değişiklik yaptıktan sonra, lütfen `docker-compose up -d` komutunu tekrar çalıştırın. Kullanılabilir tüm ortam değişkenlerinin tam listesini [burada](https://docs.dify.ai/getting-started/install-self-hosted/environments) bulabilirsiniz.
### Grafana ile Metrik İzleme
Uygulamalar, kiracılar, mesajlar ve daha fazlasının granularitesinde metrikleri izlemek için Dify'nin PostgreSQL veritabanını veri kaynağı olarak kullanarak panoyu Grafana'ya aktarın.
- [@bowenliang123 tarafından Grafana Panosu](https://github.com/bowenliang123/dify-grafana-dashboard)
### Kubernetes ile Dağıtım
Yüksek kullanılabilirliğe sahip bir kurulum yapılandırmak isterseniz, Dify'ın Kubernetes üzerine dağıtılmasına olanak tanıyan topluluk katkılı [Helm Charts](https://helm.sh/) ve YAML dosyaları mevcuttur.
- [@LeoQuote tarafından Helm Chart](https://github.com/douban/charts/tree/master/charts/dify)
- [@BorisPolonsky tarafından Helm Chart](https://github.com/BorisPolonsky/dify-helm)
- [@Winson-030 tarafından YAML dosyası](https://github.com/Winson-030/dify-kubernetes)
- [@wyy-holding tarafından YAML dosyası](https://github.com/wyy-holding/dify-k8s)
- [🚀 YENİ! YAML dosyaları (Dify v1.6.0 destekli) @Zhoneym tarafından](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Dağıtım için Terraform Kullanımı
Dify'ı bulut platformuna tek tıklamayla dağıtın [terraform](https://www.terraform.io/) kullanarak
##### Azure Global
- [Azure Terraform tarafından @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform tarafından @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### AWS CDK ile Dağıtım
[CDK](https://aws.amazon.com/cdk/) kullanarak Dify'ı AWS'ye dağıtın
##### AWS
- [AWS CDK tarafından @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK tarafından @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
[Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/) kullanarak Dify'ı tek tıkla Alibaba Cloud'a dağıtın
#### AKS'ye Dağıtım için Azure Devops Pipeline Kullanımı
[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) kullanarak Dify'ı tek tıkla AKS'ye dağıtın
#### Sealos ile Dağıtım
[Sealos App Store](https://sealos.io/products/app-store/dify/) kullanarak Dify'ı tek tıkla dağıtın
Özel yapılandırma, gözlemlenebilirlik ve dağıtım seçenekleri için [Gelişmiş Kurulum](ADVANCED_SETUP.md) belgesine bakın.
## Katkıda Bulunma
Kod katkısında bulunmak isteyenler için [Katkı Kılavuzumuza](./CONTRIBUTING.md) bakabilirsiniz.
Aynı zamanda, lütfen Dify'ı sosyal medyada, etkinliklerde ve konferanslarda paylaşarak desteklemeyi düşünün.
Dify her türlü katkıyı memnuniyetle karşılar:
> Dify'ı Mandarin veya İngilizce dışındaki dillere çevirmemize yardımcı olacak katkıda bulunanlara ihtiyacımız var. Yardımcı olmakla ilgileniyorsanız, lütfen daha fazla bilgi için [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) dosyasına bakın ve [Discord Topluluk Sunucumuzdaki](https://discord.gg/8Tpq4AcN9c) `global-users` kanalında bize bir yorum bırakın.
- **Kod**: [Katkı Kılavuzu'nu](./CONTRIBUTING.md) okuyun, ardından [yeni katkıda bulunanlara uygun sorunlara](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) göz atın.
- **Fikirler ve geri bildirim**: Bir [GitHub tartışması](https://github.com/langgenius/dify/discussions) başlatın veya mevcut bir tartışmaya katılın.
- **Çeviriler**: Bir dil eklemek veya güncellemek için [uluslararasılaştırma kılavuzunu](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) izleyin.
- **Topluluk**: Oluşturduğunuz uygulamaları paylaşın, diğer kullanıcılara yardımcı olun ve Dify'ı duyurun.
**Katkıda Bulunanlar**
### Katkıda bulunanlar
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Topluluk & iletişim
## Topluluk ve iletişim
- [GitHub Tartışmaları](https://github.com/langgenius/dify/discussions). En uygun: geri bildirim paylaşmak ve soru sormak için.
- [GitHub Sorunları](https://github.com/langgenius/dify/issues). En uygun: Dify.AI kullanırken karşılaştığınız hatalar ve özellik önerileri için. [Katkı Kılavuzumuza](./CONTRIBUTING.md) bakın.
- [Discord](https://discord.gg/FngNHpbcY7). En uygun: uygulamalarınızı paylaşmak ve toplulukla vakit geçirmek için.
- [X(Twitter)](https://twitter.com/dify_ai). En uygun: uygulamalarınızı paylaşmak ve toplulukla vakit geçirmek için.
Sorunuza en uygun kanalı seçin:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): Yardım alın, geri bildirim paylaşın ve fikir önerin.
- [GitHub Issues](https://github.com/langgenius/dify/issues): Tekrarlanabilir hataları bildirin ve mühendislik çalışmalarını takip edin. Bir sorun açmadan önce [Katkı Kılavuzu'nu](./CONTRIBUTING.md) okuyun.
- [Discord](https://discord.gg/FngNHpbcY7): Gerçek zamanlı sohbet edin, uygulamalarınızı paylaşın ve diğer Dify kullanıcılarıyla bağlantı kurun.
- [X](https://x.com/dify_ai): Sürüm haberleri ve proje güncellemeleri için Dify'ı takip edin.
## Star history
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,56 @@
# Thiết lập nâng cao
Nếu bạn cần tùy chỉnh cấu hình, vui lòng tham khảo các nhận xét trong tệp [.env.example](../../docker/.env.example) của chúng tôi và cập nhật các giá trị tương ứng trong tệp `.env` của bạn. Ngoài ra, bạn có thể cần điều chỉnh tệp `docker-compose.yaml`, chẳng hạn như thay đổi phiên bản hình ảnh, ánh xạ cổng hoặc gắn kết khối lượng, dựa trên môi trường triển khai cụ thể và yêu cầu của bạn. Sau khi thực hiện bất kỳ thay đổi nào, vui lòng chạy lại `docker-compose up -d`. Bạn có thể tìm thấy danh sách đầy đủ các biến môi trường có sẵn [tại đây](https://docs.dify.ai/getting-started/install-self-hosted/environments).
## Giám sát Số liệu với Grafana
Nhập bảng điều khiển vào Grafana, sử dụng cơ sở dữ liệu PostgreSQL của Dify làm nguồn dữ liệu, để giám sát số liệu theo mức độ chi tiết của ứng dụng, người thuê, tin nhắn và hơn thế nữa.
- [Bảng điều khiển Grafana của @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
## Triển khai với Kubernetes
Nếu bạn muốn cấu hình một cài đặt có độ sẵn sàng cao, có các [Helm Charts](https://helm.sh/) và tệp YAML do cộng đồng đóng góp cho phép Dify được triển khai trên Kubernetes.
- [Helm Chart bởi @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart bởi @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Tệp YAML bởi @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Tệp YAML bởi @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 MỚI! Tệp YAML (Hỗ trợ Dify v1.6.0) bởi @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### Sử dụng Terraform để Triển khai
Triển khai Dify lên nền tảng đám mây với một cú nhấp chuột bằng cách sử dụng [terraform](https://www.terraform.io/)
#### Azure Global
- [Azure Terraform bởi @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform bởi @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### Sử dụng AWS CDK để Triển khai
Triển khai Dify trên AWS bằng [CDK](https://aws.amazon.com/cdk/)
#### AWS
- [AWS CDK bởi @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK bởi @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### Alibaba Cloud Data Management
Triển khai Dify lên Alibaba Cloud chỉ với một cú nhấp chuột bằng [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
### Sử dụng Azure Devops Pipeline để Triển khai lên AKS
Triển khai Dify lên AKS chỉ với một cú nhấp chuột bằng [Azure Devops Pipeline Helm Chart bởi @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
### Triển khai với Sealos
Triển khai Dify chỉ với một cú nhấp chuột từ [Sealos App Store](https://sealos.io/products/app-store/dify/)

View File

@ -102,7 +102,7 @@ Tất cả các dịch vụ của Dify đều đi kèm với các API tương
Yêu thích Dify trên GitHub và được thông báo ngay lập tức về các bản phát hành mới.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## Bắt đầu nhanh
@ -127,81 +127,31 @@ Sau khi chạy, bạn có thể truy cập bảng điều khiển Dify trong tr
## Các bước tiếp theo
Nếu bạn cần tùy chỉnh cấu hình, vui lòng tham khảo các nhận xét trong tệp [.env.example](../../docker/.env.example) của chúng tôi và cập nhật các giá trị tương ứng trong tệp `.env` của bạn. Ngoài ra, bạn có thể cần điều chỉnh tệp `docker-compose.yaml`, chẳng hạn như thay đổi phiên bản hình ảnh, ánh xạ cổng hoặc gắn kết khối lượng, dựa trên môi trường triển khai cụ thể và yêu cầu của bạn. Sau khi thực hiện bất kỳ thay đổi nào, vui lòng chạy lại `docker-compose up -d`. Bạn có thể tìm thấy danh sách đầy đủ các biến môi trường có sẵn [tại đây](https://docs.dify.ai/getting-started/install-self-hosted/environments).
### Giám sát Số liệu với Grafana
Nhập bảng điều khiển vào Grafana, sử dụng cơ sở dữ liệu PostgreSQL của Dify làm nguồn dữ liệu, để giám sát số liệu theo mức độ chi tiết của ứng dụng, người thuê, tin nhắn và hơn thế nữa.
- [Bảng điều khiển Grafana của @bowenliang123](https://github.com/bowenliang123/dify-grafana-dashboard)
### Triển khai với Kubernetes
Nếu bạn muốn cấu hình một cài đặt có độ sẵn sàng cao, có các [Helm Charts](https://helm.sh/) và tệp YAML do cộng đồng đóng góp cho phép Dify được triển khai trên Kubernetes.
- [Helm Chart bởi @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart bởi @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Tệp YAML bởi @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [Tệp YAML bởi @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 MỚI! Tệp YAML (Hỗ trợ Dify v1.6.0) bởi @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### Sử dụng Terraform để Triển khai
Triển khai Dify lên nền tảng đám mây với một cú nhấp chuột bằng cách sử dụng [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform bởi @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform bởi @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Sử dụng AWS CDK để Triển khai
Triển khai Dify trên AWS bằng [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK bởi @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK bởi @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### Alibaba Cloud
[Alibaba Cloud Computing Nest](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
#### Alibaba Cloud Data Management
Triển khai Dify lên Alibaba Cloud chỉ với một cú nhấp chuột bằng [Alibaba Cloud Data Management](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/)
#### Sử dụng Azure Devops Pipeline để Triển khai lên AKS
Triển khai Dify lên AKS chỉ với một cú nhấp chuột bằng [Azure Devops Pipeline Helm Chart bởi @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS)
#### Triển khai với Sealos
Triển khai Dify chỉ với một cú nhấp chuột từ [Sealos App Store](https://sealos.io/products/app-store/dify/)
Để biết các tùy chọn cấu hình tùy chỉnh, khả năng quan sát và triển khai, hãy xem [Thiết lập nâng cao](ADVANCED_SETUP.md).
## Đóng góp
Đối với những người muốn đóng góp mã, xem [Hướng dẫn Đóng góp](./CONTRIBUTING.md) của chúng tôi.
Đồng thời, vui lòng xem xét hỗ trợ Dify bằng cách chia sẻ nó trên mạng xã hội và tại các sự kiện và hội nghị.
Dify chào đón mọi hình thức đóng góp:
> Chúng tôi đang tìm kiếm người đóng góp để giúp dịch Dify sang các ngôn ngữ khác ngoài tiếng Trung hoặc tiếng Anh. Nếu bạn quan tâm đến việc giúp đỡ, vui lòng xem [README i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) để biết thêm thông tin và để lại bình luận cho chúng tôi trong kênh `global-users` của [Máy chủ Cộng đồng Discord](https://discord.gg/8Tpq4AcN9c) của chúng tôi.
- **Mã nguồn**: đọc [Hướng dẫn đóng góp](./CONTRIBUTING.md), sau đó xem các [issue phù hợp cho người mới](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22).
- **Ý tưởng và phản hồi**: bắt đầu hoặc tham gia một [thảo luận trên GitHub](https://github.com/langgenius/dify/discussions).
- **Bản dịch**: làm theo [hướng dẫn quốc tế hóa](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) để thêm hoặc cập nhật một ngôn ngữ.
- **Cộng đồng**: chia sẻ ứng dụng bạn xây dựng, hỗ trợ người dùng khác và giới thiệu Dify đến nhiều người hơn.
**Người đóng góp**
### Người đóng góp
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Cộng đồng & liên hệ
## Cộng đồng và liên hệ
- [Thảo luận GitHub](https://github.com/langgenius/dify/discussions). Tốt nhất cho: chia sẻ phản hồi và đặt câu hỏi.
- [Vấn đề GitHub](https://github.com/langgenius/dify/issues). Tốt nhất cho: lỗi bạn gặp phải khi sử dụng Dify.AI và đề xuất tính năng. Xem [Hướng dẫn Đóng góp](./CONTRIBUTING.md) của chúng tôi.
- [Discord](https://discord.gg/FngNHpbcY7). Tốt nhất cho: chia sẻ ứng dụng của bạn và giao lưu với cộng đồng.
- [X(Twitter)](https://twitter.com/dify_ai). Tốt nhất cho: chia sẻ ứng dụng của bạn và giao lưu với cộng đồng.
Chọn kênh phù hợp nhất với câu hỏi của bạn:
- [GitHub Discussions](https://github.com/langgenius/dify/discussions): nhận trợ giúp, chia sẻ phản hồi và đề xuất ý tưởng.
- [GitHub Issues](https://github.com/langgenius/dify/issues): báo cáo lỗi có thể tái hiện và theo dõi công việc kỹ thuật. Đọc [Hướng dẫn đóng góp](./CONTRIBUTING.md) trước khi mở issue.
- [Discord](https://discord.gg/FngNHpbcY7): trò chuyện theo thời gian thực, chia sẻ ứng dụng và kết nối với những người dùng Dify khác.
- [X](https://x.com/dify_ai): theo dõi tin tức phát hành và cập nhật dự án.
## Lịch sử Yêu thích
[![Biểu đồ Lịch sử Yêu thích](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)

View File

@ -0,0 +1,64 @@
# 高级设置
## 自定义配置
如果您需要自定义配置,请参考 [.env.example](../../docker/.env.example) 文件中的注释,并更新 `.env` 文件中对应的值。此外,您可能需要根据您的具体部署环境和需求对 `docker-compose.yaml` 文件本身进行调整,例如更改镜像版本、端口映射或卷挂载。完成任何更改后,请重新运行 `docker-compose up -d`。您可以在[此处](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用环境变量的完整列表。
## 使用 Grafana 进行指标监控
将仪表板导入 Grafana使用 Dify 的 PostgreSQL 数据库作为数据源,以监控应用、租户、消息等粒度的指标。
- [由 @bowenliang123 提供的 Grafana 仪表板](https://github.com/bowenliang123/dify-grafana-dashboard)
## 使用 Helm Chart 或 Kubernetes 资源清单YAML部署
使用 [Helm Chart](https://helm.sh/) 版本或者 Kubernetes 资源清单YAML可以在 Kubernetes 上部署 Dify。
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML 文件 by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML 文件 (支持 Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### 使用 Terraform 部署
使用 [terraform](https://www.terraform.io/) 一键将 Dify 部署到云平台
#### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
### 使用 AWS CDK 部署
使用 [CDK](https://aws.amazon.com/cdk/) 将 Dify 部署到 AWS
#### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### 使用 阿里云计算巢 部署
使用 [阿里云计算巢](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88) 将 Dify 一键部署到 阿里云
### 使用 阿里云数据管理DMS 部署
使用 [阿里云数据管理DMS](https://help.aliyun.com/zh/dms/dify-in-invitational-preview) 将 Dify 一键部署到 阿里云
### 使用 Azure Devops Pipeline 部署到AKS
使用[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) 将 Dify 一键部署到 AKS
### 使用 Sealos 部署
通过 [Sealos App Store](https://sealos.io/products/app-store/dify/) 一键部署 Dify

View File

@ -108,7 +108,7 @@ Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI
在 GitHub 上给 Dify Star并立即收到新版本的通知。
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
<img width="1344" height="720" alt="star" src="https://github.com/user-attachments/assets/dcd086d1-af0f-471b-ae52-1ad2fa040595" />
## 安装社区版
@ -131,97 +131,37 @@ docker compose up -d
运行后,可以在浏览器上访问 [http://localhost/install](http://localhost/install) 进入 Dify 控制台并开始初始化安装操作。
### 自定义配
### 高级设
果您需要自定义配置,请参考 [.env.example](../../docker/.env.example) 文件中的注释,并更新 `.env` 文件中对应的值。此外,您可能需要根据您的具体部署环境和需求对 `docker-compose.yaml` 文件本身进行调整,例如更改镜像版本、端口映射或卷挂载。完成任何更改后,请重新运行 `docker-compose up -d`。您可以在[此处](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用环境变量的完整列表
需自定义配置、可观测性和部署选项,请参阅[高级设置](ADVANCED_SETUP.md)
### 使用 Grafana 进行指标监控
## 贡献
将仪表板导入 Grafana使用 Dify 的 PostgreSQL 数据库作为数据源,以监控应用、租户、消息等粒度的指标。
Dify 欢迎各种形式的贡献:
- [由 @bowenliang123 提供的 Grafana 仪表板](https://github.com/bowenliang123/dify-grafana-dashboard)
- **代码**:阅读[贡献指南](./CONTRIBUTING.md),然后浏览[适合新手的问题](https://github.com/langgenius/dify/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)。
- **想法与反馈**:发起或加入 [GitHub Discussions](https://github.com/langgenius/dify/discussions)。
- **翻译**:按照[国际化指南](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)添加或更新语言。
- **社区**:分享您构建的应用、帮助其他用户,并向更多人介绍 Dify。
#### 使用 Helm Chart 或 Kubernetes 资源清单YAML部署
使用 [Helm Chart](https://helm.sh/) 版本或者 Kubernetes 资源清单YAML可以在 Kubernetes 上部署 Dify。
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [Helm Chart by @magicsong](https://github.com/magicsong/ai-charts)
- [YAML 文件 by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
- [YAML file by @wyy-holding](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML 文件 (支持 Dify v1.6.0) by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
#### 使用 Terraform 部署
使用 [terraform](https://www.terraform.io/) 一键将 Dify 部署到云平台
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### 使用 AWS CDK 部署
使用 [CDK](https://aws.amazon.com/cdk/) 将 Dify 部署到 AWS
##### AWS
- [AWS CDK by @KevinZhao (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [AWS CDK by @tmokmss (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
#### 使用 阿里云计算巢 部署
使用 [阿里云计算巢](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88) 将 Dify 一键部署到 阿里云
#### 使用 阿里云数据管理DMS 部署
使用 [阿里云数据管理DMS](https://help.aliyun.com/zh/dms/dify-in-invitational-preview) 将 Dify 一键部署到 阿里云
#### 使用 Azure Devops Pipeline 部署到AKS
使用[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) 将 Dify 一键部署到 AKS
#### 使用 Sealos 部署
通过 [Sealos App Store](https://sealos.io/products/app-store/dify/) 一键部署 Dify
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
## Contributing
对于那些想要贡献代码的人,请参阅我们的[贡献指南](./CONTRIBUTING.md)。
同时,请考虑通过社交媒体、活动和会议来支持 Dify 的分享。
> 我们正在寻找贡献者来帮助将 Dify 翻译成除了中文和英文之外的其他语言。如果您有兴趣帮助,请参阅我们的[i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)获取更多信息,并在我们的[Discord 社区服务器](https://discord.gg/8Tpq4AcN9c)的`global-users`频道中留言。
**Contributors**
### 贡献者
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
<img alt="Dify contributors" src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## 社区与支持
我们欢迎您为 Dify 做出贡献,以帮助改善 Dify。包括提交代码、问题、新想法或分享您基于 Dify 创建的有趣且有用的 AI 应用程序。同时,我们也欢迎您在不同的活动、会议和社交媒体上分享 Dify。
请选择最适合您问题的渠道:
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). 👉:分享您的应用程序并与社区交流。
- [GitHub Issues](https://github.com/langgenius/dify/issues)。👉:使用 Dify.AI 时遇到的错误和问题,请参阅[贡献指南](./CONTRIBUTING.md)。
- [电子邮件支持](mailto:hello@dify.ai?subject=%5BGitHub%5DQuestions%20About%20Dify)。👉:关于使用 Dify.AI 的问题。
- [Discord](https://discord.gg/FngNHpbcY7)。👉:分享您的应用程序并与社区交流。
- [X(Twitter)](https://twitter.com/dify_ai)。👉:分享您的应用程序并与社区交流。
- [商业许可](mailto:business@dify.ai?subject=%5BGitHub%5DBusiness%20License%20Inquiry)。👉:有关商业用途许可 Dify.AI 的商业咨询。
- [GitHub Discussions](https://github.com/langgenius/dify/discussions):获取帮助、分享反馈并提出想法。
- [GitHub Issues](https://github.com/langgenius/dify/issues):报告可重现的错误并跟踪工程工作。创建问题前,请先阅读[贡献指南](./CONTRIBUTING.md)。
- [Discord](https://discord.gg/FngNHpbcY7):实时交流、分享您的应用,并与其他 Dify 用户互动。
- [X](https://x.com/dify_ai):关注版本新闻和项目更新。
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
## 安全问题
为了保护您的隐私,请避免在 GitHub 上发布安全问题。发送问题至 security@dify.ai我们将为您做更细致的解答。

View File

@ -0,0 +1,56 @@
# 進階設定
如果您需要自定義配置,請參考我們的 [.env.example](../../docker/.env.example) 文件中的註釋,並在您的 `.env` 文件中更新相應的值。此外,根據您特定的部署環境和需求,您可能需要調整 `docker-compose.yaml` 文件本身,例如更改映像版本、端口映射或卷掛載。進行任何更改後,請重新運行 `docker-compose up -d`。您可以在[這裡](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用環境變數的完整列表。
## 使用 Grafana 進行指標監控
將儀表板匯入 Grafana使用 Dify 的 PostgreSQL 資料庫作為資料來源,以監控應用程式、租戶、訊息等顆粒度的指標。
- [由 @bowenliang123 提供的 Grafana 儀表板](https://github.com/bowenliang123/dify-grafana-dashboard)
## 使用 Kubernetes 部署
如果您想配置高可用性設置,社區貢獻的 [Helm Charts](https://helm.sh/) 和 Kubernetes 資源清單YAML允許在 Kubernetes 上部署 Dify。
- [由 @LeoQuote 提供的 Helm Chart](https://github.com/douban/charts/tree/master/charts/dify)
- [由 @BorisPolonsky 提供的 Helm Chart](https://github.com/BorisPolonsky/dify-helm)
- [由 @Winson-030 提供的 YAML 文件](https://github.com/Winson-030/dify-kubernetes)
- [由 @wyy-holding 提供的 YAML 文件](https://github.com/wyy-holding/dify-k8s)
- [🚀 NEW! YAML 檔案(支援 Dify v1.6.0by @Zhoneym](https://github.com/Zhoneym/DifyAI-Kubernetes)
### 使用 Terraform 進行部署
使用 [terraform](https://www.terraform.io/) 一鍵部署 Dify 到雲端平台
#### Azure 全球
- [由 @nikawang 提供的 Azure Terraform](https://github.com/nikawang/dify-azure-terraform)
#### Google Cloud
- [由 @sotazum 提供的 Google Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform)
### 使用 AWS CDK 進行部署
使用 [CDK](https://aws.amazon.com/cdk/) 部署 Dify 到 AWS
#### AWS
- [由 @KevinZhao 提供的 AWS CDK (EKS based)](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
- [由 @tmokmss 提供的 AWS CDK (ECS based)](https://github.com/aws-samples/dify-self-hosted-on-aws)
### 使用 阿里云计算巢進行部署
[阿里云](https://computenest.console.aliyun.com/service/instance/create/default?type=user&ServiceName=Dify%E7%A4%BE%E5%8C%BA%E7%89%88)
### 使用 阿里雲數據管理DMS 進行部署
透過 [阿里雲數據管理DMS](https://www.alibabacloud.com/help/en/dms/dify-in-invitational-preview/),一鍵將 Dify 部署至阿里雲
### 使用 Azure Devops Pipeline 部署到AKS
使用[Azure Devops Pipeline Helm Chart by @LeoZhang](https://github.com/Ruiruiz30/Dify-helm-chart-AKS) 將 Dify 一鍵部署到 AKS
### 使用 Sealos 部署
透過 [Sealos App Store](https://sealos.io/products/app-store/dify/) 一鍵部署 Dify

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