Compare commits

..

14 Commits

Author SHA1 Message Date
37f7d5732a external knowledge api 2024-09-18 15:29:30 +08:00
dcb033d221 Merge branch 'main' into feat/external-knowledge
# Conflicts:
#	api/core/rag/datasource/retrieval_service.py
#	api/models/dataset.py
#	api/services/dataset_service.py
2024-09-18 14:40:43 +08:00
9f894bb3b3 external knowledge api 2024-09-18 14:36:51 +08:00
89e81873c4 merge error 2024-09-13 09:49:24 +08:00
9ca0e56a8a external dataset binding 2024-09-11 16:59:19 +08:00
e7c77d961b Merge branch 'main' into feat/external-knowledge
# Conflicts:
#	api/controllers/console/auth/data_source_oauth.py
2024-09-09 15:54:43 +08:00
a63e15081f update nltk version 2024-08-23 16:43:47 +08:00
0724640bbb fix rerank mode is none 2024-08-22 15:36:47 +08:00
cb70e12827 fix rerank mode is none 2024-08-22 15:33:43 +08:00
067b956b2c merge migration 2024-08-21 16:25:18 +08:00
e7762b731c external knowledge 2024-08-20 16:18:35 +08:00
f6c8390b0b external knowledge 2024-08-20 12:47:51 +08:00
4fd57929df Merge branch 'main' into feat/external-knowledge 2024-08-20 12:46:37 +08:00
517cdb2ca4 add external knowledge 2024-08-20 11:13:29 +08:00
2479 changed files with 24933 additions and 100809 deletions

View File

@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
cd api && poetry install poetry install -C api

View File

@ -1,36 +0,0 @@
name: Setup Poetry and Python
inputs:
python-version:
description: Python version to use and the Poetry installed with
required: true
default: '3.10'
poetry-version:
description: Poetry version to set up
required: true
default: '1.8.4'
poetry-lockfile:
description: Path to the Poetry lockfile to restore cache from
required: true
default: ''
runs:
using: composite
steps:
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: pip
- name: Install Poetry
shell: bash
run: pip install poetry==${{ inputs.poetry-version }}
- name: Restore Poetry cache
if: ${{ inputs.poetry-lockfile != '' }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: poetry
cache-dependency-path: ${{ inputs.poetry-lockfile }}

View File

@ -1,32 +1,34 @@
# Summary # Checklist:
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
> [!Tip]
> Close issue syntax: `Fixes #<issue number>` or `Resolves #<issue number>`, see [documentation](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) for more details.
# Screenshots
<table>
<tr>
<td>Before: </td>
<td>After: </td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
</table>
# Checklist
> [!IMPORTANT] > [!IMPORTANT]
> Please review the checklist below before submitting your pull request. > Please review the checklist below before submitting your pull request.
- [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs) - [ ] Please open an issue before creating a PR or link to an existing issue
- [x] I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!) - [ ] I have performed a self-review of my own code
- [x] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change. - [ ] I have commented my code, particularly in hard-to-understand areas
- [x] I've updated the documentation accordingly. - [ ] I ran `dev/reformat`(backend) and `cd web && npx lint-staged`(frontend) to appease the lint gods
- [x] I ran `dev/reformat`(backend) and `cd web && npx lint-staged`(frontend) to appease the lint gods
# Description
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. Close issue syntax: `Fixes #<issue number>`, see [documentation](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) for more details.
Fixes
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs)
- [ ] Improvement, including but not limited to code refactoring, performance optimization, and UI/UX improvement
- [ ] Dependency upgrade
# Testing Instructions
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
- [ ] Test A
- [ ] Test B

View File

@ -7,7 +7,6 @@ on:
paths: paths:
- api/** - api/**
- docker/** - docker/**
- .github/workflows/api-tests.yml
concurrency: concurrency:
group: api-tests-${{ github.head_ref || github.run_id }} group: api-tests-${{ github.head_ref || github.run_id }}
@ -28,13 +27,19 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Poetry and Python ${{ matrix.python-version }} - name: Install Poetry
uses: ./.github/actions/setup-poetry uses: abatilo/actions-poetry@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
poetry-lockfile: api/poetry.lock cache: 'poetry'
cache-dependency-path: |
api/pyproject.toml
api/poetry.lock
- name: Check Poetry lockfile - name: Poetry check
run: | run: |
poetry check -C api --lock poetry check -C api --lock
poetry show -C api poetry show -C api
@ -42,9 +47,6 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: poetry install -C api --with dev run: poetry install -C api --with dev
- name: Check dependencies in pyproject.toml
run: poetry run -C api bash dev/pytest/pytest_artifacts.sh
- name: Run Unit tests - name: Run Unit tests
run: poetry run -C api bash dev/pytest/pytest_unit_tests.sh run: poetry run -C api bash dev/pytest/pytest_unit_tests.sh
@ -63,7 +65,7 @@ jobs:
run: sh .github/workflows/expose_service_ports.sh run: sh .github/workflows/expose_service_ports.sh
- name: Set up Sandbox - name: Set up Sandbox
uses: hoverkraft-tech/compose-action@v2.0.2 uses: hoverkraft-tech/compose-action@v2.0.0
with: with:
compose-file: | compose-file: |
docker/docker-compose.middleware.yaml docker/docker-compose.middleware.yaml
@ -73,3 +75,21 @@ jobs:
- name: Run Workflow - name: Run Workflow
run: poetry run -C api bash dev/pytest/pytest_workflow.sh run: poetry run -C api bash dev/pytest/pytest_workflow.sh
- name: Set up Vector Stores (Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch)
uses: hoverkraft-tech/compose-action@v2.0.0
with:
compose-file: |
docker/docker-compose.yaml
services: |
weaviate
qdrant
etcd
minio
milvus-standalone
pgvecto-rs
pgvector
chroma
elasticsearch
- name: Test Vector Stores
run: poetry run -C api bash dev/pytest/pytest_vdb.sh

View File

@ -49,7 +49,7 @@ jobs:
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ env.DOCKERHUB_USER }} username: ${{ env.DOCKERHUB_USER }}
password: ${{ env.DOCKERHUB_TOKEN }} password: ${{ env.DOCKERHUB_TOKEN }}
@ -114,7 +114,7 @@ jobs:
merge-multiple: true merge-multiple: true
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ env.DOCKERHUB_USER }} username: ${{ env.DOCKERHUB_USER }}
password: ${{ env.DOCKERHUB_TOKEN }} password: ${{ env.DOCKERHUB_TOKEN }}
@ -125,7 +125,7 @@ jobs:
with: with:
images: ${{ env[matrix.image_name_env] }} images: ${{ env[matrix.image_name_env] }}
tags: | tags: |
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-') }} type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=ref,event=branch type=ref,event=branch
type=sha,enable=true,priority=100,prefix=,suffix=,format=long type=sha,enable=true,priority=100,prefix=,suffix=,format=long
type=raw,value=${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/tags/') }} type=raw,value=${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/tags/') }}

View File

@ -6,7 +6,6 @@ on:
- main - main
paths: paths:
- api/migrations/** - api/migrations/**
- .github/workflows/db-migration-test.yml
concurrency: concurrency:
group: db-migration-test-${{ github.ref }} group: db-migration-test-${{ github.ref }}
@ -15,15 +14,26 @@ concurrency:
jobs: jobs:
db-migration-test: db-migration-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Poetry and Python - name: Install Poetry
uses: ./.github/actions/setup-poetry uses: abatilo/actions-poetry@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with: with:
poetry-lockfile: api/poetry.lock python-version: ${{ matrix.python-version }}
cache: 'poetry'
cache-dependency-path: |
api/pyproject.toml
api/poetry.lock
- name: Install dependencies - name: Install dependencies
run: poetry install -C api run: poetry install -C api
@ -34,7 +44,7 @@ jobs:
cp middleware.env.example middleware.env cp middleware.env.example middleware.env
- name: Set up Middlewares - name: Set up Middlewares
uses: hoverkraft-tech/compose-action@v2.0.2 uses: hoverkraft-tech/compose-action@v2.0.0
with: with:
compose-file: | compose-file: |
docker/docker-compose.middleware.yaml docker/docker-compose.middleware.yaml

View File

@ -7,7 +7,5 @@ yq eval '.services["milvus-standalone"].ports += ["19530:19530"]' -i docker/dock
yq eval '.services.pgvector.ports += ["5433:5432"]' -i docker/docker-compose.yaml yq eval '.services.pgvector.ports += ["5433:5432"]' -i docker/docker-compose.yaml
yq eval '.services["pgvecto-rs"].ports += ["5431:5432"]' -i docker/docker-compose.yaml yq eval '.services["pgvecto-rs"].ports += ["5431:5432"]' -i docker/docker-compose.yaml
yq eval '.services["elasticsearch"].ports += ["9200:9200"]' -i docker/docker-compose.yaml yq eval '.services["elasticsearch"].ports += ["9200:9200"]' -i docker/docker-compose.yaml
yq eval '.services.couchbase-server.ports += ["8091-8096:8091-8096"]' -i docker/docker-compose.yaml
yq eval '.services.couchbase-server.ports += ["11210:11210"]' -i docker/docker-compose.yaml
echo "Ports exposed for sandbox, weaviate, qdrant, chroma, milvus, pgvector, pgvecto-rs, elasticsearch, couchbase" echo "Ports exposed for sandbox, weaviate, qdrant, chroma, milvus, pgvector, pgvecto-rs, elasticsearch"

View File

@ -22,28 +22,33 @@ jobs:
id: changed-files id: changed-files
uses: tj-actions/changed-files@v45 uses: tj-actions/changed-files@v45
with: with:
files: | files: api/**
api/**
.github/workflows/style.yml
- name: Setup Poetry and Python - name: Install Poetry
uses: abatilo/actions-poetry@v3
- name: Set up Python
uses: actions/setup-python@v5
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/actions/setup-poetry with:
python-version: '3.10'
- name: Install dependencies - name: Python dependencies
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
run: poetry install -C api --only lint run: poetry install -C api --only lint
- name: Ruff check - name: Ruff check
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
run: | run: poetry run -C api ruff check ./api
poetry run -C api ruff check ./api
poetry run -C api ruff format --check ./api
- name: Dotenv check - name: Dotenv check
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
run: poetry run -C api dotenv-linter ./api/.env.example ./web/.env.example run: poetry run -C api dotenv-linter ./api/.env.example ./web/.env.example
- name: Ruff formatter check
if: steps.changed-files.outputs.any_changed == 'true'
run: poetry run -C api ruff format --check ./api
- name: Lint hints - name: Lint hints
if: failure() if: failure()
run: echo "Please run 'dev/reformat' to fix the fixable linting errors." run: echo "Please run 'dev/reformat' to fix the fixable linting errors."

View File

@ -1,71 +0,0 @@
name: Run VDB Tests
on:
pull_request:
branches:
- main
paths:
- api/core/rag/datasource/**
- docker/**
- .github/workflows/vdb-tests.yml
concurrency:
group: vdb-tests-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
test:
name: VDB Tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Poetry and Python ${{ matrix.python-version }}
uses: ./.github/actions/setup-poetry
with:
python-version: ${{ matrix.python-version }}
poetry-lockfile: api/poetry.lock
- name: Check Poetry lockfile
run: |
poetry check -C api --lock
poetry show -C api
- name: Install dependencies
run: poetry install -C api --with dev
- name: Set up dotenvs
run: |
cp docker/.env.example docker/.env
cp docker/middleware.env.example docker/middleware.env
- name: Expose Service Ports
run: sh .github/workflows/expose_service_ports.sh
- name: Set up Vector Stores (Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch, Couchbase)
uses: hoverkraft-tech/compose-action@v2.0.2
with:
compose-file: |
docker/docker-compose.yaml
services: |
weaviate
qdrant
couchbase-server
etcd
minio
milvus-standalone
pgvecto-rs
pgvector
chroma
elasticsearch
- name: Test Vector Stores
run: poetry run -C api bash dev/pytest/pytest_vdb.sh

View File

@ -1,46 +0,0 @@
name: Web Tests
on:
pull_request:
branches:
- main
paths:
- web/**
concurrency:
group: web-tests-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
test:
name: Web Tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./web
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check changed files
id: changed-files
uses: tj-actions/changed-files@v45
with:
files: web/**
- name: Setup Node.js
uses: actions/setup-node@v4
if: steps.changed-files.outputs.any_changed == 'true'
with:
node-version: 20
cache: yarn
cache-dependency-path: ./web/package.json
- name: Install dependencies
if: steps.changed-files.outputs.any_changed == 'true'
run: yarn install --frozen-lockfile
- name: Run tests
if: steps.changed-files.outputs.any_changed == 'true'
run: yarn test

7
.gitignore vendored
View File

@ -173,13 +173,8 @@ docker/volumes/myscale/log/*
docker/volumes/unstructured/* docker/volumes/unstructured/*
docker/volumes/pgvector/data/* docker/volumes/pgvector/data/*
docker/volumes/pgvecto_rs/data/* docker/volumes/pgvecto_rs/data/*
docker/volumes/couchbase/*
docker/volumes/oceanbase/*
!docker/volumes/oceanbase/init.d
docker/nginx/conf.d/default.conf docker/nginx/conf.d/default.conf
docker/nginx/ssl/*
!docker/nginx/ssl/.gitkeep
docker/middleware.env docker/middleware.env
sdks/python-client/build sdks/python-client/build
@ -192,4 +187,4 @@ pyrightconfig.json
api/.vscode api/.vscode
.idea/ .idea/
.vscode .vscode

View File

@ -81,7 +81,7 @@ Dify requires the following dependencies to build, make sure they're installed o
Dify is composed of a backend and a frontend. Navigate to the backend directory by `cd api/`, then follow the [Backend README](api/README.md) to install it. In a separate terminal, navigate to the frontend directory by `cd web/`, then follow the [Frontend README](web/README.md) to install. Dify is composed of a backend and a frontend. Navigate to the backend directory by `cd api/`, then follow the [Backend README](api/README.md) to install it. In a separate terminal, navigate to the frontend directory by `cd web/`, then follow the [Frontend README](web/README.md) to install.
Check the [installation FAQ](https://docs.dify.ai/learn-more/faq/install-faq) for a list of common issues and steps to troubleshoot. Check the [installation FAQ](https://docs.dify.ai/learn-more/faq/self-host-faq) for a list of common issues and steps to troubleshoot.
### 5. Visit dify in your browser ### 5. Visit dify in your browser

View File

@ -36,7 +36,7 @@
| 被团队成员标记为高优先级的功能 | 高优先级 | | 被团队成员标记为高优先级的功能 | 高优先级 |
| 在 [community feedback board](https://github.com/langgenius/dify/discussions/categories/feedbacks) 内反馈的常见功能请求 | 中等优先级 | | 在 [community feedback board](https://github.com/langgenius/dify/discussions/categories/feedbacks) 内反馈的常见功能请求 | 中等优先级 |
| 非核心功能和小幅改进 | 低优先级 | | 非核心功能和小幅改进 | 低优先级 |
| 有价值不紧急 | 未来功能 | | 有价值不紧急 | 未来功能 |
### 其他任何事情(例如 bug 报告、性能优化、拼写错误更正): ### 其他任何事情(例如 bug 报告、性能优化、拼写错误更正):
* 立即开始编码。 * 立即开始编码。
@ -138,7 +138,7 @@ Dify 的后端使用 Python 编写,使用 [Flask](https://flask.palletsproject
├── models // 描述数据模型和 API 响应的形状 ├── models // 描述数据模型和 API 响应的形状
├── public // 如 favicon 等元资源 ├── public // 如 favicon 等元资源
├── service // 定义 API 操作的形状 ├── service // 定义 API 操作的形状
├── test ├── test
├── types // 函数参数和返回值的描述 ├── types // 函数参数和返回值的描述
└── utils // 共享的实用函数 └── utils // 共享的实用函数
``` ```

View File

@ -79,7 +79,7 @@ Dify yêu cầu các phụ thuộc sau để build, hãy đảm bảo chúng đ
Dify bao gồm một backend và một frontend. Đi đến thư mục backend bằng lệnh `cd api/`, sau đó làm theo hướng dẫn trong [README của Backend](api/README.md) để cài đặt. Trong một terminal khác, đi đến thư mục frontend bằng lệnh `cd web/`, sau đó làm theo hướng dẫn trong [README của Frontend](web/README.md) để cài đặt. Dify bao gồm một backend và một frontend. Đi đến thư mục backend bằng lệnh `cd api/`, sau đó làm theo hướng dẫn trong [README của Backend](api/README.md) để cài đặt. Trong một terminal khác, đi đến thư mục frontend bằng lệnh `cd web/`, sau đó làm theo hướng dẫn trong [README của Frontend](web/README.md) để cài đặt.
Kiểm tra [FAQ về cài đặt](https://docs.dify.ai/learn-more/faq/install-faq) để xem danh sách các vấn đề thường gặp và các bước khắc phục. Kiểm tra [FAQ về cài đặt](https://docs.dify.ai/learn-more/faq/self-host-faq) để xem danh sách các vấn đề thường gặp và các bước khắc phục.
### 5. Truy cập Dify trong trình duyệt của bạn ### 5. Truy cập Dify trong trình duyệt của bạn

View File

@ -6,9 +6,8 @@ Dify is licensed under the Apache License 2.0, with the following additional con
a. Multi-tenant service: Unless explicitly authorized by Dify in writing, you may not use the Dify source code to operate a multi-tenant environment. a. Multi-tenant service: Unless explicitly authorized by Dify in writing, you may not use the Dify source code to operate a multi-tenant environment.
- Tenant Definition: Within the context of Dify, one tenant corresponds to one workspace. The workspace provides a separated area for each tenant's data and configurations. - Tenant Definition: Within the context of Dify, one tenant corresponds to one workspace. The workspace provides a separated area for each tenant's data and configurations.
b. LOGO and copyright information: In the process of using Dify's frontend, you may not remove or modify the LOGO or copyright information in the Dify console or applications. This restriction is inapplicable to uses of Dify that do not involve its frontend. b. LOGO and copyright information: In the process of using Dify's frontend components, you may not remove or modify the LOGO or copyright information in the Dify console or applications. This restriction is inapplicable to uses of Dify that do not involve its frontend components.
- Frontend Definition: For the purposes of this license, the "frontend" of Dify includes all components located in the `web/` directory when running Dify from the raw source code, or the "web" image when running Dify with Docker.
Please contact business@dify.ai by email to inquire about licensing matters. Please contact business@dify.ai by email to inquire about licensing matters.

148
README.md
View File

@ -1,9 +1,5 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab) ![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
<p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introducing Dify Workflow File Upload: Recreate Google NotebookLM Podcast</a>
</p>
<p align="center"> <p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> · <a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Self-hosting</a> · <a href="https://docs.dify.ai/getting-started/install-self-hosted">Self-hosting</a> ·
@ -19,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a> alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="join Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a> alt="follow on Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -49,33 +42,9 @@
</p> </p>
Dify is an open-source LLM app development platform. Its intuitive interface combines agentic AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production. Dify is an open-source LLM app development platform. Its intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production. Here's a list of the core features:
</br> </br>
## Quick start
> Before installing Dify, make sure your machine meets the following minimum system requirements:
>
>- CPU >= 2 Core
>- RAM >= 4 GiB
</br>
The easiest way to start the Dify server is through [docker compose](docker/docker-compose.yaml). Before running Dify with the following commands, make sure that [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed on your machine:
```bash
cd dify
cd docker
cp .env.example .env
docker compose up -d
```
After running, you can access the Dify dashboard in your browser at [http://localhost/install](http://localhost/install) and start the initialization process.
#### Seeking help
Please refer to our [FAQ](https://docs.dify.ai/getting-started/install-self-hosted/faqs) if you encounter problems setting up Dify. Reach out to [the community and us](#community--contact) if you are still having issues.
> If you'd like to contribute to Dify or do additional development, refer to our [guide to deploying from source code](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code)
## Key features
**1. Workflow**: **1. Workflow**:
Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond. Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond.
@ -106,6 +75,73 @@ Please refer to our [FAQ](https://docs.dify.ai/getting-started/install-self-host
All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic. All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic.
## Feature comparison
<table style="width: 100%;">
<tr>
<th align="center">Feature</th>
<th align="center">Dify.AI</th>
<th align="center">LangChain</th>
<th align="center">Flowise</th>
<th align="center">OpenAI Assistants API</th>
</tr>
<tr>
<td align="center">Programming Approach</td>
<td align="center">API + App-oriented</td>
<td align="center">Python Code</td>
<td align="center">App-oriented</td>
<td align="center">API-oriented</td>
</tr>
<tr>
<td align="center">Supported LLMs</td>
<td align="center">Rich Variety</td>
<td align="center">Rich Variety</td>
<td align="center">Rich Variety</td>
<td align="center">OpenAI-only</td>
</tr>
<tr>
<td align="center">RAG Engine</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Agent</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Workflow</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Observability</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Enterprise Features (SSO/Access control)</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Local Deployment</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</table>
## Using Dify ## Using Dify
- **Cloud </br>** - **Cloud </br>**
@ -127,7 +163,28 @@ 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) ![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
## Advanced Setup
## Quick start
> Before installing Dify, make sure your machine meets the following minimum system requirements:
>
>- CPU >= 2 Core
>- RAM >= 4GB
</br>
The easiest way to start the Dify server is to run our [docker-compose.yml](docker/docker-compose.yaml) file. Before running the installation command, make sure that [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed on your machine:
```bash
cd docker
cp .env.example .env
docker compose up -d
```
After running, you can access the Dify dashboard in your browser at [http://localhost/install](http://localhost/install) and start the initialization process.
> If you'd like to contribute to Dify or do additional development, refer to our [guide to deploying from source code](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code)
## 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 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).
@ -139,14 +196,10 @@ If you'd like to configure a highly-available setup, there are community-contrib
#### Using Terraform for Deployment #### Using Terraform for Deployment
Deploy Dify to Cloud Platform with a single click using [terraform](https://www.terraform.io/)
##### Azure Global ##### Azure Global
Deploy Dify to Azure with a single click using [terraform](https://www.terraform.io/).
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform) - [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)
## Contributing ## Contributing
For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
@ -155,19 +208,19 @@ At the same time, please consider supporting Dify by sharing it on social media
> 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/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c). > 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/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c).
## 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.
**Contributors** **Contributors**
<a href="https://github.com/langgenius/dify/graphs/contributors"> <a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" /> <img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a> </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.
* [Twitter](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
## Star history ## Star history
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date) [![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
@ -180,4 +233,3 @@ To protect your privacy, please avoid posting security issues on GitHub. Instead
## License ## License
This repository is available under the [Dify Open Source License](LICENSE), which is essentially Apache 2.0 with a few additional restrictions. This repository is available under the [Dify Open Source License](LICENSE), which is essentially Apache 2.0 with a few additional restrictions.

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a> alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="join Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a> alt="follow on Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -182,13 +179,10 @@ docker compose up -d
#### استخدام Terraform للتوزيع #### استخدام Terraform للتوزيع
انشر Dify إلى منصة السحابة بنقرة واحدة باستخدام [terraform](https://www.terraform.io/)
##### Azure Global ##### Azure Global
استخدم [terraform](https://www.terraform.io/) لنشر Dify على Azure بنقرة واحدة.
- [Azure Terraform بواسطة @nikawang](https://github.com/nikawang/dify-azure-terraform) - [Azure Terraform بواسطة @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform بواسطة @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
## المساهمة ## المساهمة

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a> alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="join Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a> alt="follow on Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -157,7 +154,7 @@ Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI
我们提供[ Dify 云服务](https://dify.ai),任何人都可以零设置尝试。它提供了自部署版本的所有功能,并在沙盒计划中包含 200 次免费的 GPT-4 调用。 我们提供[ Dify 云服务](https://dify.ai),任何人都可以零设置尝试。它提供了自部署版本的所有功能,并在沙盒计划中包含 200 次免费的 GPT-4 调用。
- **自托管 Dify 社区版</br>** - **自托管 Dify 社区版</br>**
使用这个[入门指南](#快速启动)快速在您的环境中运行 Dify。 使用这个[入门指南](#quick-start)快速在您的环境中运行 Dify。
使用我们的[文档](https://docs.dify.ai)进行进一步的参考和更深入的说明。 使用我们的[文档](https://docs.dify.ai)进行进一步的参考和更深入的说明。
- **面向企业/组织的 Dify</br>** - **面向企业/组织的 Dify</br>**
@ -177,7 +174,7 @@ Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI
在安装 Dify 之前,请确保您的机器满足以下最低系统要求: 在安装 Dify 之前,请确保您的机器满足以下最低系统要求:
- CPU >= 2 Core - CPU >= 2 Core
- RAM >= 4 GiB - RAM >= 4GB
### 快速启动 ### 快速启动
@ -205,14 +202,10 @@ docker compose up -d
#### 使用 Terraform 部署 #### 使用 Terraform 部署
使用 [terraform](https://www.terraform.io/) 一键将 Dify 部署到云平台
##### Azure Global ##### Azure Global
使用 [terraform](https://www.terraform.io/) 一键部署 Dify 到 Azure。
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform) - [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)
## Star History ## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date) [![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
@ -239,7 +232,7 @@ docker compose up -d
- [GitHub Issues](https://github.com/langgenius/dify/issues)。👉:使用 Dify.AI 时遇到的错误和问题,请参阅[贡献指南](CONTRIBUTING.md)。 - [GitHub Issues](https://github.com/langgenius/dify/issues)。👉:使用 Dify.AI 时遇到的错误和问题,请参阅[贡献指南](CONTRIBUTING.md)。
- [电子邮件支持](mailto:hello@dify.ai?subject=[GitHub]Questions%20About%20Dify)。👉:关于使用 Dify.AI 的问题。 - [电子邮件支持](mailto:hello@dify.ai?subject=[GitHub]Questions%20About%20Dify)。👉:关于使用 Dify.AI 的问题。
- [Discord](https://discord.gg/FngNHpbcY7)。👉:分享您的应用程序并与社区交流。 - [Discord](https://discord.gg/FngNHpbcY7)。👉:分享您的应用程序并与社区交流。
- [X(Twitter)](https://twitter.com/dify_ai)。👉:分享您的应用程序并与社区交流。 - [Twitter](https://twitter.com/dify_ai)。👉:分享您的应用程序并与社区交流。
- [商业许可](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry)。👉:有关商业用途许可 Dify.AI 的商业咨询。 - [商业许可](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry)。👉:有关商业用途许可 Dify.AI 的商业咨询。
- [微信]() 👉:扫描下方二维码,添加微信好友,备注 Dify我们将邀请您加入 Dify 社区。 - [微信]() 👉:扫描下方二维码,添加微信好友,备注 Dify我们将邀请您加入 Dify 社区。
<img src="./images/wechat.png" alt="wechat" width="100"/> <img src="./images/wechat.png" alt="wechat" width="100"/>

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat en Discord"></a> alt="chat en Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="join Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="seguir en X(Twitter)"></a> alt="seguir en Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Descargas de Docker" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Descargas de Docker" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -207,13 +204,10 @@ Si desea configurar una configuración de alta disponibilidad, la comunidad prop
#### Uso de Terraform para el despliegue #### 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 Global
Utiliza [terraform](https://www.terraform.io/) para desplegar Dify en Azure con un solo clic.
- [Azure Terraform por @nikawang](https://github.com/nikawang/dify-azure-terraform) - [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)
## Contribuir ## Contribuir
@ -234,7 +228,7 @@ Al mismo tiempo, considera apoyar a Dify compartiéndolo en redes sociales y en
* [Discusión en GitHub](https://github.com/langgenius/dify/discussions). Lo mejor para: compartir comentarios y hacer preguntas. * [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](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). * [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](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
* [Discord](https://discord.gg/FngNHpbcY7). Lo mejor para: compartir tus aplicaciones y pasar el rato con la comunidad. * [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. * [Twitter](https://twitter.com/dify_ai). Lo mejor para: compartir tus aplicaciones y pasar el rato con la comunidad.
## Historial de Estrellas ## Historial de Estrellas

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat sur Discord"></a> alt="chat sur Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="join Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="suivre sur X(Twitter)"></a> alt="suivre sur Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Tirages Docker" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Tirages Docker" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -205,13 +202,10 @@ Si vous souhaitez configurer une configuration haute disponibilité, la communau
#### Utilisation de Terraform pour le déploiement #### 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 Global
Utilisez [terraform](https://www.terraform.io/) pour déployer Dify sur Azure en un clic.
- [Azure Terraform par @nikawang](https://github.com/nikawang/dify-azure-terraform) - [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)
## Contribuer ## Contribuer
@ -232,7 +226,7 @@ Dans le même temps, veuillez envisager de soutenir Dify en le partageant sur le
* [Discussion GitHub](https://github.com/langgenius/dify/discussions). Meilleur pour: partager des commentaires et poser des questions. * [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](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). * [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](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
* [Discord](https://discord.gg/FngNHpbcY7). Meilleur pour: partager vos applications et passer du temps avec la communauté. * [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é. * [Twitter](https://twitter.com/dify_ai). Meilleur pour: partager vos applications et passer du temps avec la communauté.
## Historique des étoiles ## Historique des étoiles

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="Discordでチャット"></a> alt="Discordでチャット"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="X(Twitter)でフォロー"></a> alt="Twitterでフォロー"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -71,7 +68,7 @@ DifyはオープンソースのLLMアプリケーション開発プラットフ
プロンプトの作成、モデルパフォーマンスの比較が行え、チャットベースのアプリに音声合成などの機能も追加できます。 プロンプトの作成、モデルパフォーマンスの比較が行え、チャットベースのアプリに音声合成などの機能も追加できます。
**4. RAGパイプライン**: **4. RAGパイプライン**:
ドキュメントの取り込みから検索までをカバーする広範なRAG機能ができます。ほかにもPDF、PPT、その他の一般的なドキュメントフォーマットからのテキスト抽出のサートも提供します。 ドキュメントの取り込みから検索までをカバーする広範なRAG機能ができます。ほかにもPDF、PPT、その他の一般的なドキュメントフォーマットからのテキスト抽出のサーポイントも提供します。
**5. エージェント機能**: **5. エージェント機能**:
LLM Function CallingやReActに基づくエージェントの定義が可能で、AIエージェント用のプリビルトまたはカスタムツールを追加できます。Difyには、Google検索、DALL·E、Stable Diffusion、WolframAlphaなどのAIエージェント用の50以上の組み込みツールが提供します。 LLM Function CallingやReActに基づくエージェントの定義が可能で、AIエージェント用のプリビルトまたはカスタムツールを追加できます。Difyには、Google検索、DALL·E、Stable Diffusion、WolframAlphaなどのAIエージェント用の50以上の組み込みツールが提供します。
@ -204,13 +201,10 @@ docker compose up -d
#### Terraformを使用したデプロイ #### Terraformを使用したデプロイ
[terraform](https://www.terraform.io/) を使用して、ワンクリックでDifyをクラウドプラットフォームにデプロイします
##### Azure Global ##### Azure Global
- [@nikawangによるAzure Terraform](https://github.com/nikawang/dify-azure-terraform) [terraform](https://www.terraform.io/) を使用して、AzureにDifyをワンクリックでデプロイします。
- [nikawangのAzure Terraform](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [@sotazumによるGoogle Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform)
## 貢献 ## 貢献
@ -231,7 +225,7 @@ docker compose up -d
* [Github Discussion](https://github.com/langgenius/dify/discussions). 主に: フィードバックの共有や質問。 * [Github Discussion](https://github.com/langgenius/dify/discussions). 主に: フィードバックの共有や質問。
* [GitHub Issues](https://github.com/langgenius/dify/issues). 主に: Dify.AIを使用する際に発生するエラーや問題については、[貢献ガイド](CONTRIBUTING_JA.md)を参照してください * [GitHub Issues](https://github.com/langgenius/dify/issues). 主に: Dify.AIを使用する際に発生するエラーや問題については、[貢献ガイド](CONTRIBUTING_JA.md)を参照してください
* [Discord](https://discord.gg/FngNHpbcY7). 主に: アプリケーションの共有やコミュニティとの交流。 * [Discord](https://discord.gg/FngNHpbcY7). 主に: アプリケーションの共有やコミュニティとの交流。
* [X(Twitter)](https://twitter.com/dify_ai). 主に: アプリケーションの共有やコミュニティとの交流。 * [Twitter](https://twitter.com/dify_ai). 主に: アプリケーションの共有やコミュニティとの交流。

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a> alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="Follow Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a> alt="follow on Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -205,13 +202,10 @@ If you'd like to configure a highly-available setup, there are community-contrib
#### Terraform atorlugu pilersitsineq #### Terraform atorlugu pilersitsineq
wa'logh nIqHom neH ghun deployment toy'wI' [terraform](https://www.terraform.io/) lo'laH.
##### Azure Global ##### Azure Global
- [Azure Terraform mung @nikawang](https://github.com/nikawang/dify-azure-terraform) Atoruk [terraform](https://www.terraform.io/) Dify-mik Azure-mut ataatsikkut ikkussuilluarlugu.
- [Azure Terraform atorlugu @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform qachlot @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
## Contributing ## Contributing
@ -234,7 +228,7 @@ At the same time, please consider supporting Dify by sharing it on social media
). Best for: sharing feedback and asking questions. ). 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). * [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. * [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. * [Twitter](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
## Star History ## Star History

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a> alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="Follow Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a> alt="follow on Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -42,6 +39,7 @@
<a href="./README_AR.md"><img alt="README بالعربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a> <a href="./README_AR.md"><img alt="README بالعربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
<a href="./README_TR.md"><img alt="Türkçe README" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a> <a href="./README_TR.md"><img alt="Türkçe README" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
<a href="./README_VI.md"><img alt="README Tiếng Việt" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a> <a href="./README_VI.md"><img alt="README Tiếng Việt" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a>
</p> </p>
@ -197,14 +195,10 @@ Dify를 Kubernetes에 배포하고 프리미엄 스케일링 설정을 구성했
#### Terraform을 사용한 배포 #### Terraform을 사용한 배포
[terraform](https://www.terraform.io/)을 사용하여 단 한 번의 클릭으로 Dify를 클라우드 플랫폼에 배포하십시오
##### Azure Global ##### Azure Global
[terraform](https://www.terraform.io/)을 사용하여 Azure에 Dify를 원클릭으로 배포하세요.
- [nikawang의 Azure Terraform](https://github.com/nikawang/dify-azure-terraform) - [nikawang의 Azure Terraform](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [sotazum의 Google Cloud Terraform](https://github.com/DeNA/dify-google-cloud-terraform)
## 기여 ## 기여
코드에 기여하고 싶은 분들은 [기여 가이드](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)를 참조하세요. 코드에 기여하고 싶은 분들은 [기여 가이드](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)를 참조하세요.

View File

@ -1,244 +0,0 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
<p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introduzindo o Dify Workflow com Upload de Arquivo: Recrie o Podcast Google NotebookLM</a>
</p>
<p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Auto-hospedagem</a> ·
<a href="https://docs.dify.ai">Documentação</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Consultas empresariais</a>
</p>
<p align="center">
<a href="https://dify.ai" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/Product-F04438"></a>
<a href="https://dify.ai/pricing" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/free-pricing?logo=free&color=%20%23155EEF&label=pricing&labelColor=%20%23528bff"></a>
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="Follow Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
<img alt="Commits last month" src="https://img.shields.io/github/commit-activity/m/langgenius/dify?labelColor=%20%2332b583&color=%20%2312b76a"></a>
<a href="https://github.com/langgenius/dify/" target="_blank">
<img alt="Issues closed" src="https://img.shields.io/github/issues-search?query=repo%3Alanggenius%2Fdify%20is%3Aclosed&label=issues%20closed&labelColor=%20%237d89b0&color=%20%235d6b98"></a>
<a href="https://github.com/langgenius/dify/discussions/" target="_blank">
<img alt="Discussion posts" src="https://img.shields.io/github/discussions/langgenius/dify?labelColor=%20%239b8afb&color=%20%237a5af8"></a>
</p>
<p align="center">
<a href="./README.md"><img alt="README em Inglês" src="https://img.shields.io/badge/English-d9d9d9"></a>
<a href="./README_CN.md"><img alt="简体中文版自述文件" src="https://img.shields.io/badge/简体中文-d9d9d9"></a>
<a href="./README_JA.md"><img alt="日本語のREADME" src="https://img.shields.io/badge/日本語-d9d9d9"></a>
<a href="./README_ES.md"><img alt="README em Espanhol" src="https://img.shields.io/badge/Español-d9d9d9"></a>
<a href="./README_FR.md"><img alt="README em Francês" src="https://img.shields.io/badge/Français-d9d9d9"></a>
<a href="./README_KL.md"><img alt="README tlhIngan Hol" src="https://img.shields.io/badge/Klingon-d9d9d9"></a>
<a href="./README_KR.md"><img alt="README em Coreano" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
<a href="./README_AR.md"><img alt="README em Árabe" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
<a href="./README_TR.md"><img alt="README em Turco" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
<a href="./README_VI.md"><img alt="README em Vietnamita" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a>
<a href="./README_PT.md"><img alt="README em Português - BR" src="https://img.shields.io/badge/Portugu%C3%AAs-BR?style=flat&label=BR&color=d9d9d9"></a>
</p>
Dify é uma plataforma de desenvolvimento de aplicativos LLM de código aberto. Sua interface intuitiva combina workflow de IA, pipeline RAG, capacidades de agente, gerenciamento de modelos, recursos de observabilidade e muito mais, permitindo que você vá rapidamente do protótipo à produção. Aqui está uma lista das principais funcionalidades:
</br> </br>
**1. Workflow**:
Construa e teste workflows poderosos de IA em uma interface visual, aproveitando todos os recursos a seguir e muito mais.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Suporte abrangente a modelos**:
Integração perfeita com centenas de LLMs proprietários e de código aberto de diversas provedoras e soluções auto-hospedadas, abrangendo GPT, Mistral, Llama3 e qualquer modelo compatível com a API da OpenAI. A lista completa de provedores suportados pode ser encontrada [aqui](https://docs.dify.ai/getting-started/readme/model-providers).
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)
**3. IDE de Prompt**:
Interface intuitiva para criação de prompts, comparação de desempenho de modelos e adição de recursos como conversão de texto para fala em um aplicativo baseado em chat.
**4. Pipeline RAG**:
Extensas capacidades de RAG que cobrem desde a ingestão de documentos até a recuperação, com suporte nativo para extração de texto de PDFs, PPTs e outros formatos de documentos comuns.
**5. Capacidades de agente**:
Você pode definir agentes com base em LLM Function Calling ou ReAct e adicionar ferramentas pré-construídas ou personalizadas para o agente. O Dify oferece mais de 50 ferramentas integradas para agentes de IA, como Google Search, DALL·E, Stable Diffusion e WolframAlpha.
**6. LLMOps**:
Monitore e analise os registros e o desempenho do aplicativo ao longo do tempo. É possível melhorar continuamente prompts, conjuntos de dados e modelos com base nos dados de produção e anotações.
**7. Backend como Serviço**:
Todas os recursos do Dify vêm com APIs correspondentes, permitindo que você integre o Dify sem esforço na lógica de negócios da sua empresa.
## Comparação de recursos
<table style="width: 100%;">
<tr>
<th align="center">Recurso</th>
<th align="center">Dify.AI</th>
<th align="center">LangChain</th>
<th align="center">Flowise</th>
<th align="center">OpenAI Assistants API</th>
</tr>
<tr>
<td align="center">Abordagem de Programação</td>
<td align="center">Orientada a API + Aplicativo</td>
<td align="center">Código Python</td>
<td align="center">Orientada a Aplicativo</td>
<td align="center">Orientada a API</td>
</tr>
<tr>
<td align="center">LLMs Suportados</td>
<td align="center">Variedade Rica</td>
<td align="center">Variedade Rica</td>
<td align="center">Variedade Rica</td>
<td align="center">Apenas OpenAI</td>
</tr>
<tr>
<td align="center">RAG Engine</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Agente</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Workflow</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Observabilidade</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Recursos Empresariais (SSO/Controle de Acesso)</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Implantação Local</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</table>
## Usando o Dify
- **Nuvem </br>**
Oferecemos o serviço [Dify Cloud](https://dify.ai) para qualquer pessoa experimentar sem nenhuma configuração. Ele fornece todas as funcionalidades da versão auto-hospedada, incluindo 200 chamadas GPT-4 gratuitas no plano sandbox.
- **Auto-hospedagem do Dify Community Edition</br>**
Configure rapidamente o Dify no seu ambiente com este [guia inicial](#quick-start).
Use nossa [documentação](https://docs.dify.ai) para referências adicionais e instruções mais detalhadas.
- **Dify para empresas/organizações</br>**
Oferecemos recursos adicionais voltados para empresas. [Envie suas perguntas através deste chatbot](https://udify.app/chat/22L1zSxg6yW1cWQg) ou [envie-nos um e-mail](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry) para discutir necessidades empresariais. </br>
> Para startups e pequenas empresas que utilizam AWS, confira o [Dify Premium no AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) e implemente no seu próprio AWS VPC com um clique. É uma oferta AMI acessível com a opção de criar aplicativos com logotipo e marca personalizados.
## Mantendo-se atualizado
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)
## Início rápido
> Antes de instalar o Dify, certifique-se de que sua máquina atenda aos seguintes requisitos mínimos de sistema:
>
>- CPU >= 2 Núcleos
>- RAM >= 4 GiB
</br>
A maneira mais fácil de iniciar o servidor Dify é executar nosso arquivo [docker-compose.yml](docker/docker-compose.yaml). Antes de rodar o comando de instalação, certifique-se de que o [Docker](https://docs.docker.com/get-docker/) e o [Docker Compose](https://docs.docker.com/compose/install/) estão instalados na sua máquina:
```bash
cd docker
cp .env.example .env
docker compose up -d
```
Após a execução, você pode acessar o painel do Dify no navegador em [http://localhost/install](http://localhost/install) e iniciar o processo de inicialização.
> Se você deseja contribuir com o Dify ou fazer desenvolvimento adicional, consulte nosso [guia para implantar a partir do código fonte](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code).
## 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).
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)
- [Arquivo YAML de @Winson-030](https://github.com/Winson-030/dify-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)
## Contribuindo
Para aqueles que desejam contribuir com código, veja nosso [Guia de Contribuição](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
Ao mesmo tempo, considere apoiar o Dify compartilhando-o nas redes sociais e em eventos e conferências.
> 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/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).
**Contribuidores**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img 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](https://github.com/langgenius/dify/blob/main/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.
## 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)
## Divulgação de segurança
Para proteger sua privacidade, evite postar problemas de segurança no GitHub. Em vez disso, envie suas perguntas para security@dify.ai e forneceremos uma resposta mais detalhada.
## Licença
Este repositório está disponível sob a [Licença de Código Aberto Dify](LICENSE), que é essencialmente Apache 2.0 com algumas restrições adicionais.

View File

@ -1,180 +0,0 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
<p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Predstavljamo nalaganje datotek Dify Workflow: znova ustvarite Google NotebookLM Podcast</a>
</p>
<p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Samostojno gostovanje</a> ·
<a href="https://docs.dify.ai">Dokumentacija</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Povpraševanje za podjetja</a>
</p>
<p align="center">
<a href="https://dify.ai" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/Product-F04438"></a>
<a href="https://dify.ai/pricing" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/free-pricing?logo=free&color=%20%23155EEF&label=pricing&labelColor=%20%23528bff"></a>
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
<img alt="Commits last month" src="https://img.shields.io/github/commit-activity/m/langgenius/dify?labelColor=%20%2332b583&color=%20%2312b76a"></a>
<a href="https://github.com/langgenius/dify/" target="_blank">
<img alt="Issues closed" src="https://img.shields.io/github/issues-search?query=repo%3Alanggenius%2Fdify%20is%3Aclosed&label=issues%20closed&labelColor=%20%237d89b0&color=%20%235d6b98"></a>
<a href="https://github.com/langgenius/dify/discussions/" target="_blank">
<img alt="Discussion posts" src="https://img.shields.io/github/discussions/langgenius/dify?labelColor=%20%239b8afb&color=%20%237a5af8"></a>
</p>
<p align="center">
<a href="./README.md"><img alt="README in English" src="https://img.shields.io/badge/English-d9d9d9"></a>
<a href="./README_CN.md"><img alt="简体中文版自述文件" src="https://img.shields.io/badge/简体中文-d9d9d9"></a>
<a href="./README_JA.md"><img alt="日本語のREADME" src="https://img.shields.io/badge/日本語-d9d9d9"></a>
<a href="./README_ES.md"><img alt="README en Español" src="https://img.shields.io/badge/Español-d9d9d9"></a>
<a href="./README_FR.md"><img alt="README en Français" src="https://img.shields.io/badge/Français-d9d9d9"></a>
<a href="./README_KL.md"><img alt="README tlhIngan Hol" src="https://img.shields.io/badge/Klingon-d9d9d9"></a>
<a href="./README_KR.md"><img alt="README in Korean" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
<a href="./README_AR.md"><img alt="README بالعربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
<a href="./README_TR.md"><img alt="Türkçe README" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
<a href="./README_VI.md"><img alt="README Tiếng Việt" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a>
<a href="./README_SI.md"><img alt="README Slovenščina" src="https://img.shields.io/badge/Sloven%C5%A1%C4%8Dina-d9d9d9"></a>
</p>
Dify je odprtokodna platforma za razvoj aplikacij LLM. Njegov intuitivni vmesnik združuje agentski potek dela z umetno inteligenco, cevovod RAG, zmogljivosti agentov, upravljanje modelov, funkcije opazovanja in več, kar vam omogoča hiter prehod od prototipa do proizvodnje.
## Hitri začetek
> Preden namestite Dify, se prepričajte, da vaša naprava izpolnjuje naslednje minimalne sistemske zahteve:
>
>- CPU >= 2 Core
>- RAM >= 4 GiB
</br>
Najlažji način za zagon strežnika Dify je prek docker compose . Preden zaženete Dify z naslednjimi ukazi, se prepričajte, da sta Docker in Docker Compose nameščena na vašem računalniku:
```bash
cd dify
cd docker
cp .env.example .env
docker compose up -d
```
Po zagonu lahko dostopate do nadzorne plošče Dify v brskalniku na [http://localhost/install](http://localhost/install) in začnete postopek inicializacije.
#### Iskanje pomoči
Prosimo, glejte naša pogosta vprašanja [FAQ](https://docs.dify.ai/getting-started/install-self-hosted/faqs) če naletite na težave pri nastavitvi Dify. Če imate še vedno težave, se obrnite na [skupnost ali nas](#community--contact).
> Če želite prispevati k Difyju ali narediti dodaten razvoj, glejte naš vodnik za [uvajanje iz izvorne kode](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code)
## Ključne značilnosti
**1. Potek dela**:
Zgradite in preizkusite zmogljive poteke dela AI na vizualnem platnu, pri čemer izkoristite vse naslednje funkcije in več.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Celovita podpora za modele**:
Brezhibna integracija s stotinami lastniških/odprtokodnih LLM-jev ducatov ponudnikov sklepanja in samostojnih rešitev, ki pokrivajo GPT, Mistral, Llama3 in vse modele, združljive z API-jem OpenAI. Celoten seznam podprtih ponudnikov modelov najdete [tukaj](https://docs.dify.ai/getting-started/readme/model-providers).
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)
**3. Prompt IDE**:
intuitivni vmesnik za ustvarjanje pozivov, primerjavo zmogljivosti modela in dodajanje dodatnih funkcij, kot je pretvorba besedila v govor, aplikaciji, ki temelji na klepetu.
**4. RAG Pipeline**:
E Obsežne zmogljivosti RAG, ki pokrivajo vse od vnosa dokumenta do priklica, s podporo za ekstrakcijo besedila iz datotek PDF, PPT in drugih običajnih formatov dokumentov.
**5. Agent capabilities**:
definirate lahko agente, ki temeljijo na klicanju funkcij LLM ali ReAct, in dodate vnaprej izdelana orodja ali orodja po meri za agenta. Dify ponuja več kot 50 vgrajenih orodij za agente AI, kot so Google Search, DALL·E, Stable Diffusion in WolframAlpha.
**6. LLMOps**:
Spremljajte in analizirajte dnevnike aplikacij in učinkovitost skozi čas. Pozive, nabore podatkov in modele lahko nenehno izboljšujete na podlagi proizvodnih podatkov in opomb.
**7. Backend-as-a-Service**:
AVse ponudbe Difyja so opremljene z ustreznimi API-ji, tako da lahko Dify brez težav integrirate v svojo poslovno logiko.
## Uporaba Dify
- **Cloud </br>**
Gostimo storitev Dify Cloud za vsakogar, ki jo lahko preizkusite brez nastavitev. Zagotavlja vse zmožnosti različice za samostojno namestitev in vključuje 200 brezplačnih klicev GPT-4 v načrtu peskovnika.
- **Self-hosting Dify Community Edition</br>**
Hitro zaženite Dify v svojem okolju s tem [začetnim vodnikom](#quick-start) . Za dodatne reference in podrobnejša navodila uporabite našo [dokumentacijo](https://docs.dify.ai) .
- **Dify za podjetja/organizacije</br>**
Ponujamo dodatne funkcije, osredotočene na podjetja. Zabeležite svoja vprašanja prek tega klepetalnega robota ali nam pošljite e-pošto, da se pogovorimo o potrebah podjetja. </br>
> Za novoustanovljena podjetja in mala podjetja, ki uporabljajo AWS, si oglejte Dify Premium na AWS Marketplace in ga z enim klikom uvedite v svoj AWS VPC. To je cenovno ugodna ponudba AMI z možnostjo ustvarjanja aplikacij z logotipom in blagovno znamko po meri.
## Staying ahead
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)
## 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 .
Č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)
#### 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)
## Prispevam
Za tiste, ki bi radi prispevali kodo, si oglejte naš vodnik za prispevke . 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 .
## 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>
## Star history
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
## Varnostno razkritje
Zaradi zaščite vaše zasebnosti se izogibajte objavljanju varnostnih vprašanj na GitHub. Namesto tega pošljite vprašanja na security@dify.ai in zagotovili vam bomo podrobnejši odgovor.
## Licenca
To skladišče je na voljo pod [odprtokodno licenco Dify](LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="Discord'da sohbet et"></a> alt="Discord'da sohbet et"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="Follow Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="X(Twitter)'da takip et"></a> alt="Twitter'da takip et"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Çekmeleri" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Çekmeleri" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -203,13 +200,9 @@ Yüksek kullanılabilirliğe sahip bir kurulum yapılandırmak isterseniz, Dify'
#### Dağıtım için Terraform Kullanımı #### 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 Global
- [Azure Terraform tarafından @nikawang](https://github.com/nikawang/dify-azure-terraform) [Terraform](https://www.terraform.io/) kullanarak Dify'ı Azure'a tek tıklamayla dağıtın.
- [@nikawang tarafından Azure Terraform](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform tarafından @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
## Katkıda Bulunma ## Katkıda Bulunma
@ -229,7 +222,7 @@ Aynı zamanda, lütfen Dify'ı sosyal medyada, etkinliklerde ve konferanslarda p
* [Github Tartışmaları](https://github.com/langgenius/dify/discussions). En uygun: geri bildirim paylaşmak ve soru sormak için. * [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](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) bakın. * [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](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) bakın.
* [Discord](https://discord.gg/FngNHpbcY7). En uygun: uygulamalarınızı paylaşmak ve toplulukla vakit geçirmek için. * [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. * [Twitter](https://twitter.com/dify_ai). En uygun: uygulamalarınızı paylaşmak ve toplulukla vakit geçirmek için.
## Star history ## Star history

View File

@ -15,12 +15,9 @@
<a href="https://discord.gg/FngNHpbcY7" target="_blank"> <a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" <img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat trên Discord"></a> alt="chat trên Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="Follow Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank"> <a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5" <img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="theo dõi trên X(Twitter)"></a> alt="theo dõi trên Twitter"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank"> <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank"> <a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
@ -199,14 +196,10 @@ Nếu bạn muốn cấu hình một cài đặt có độ sẵn sàng cao, có
#### Sử dụng Terraform để Triển khai #### 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 Global
Triển khai Dify lên Azure chỉ với một cú nhấp chuột bằng cách sử dụng [terraform](https://www.terraform.io/).
- [Azure Terraform bởi @nikawang](https://github.com/nikawang/dify-azure-terraform) - [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)
## Đóng góp ## Đóng góp
Đối với những người muốn đóng góp mã, xem [Hướng dẫn Đóng góp](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) của chúng tôi. Đối với những người muốn đóng góp mã, xem [Hướng dẫn Đóng góp](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) của chúng tôi.
@ -226,7 +219,7 @@ Triển khai Dify lên nền tảng đám mây với một cú nhấp chuột b
* [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. * [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](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) của chúng tô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](https://github.com/langgenius/dify/blob/main/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. * [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. * [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.
## Lịch sử Yêu thích ## Lịch sử Yêu thích
@ -238,4 +231,4 @@ Triển khai Dify lên nền tảng đám mây với một cú nhấp chuột b
## Giấy phép ## Giấy phép
Kho lưu trữ này có sẵn theo [Giấy phép Mã nguồn Mở Dify](LICENSE), về cơ bản là Apache 2.0 với một vài hạn chế bổ sung. Kho lưu trữ này có sẵn theo [Giấy phép Mã nguồn Mở Dify](LICENSE), về cơ bản là Apache 2.0 với một vài hạn chế bổ sung.

View File

@ -20,9 +20,6 @@ FILES_URL=http://127.0.0.1:5001
# The time in seconds after the signature is rejected # The time in seconds after the signature is rejected
FILES_ACCESS_TIMEOUT=300 FILES_ACCESS_TIMEOUT=300
# Access token expiration time in minutes
ACCESS_TOKEN_EXPIRE_MINUTES=60
# celery configuration # celery configuration
CELERY_BROKER_URL=redis://:difyai123456@localhost:6379/1 CELERY_BROKER_URL=redis://:difyai123456@localhost:6379/1
@ -31,17 +28,8 @@ REDIS_HOST=localhost
REDIS_PORT=6379 REDIS_PORT=6379
REDIS_USERNAME= REDIS_USERNAME=
REDIS_PASSWORD=difyai123456 REDIS_PASSWORD=difyai123456
REDIS_USE_SSL=false
REDIS_DB=0 REDIS_DB=0
# redis Sentinel configuration.
REDIS_USE_SENTINEL=false
REDIS_SENTINELS=
REDIS_SENTINEL_SERVICE_NAME=
REDIS_SENTINEL_USERNAME=
REDIS_SENTINEL_PASSWORD=
REDIS_SENTINEL_SOCKET_TIMEOUT=0.1
# PostgreSQL database configuration # PostgreSQL database configuration
DB_USERNAME=postgres DB_USERNAME=postgres
DB_PASSWORD=difyai123456 DB_PASSWORD=difyai123456
@ -51,7 +39,7 @@ DB_DATABASE=dify
# Storage configuration # Storage configuration
# use for store upload files, private keys... # use for store upload files, private keys...
# storage type: local, s3, aliyun-oss, azure-blob, baidu-obs, google-storage, huawei-obs, oci-storage, tencent-cos, volcengine-tos, supabase # storage type: local, s3, azure-blob, google-storage, tencent-cos, huawei-obs, volcengine-tos
STORAGE_TYPE=local STORAGE_TYPE=local
STORAGE_LOCAL_PATH=storage STORAGE_LOCAL_PATH=storage
S3_USE_AWS_MANAGED_IAM=false S3_USE_AWS_MANAGED_IAM=false
@ -91,12 +79,6 @@ HUAWEI_OBS_SECRET_KEY=your-secret-key
HUAWEI_OBS_ACCESS_KEY=your-access-key HUAWEI_OBS_ACCESS_KEY=your-access-key
HUAWEI_OBS_SERVER=your-server-url HUAWEI_OBS_SERVER=your-server-url
# Baidu OBS Storage Configuration
BAIDU_OBS_BUCKET_NAME=your-bucket-name
BAIDU_OBS_SECRET_KEY=your-secret-key
BAIDU_OBS_ACCESS_KEY=your-access-key
BAIDU_OBS_ENDPOINT=your-server-url
# OCI Storage configuration # OCI Storage configuration
OCI_ENDPOINT=your-endpoint OCI_ENDPOINT=your-endpoint
OCI_BUCKET_NAME=your-bucket-name OCI_BUCKET_NAME=your-bucket-name
@ -111,17 +93,11 @@ VOLCENGINE_TOS_ACCESS_KEY=your-access-key
VOLCENGINE_TOS_SECRET_KEY=your-secret-key VOLCENGINE_TOS_SECRET_KEY=your-secret-key
VOLCENGINE_TOS_REGION=your-region VOLCENGINE_TOS_REGION=your-region
# Supabase Storage Configuration
SUPABASE_BUCKET_NAME=your-bucket-name
SUPABASE_API_KEY=your-access-key
SUPABASE_URL=your-server-url
# CORS configuration # CORS configuration
WEB_API_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,* WEB_API_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,*
CONSOLE_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,* CONSOLE_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,*
# Vector database configuration, support: weaviate, qdrant, milvus, myscale, relyt, pgvecto_rs, pgvector, pgvector, chroma, opensearch, tidb_vector
# Vector database configuration, support: weaviate, qdrant, milvus, myscale, relyt, pgvecto_rs, pgvector, pgvector, chroma, opensearch, tidb_vector, couchbase, vikingdb, upstash, lindorm, oceanbase
VECTOR_STORE=weaviate VECTOR_STORE=weaviate
# Weaviate configuration # Weaviate configuration
@ -137,13 +113,6 @@ QDRANT_CLIENT_TIMEOUT=20
QDRANT_GRPC_ENABLED=false QDRANT_GRPC_ENABLED=false
QDRANT_GRPC_PORT=6334 QDRANT_GRPC_PORT=6334
#Couchbase configuration
COUCHBASE_CONNECTION_STRING=127.0.0.1
COUCHBASE_USER=Administrator
COUCHBASE_PASSWORD=password
COUCHBASE_BUCKET_NAME=Embeddings
COUCHBASE_SCOPE_NAME=_default
# Milvus configuration # Milvus configuration
MILVUS_URI=http://127.0.0.1:19530 MILVUS_URI=http://127.0.0.1:19530
MILVUS_TOKEN= MILVUS_TOKEN=
@ -193,8 +162,6 @@ PGVECTOR_PORT=5433
PGVECTOR_USER=postgres PGVECTOR_USER=postgres
PGVECTOR_PASSWORD=postgres PGVECTOR_PASSWORD=postgres
PGVECTOR_DATABASE=postgres PGVECTOR_DATABASE=postgres
PGVECTOR_MIN_CONNECTION=1
PGVECTOR_MAX_CONNECTION=5
# Tidb Vector configuration # Tidb Vector configuration
TIDB_VECTOR_HOST=xxx.eu-central-1.xxx.aws.tidbcloud.com TIDB_VECTOR_HOST=xxx.eu-central-1.xxx.aws.tidbcloud.com
@ -203,20 +170,6 @@ TIDB_VECTOR_USER=xxx.root
TIDB_VECTOR_PASSWORD=xxxxxx TIDB_VECTOR_PASSWORD=xxxxxx
TIDB_VECTOR_DATABASE=dify TIDB_VECTOR_DATABASE=dify
# Tidb on qdrant configuration
TIDB_ON_QDRANT_URL=http://127.0.0.1
TIDB_ON_QDRANT_API_KEY=dify
TIDB_ON_QDRANT_CLIENT_TIMEOUT=20
TIDB_ON_QDRANT_GRPC_ENABLED=false
TIDB_ON_QDRANT_GRPC_PORT=6334
TIDB_PUBLIC_KEY=dify
TIDB_PRIVATE_KEY=dify
TIDB_API_URL=http://127.0.0.1
TIDB_IAM_API_URL=http://127.0.0.1
TIDB_REGION=regions/aws-us-east-1
TIDB_PROJECT_ID=dify
TIDB_SPEND_LIMIT=100
# Chroma configuration # Chroma configuration
CHROMA_HOST=127.0.0.1 CHROMA_HOST=127.0.0.1
CHROMA_PORT=8000 CHROMA_PORT=8000
@ -242,54 +195,14 @@ OPENSEARCH_USER=admin
OPENSEARCH_PASSWORD=admin OPENSEARCH_PASSWORD=admin
OPENSEARCH_SECURE=true OPENSEARCH_SECURE=true
# Baidu configuration
BAIDU_VECTOR_DB_ENDPOINT=http://127.0.0.1:5287
BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS=30000
BAIDU_VECTOR_DB_ACCOUNT=root
BAIDU_VECTOR_DB_API_KEY=dify
BAIDU_VECTOR_DB_DATABASE=dify
BAIDU_VECTOR_DB_SHARD=1
BAIDU_VECTOR_DB_REPLICAS=3
# Upstash configuration
UPSTASH_VECTOR_URL=your-server-url
UPSTASH_VECTOR_TOKEN=your-access-token
# ViKingDB configuration
VIKINGDB_ACCESS_KEY=your-ak
VIKINGDB_SECRET_KEY=your-sk
VIKINGDB_REGION=cn-shanghai
VIKINGDB_HOST=api-vikingdb.xxx.volces.com
VIKINGDB_SCHEMA=http
VIKINGDB_CONNECTION_TIMEOUT=30
VIKINGDB_SOCKET_TIMEOUT=30
# Lindorm configuration
LINDORM_URL=http://ld-*******************-proxy-search-pub.lindorm.aliyuncs.com:30070
LINDORM_USERNAME=admin
LINDORM_PASSWORD=admin
# OceanBase Vector configuration
OCEANBASE_VECTOR_HOST=127.0.0.1
OCEANBASE_VECTOR_PORT=2881
OCEANBASE_VECTOR_USER=root@test
OCEANBASE_VECTOR_PASSWORD=difyai123456
OCEANBASE_VECTOR_DATABASE=test
OCEANBASE_MEMORY_LIMIT=6G
# Upload configuration # Upload configuration
UPLOAD_FILE_SIZE_LIMIT=15 UPLOAD_FILE_SIZE_LIMIT=15
UPLOAD_FILE_BATCH_LIMIT=5 UPLOAD_FILE_BATCH_LIMIT=5
UPLOAD_IMAGE_FILE_SIZE_LIMIT=10 UPLOAD_IMAGE_FILE_SIZE_LIMIT=10
UPLOAD_VIDEO_FILE_SIZE_LIMIT=100
UPLOAD_AUDIO_FILE_SIZE_LIMIT=50
# Model configuration # Model Configuration
MULTIMODAL_SEND_IMAGE_FORMAT=base64 MULTIMODAL_SEND_IMAGE_FORMAT=base64
MULTIMODAL_SEND_VIDEO_FORMAT=base64
PROMPT_GENERATION_MAX_TOKENS=512 PROMPT_GENERATION_MAX_TOKENS=512
CODE_GENERATION_MAX_TOKENS=1024
# Mail configuration, support: resend, smtp # Mail configuration, support: resend, smtp
MAIL_TYPE= MAIL_TYPE=
@ -321,21 +234,13 @@ ETL_TYPE=dify
UNSTRUCTURED_API_URL= UNSTRUCTURED_API_URL=
UNSTRUCTURED_API_KEY= UNSTRUCTURED_API_KEY=
#ssrf
SSRF_PROXY_HTTP_URL= SSRF_PROXY_HTTP_URL=
SSRF_PROXY_HTTPS_URL= SSRF_PROXY_HTTPS_URL=
SSRF_DEFAULT_MAX_RETRIES=3 SSRF_DEFAULT_MAX_RETRIES=3
SSRF_DEFAULT_TIME_OUT=5
SSRF_DEFAULT_CONNECT_TIME_OUT=5
SSRF_DEFAULT_READ_TIME_OUT=5
SSRF_DEFAULT_WRITE_TIME_OUT=5
BATCH_UPLOAD_LIMIT=10 BATCH_UPLOAD_LIMIT=10
KEYWORD_DATA_SOURCE_TYPE=database KEYWORD_DATA_SOURCE_TYPE=database
# Workflow file upload limit
WORKFLOW_FILE_UPLOAD_LIMIT=10
# CODE EXECUTION CONFIGURATION # CODE EXECUTION CONFIGURATION
CODE_EXECUTION_ENDPOINT=http://127.0.0.1:8194 CODE_EXECUTION_ENDPOINT=http://127.0.0.1:8194
CODE_EXECUTION_API_KEY=dify-sandbox CODE_EXECUTION_API_KEY=dify-sandbox
@ -358,19 +263,8 @@ HTTP_REQUEST_MAX_WRITE_TIMEOUT=600
HTTP_REQUEST_NODE_MAX_BINARY_SIZE=10485760 HTTP_REQUEST_NODE_MAX_BINARY_SIZE=10485760
HTTP_REQUEST_NODE_MAX_TEXT_SIZE=1048576 HTTP_REQUEST_NODE_MAX_TEXT_SIZE=1048576
# Respect X-* headers to redirect clients
RESPECT_XFORWARD_HEADERS_ENABLED=false
# Log file path # Log file path
LOG_FILE= LOG_FILE=
# Log file max size, the unit is MB
LOG_FILE_MAX_SIZE=20
# Log file max backup count
LOG_FILE_BACKUP_COUNT=5
# Log dateformat
LOG_DATEFORMAT=%Y-%m-%d %H:%M:%S
# Log Timezone
LOG_TZ=UTC
# Indexing configuration # Indexing configuration
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=1000 INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=1000
@ -379,7 +273,6 @@ INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=1000
WORKFLOW_MAX_EXECUTION_STEPS=500 WORKFLOW_MAX_EXECUTION_STEPS=500
WORKFLOW_MAX_EXECUTION_TIME=1200 WORKFLOW_MAX_EXECUTION_TIME=1200
WORKFLOW_CALL_MAX_DEPTH=5 WORKFLOW_CALL_MAX_DEPTH=5
MAX_VARIABLE_SIZE=204800
# App configuration # App configuration
APP_MAX_EXECUTION_TIME=1200 APP_MAX_EXECUTION_TIME=1200
@ -397,8 +290,3 @@ POSITION_TOOL_EXCLUDES=
POSITION_PROVIDER_PINS= POSITION_PROVIDER_PINS=
POSITION_PROVIDER_INCLUDES= POSITION_PROVIDER_INCLUDES=
POSITION_PROVIDER_EXCLUDES= POSITION_PROVIDER_EXCLUDES=
# Reset password token expiry minutes
RESET_PASSWORD_TOKEN_EXPIRY_MINUTES=5
CREATE_TIDB_SERVICE_JOB_ENABLED=false

View File

@ -1,15 +1,8 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"compounds": [
{
"name": "Launch Flask and Celery",
"configurations": ["Python: Flask", "Python: Celery"]
}
],
"configurations": [ "configurations": [
{ {
"name": "Python: Flask", "name": "Python: Flask",
"consoleName": "Flask",
"type": "debugpy", "type": "debugpy",
"request": "launch", "request": "launch",
"python": "${workspaceFolder}/.venv/bin/python", "python": "${workspaceFolder}/.venv/bin/python",
@ -24,12 +17,12 @@
}, },
"args": [ "args": [
"run", "run",
"--host=0.0.0.0",
"--port=5001" "--port=5001"
] ]
}, },
{ {
"name": "Python: Celery", "name": "Python: Celery",
"consoleName": "Celery",
"type": "debugpy", "type": "debugpy",
"request": "launch", "request": "launch",
"python": "${workspaceFolder}/.venv/bin/python", "python": "${workspaceFolder}/.venv/bin/python",
@ -52,10 +45,10 @@
"-c", "-c",
"1", "1",
"--loglevel", "--loglevel",
"DEBUG", "info",
"-Q", "-Q",
"dataset,generation,mail,ops_trace,app_deletion" "dataset,generation,mail,ops_trace,app_deletion"
] ]
} },
] ]
} }

View File

@ -4,7 +4,7 @@ FROM python:3.10-slim-bookworm AS base
WORKDIR /app/api WORKDIR /app/api
# Install Poetry # Install Poetry
ENV POETRY_VERSION=1.8.4 ENV POETRY_VERSION=1.8.3
# if you located in China, you can use aliyun mirror to speed up # if you located in China, you can use aliyun mirror to speed up
# RUN pip install --no-cache-dir poetry==${POETRY_VERSION} -i https://mirrors.aliyun.com/pypi/simple/ # RUN pip install --no-cache-dir poetry==${POETRY_VERSION} -i https://mirrors.aliyun.com/pypi/simple/
@ -55,9 +55,7 @@ RUN apt-get update \
&& echo "deb http://deb.debian.org/debian testing main" > /etc/apt/sources.list \ && echo "deb http://deb.debian.org/debian testing main" > /etc/apt/sources.list \
&& apt-get update \ && apt-get update \
# For Security # For Security
&& apt-get install -y --no-install-recommends expat=2.6.4-1 libldap-2.5-0=2.5.18+dfsg-3+b1 perl=5.40.0-7 libsqlite3-0=3.46.1-1 zlib1g=1:1.3.dfsg+really1.3.1-1+b1 \ && apt-get install -y --no-install-recommends zlib1g=1:1.3.dfsg+really1.3.1-1 expat=2.6.3-1 libldap-2.5-0=2.5.18+dfsg-3 perl=5.38.2-5 libsqlite3-0=3.46.0-1 \
# install a chinese font to support the use of tools like matplotlib
&& apt-get install -y fonts-noto-cjk \
&& apt-get autoremove -y \ && apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*

View File

@ -65,24 +65,25 @@
8. Start Dify [web](../web) service. 8. Start Dify [web](../web) service.
9. Setup your application by visiting `http://localhost:3000`... 9. Setup your application by visiting `http://localhost:3000`...
10. If you need to handle and debug the async tasks (e.g. dataset importing and documents indexing), please start the worker service. 10. If you need to debug local async processing, please start the worker service.
```bash ```bash
poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion poetry run python -m celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion
``` ```
The started celery app handles the async tasks, e.g. dataset importing and documents indexing.
## Testing ## Testing
1. Install dependencies for both the backend and the test environment 1. Install dependencies for both the backend and the test environment
```bash ```bash
poetry install -C api --with dev poetry install --with dev
``` ```
2. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml` 2. Run the tests locally with mocked system environment variables in `tool.pytest_env` section in `pyproject.toml`
```bash ```bash
cd ../
poetry run -C api bash dev/pytest/pytest_all_tests.sh poetry run -C api bash dev/pytest/pytest_all_tests.sh
``` ```

View File

@ -1,9 +1,6 @@
import os import os
import sys
from configs import dify_config if os.environ.get("DEBUG", "false").lower() != "true":
if not dify_config.DEBUG:
from gevent import monkey from gevent import monkey
monkey.patch_all() monkey.patch_all()
@ -13,55 +10,259 @@ if not dify_config.DEBUG:
grpc.experimental.gevent.init_gevent() grpc.experimental.gevent.init_gevent()
import json import json
import logging
import sys
import threading import threading
import time import time
import warnings import warnings
from logging.handlers import RotatingFileHandler
from flask import Response from flask import Flask, Response, request
from flask_cors import CORS
from werkzeug.exceptions import Unauthorized
from app_factory import create_app import contexts
from commands import register_commands
from configs import dify_config
# DO NOT REMOVE BELOW # DO NOT REMOVE BELOW
from events import event_handlers # noqa: F401 from events import event_handlers
from extensions import (
ext_celery,
ext_code_based_extension,
ext_compress,
ext_database,
ext_hosting_provider,
ext_login,
ext_mail,
ext_migrate,
ext_redis,
ext_sentry,
ext_storage,
)
from extensions.ext_database import db from extensions.ext_database import db
from extensions.ext_login import login_manager
from libs.passport import PassportService
# TODO: Find a way to avoid importing models here # TODO: Find a way to avoid importing models here
from models import account, dataset, model, source, task, tool, tools, web # noqa: F401 from models import account, dataset, model, source, task, tool, tools, web
from services.account_service import AccountService
# DO NOT REMOVE ABOVE # DO NOT REMOVE ABOVE
if sys.version_info[:2] == (3, 10):
print("Warning: Python 3.10 will not be supported in the next version.")
warnings.simplefilter("ignore", ResourceWarning) warnings.simplefilter("ignore", ResourceWarning)
os.environ["TZ"] = "UTC" # fix windows platform
# windows platform not support tzset if os.name == "nt":
if hasattr(time, "tzset"): os.system('tzutil /s "UTC"')
else:
os.environ["TZ"] = "UTC"
time.tzset() time.tzset()
class DifyApp(Flask):
pass
# -------------
# Configuration
# -------------
config_type = os.getenv("EDITION", default="SELF_HOSTED") # ce edition first
# ----------------------------
# Application Factory Function
# ----------------------------
def create_flask_app_with_configs() -> Flask:
"""
create a raw flask app
with configs loaded from .env file
"""
dify_app = DifyApp(__name__)
dify_app.config.from_mapping(dify_config.model_dump())
# populate configs into system environment variables
for key, value in dify_app.config.items():
if isinstance(value, str):
os.environ[key] = value
elif isinstance(value, int | float | bool):
os.environ[key] = str(value)
elif value is None:
os.environ[key] = ""
return dify_app
def create_app() -> Flask:
app = create_flask_app_with_configs()
app.secret_key = app.config["SECRET_KEY"]
log_handlers = None
log_file = app.config.get("LOG_FILE")
if log_file:
log_dir = os.path.dirname(log_file)
os.makedirs(log_dir, exist_ok=True)
log_handlers = [
RotatingFileHandler(
filename=log_file,
maxBytes=1024 * 1024 * 1024,
backupCount=5,
),
logging.StreamHandler(sys.stdout),
]
logging.basicConfig(
level=app.config.get("LOG_LEVEL"),
format=app.config.get("LOG_FORMAT"),
datefmt=app.config.get("LOG_DATEFORMAT"),
handlers=log_handlers,
force=True,
)
log_tz = app.config.get("LOG_TZ")
if log_tz:
from datetime import datetime
import pytz
timezone = pytz.timezone(log_tz)
def time_converter(seconds):
return datetime.utcfromtimestamp(seconds).astimezone(timezone).timetuple()
for handler in logging.root.handlers:
handler.formatter.converter = time_converter
initialize_extensions(app)
register_blueprints(app)
register_commands(app)
return app
def initialize_extensions(app):
# Since the application instance is now created, pass it to each Flask
# extension instance to bind it to the Flask application instance (app)
ext_compress.init_app(app)
ext_code_based_extension.init()
ext_database.init_app(app)
ext_migrate.init(app, db)
ext_redis.init_app(app)
ext_storage.init_app(app)
ext_celery.init_app(app)
ext_login.init_app(app)
ext_mail.init_app(app)
ext_hosting_provider.init_app(app)
ext_sentry.init_app(app)
# Flask-Login configuration
@login_manager.request_loader
def load_user_from_request(request_from_flask_login):
"""Load user based on the request."""
if request.blueprint not in {"console", "inner_api"}:
return None
# Check if the user_id contains a dot, indicating the old format
auth_header = request.headers.get("Authorization", "")
if not auth_header:
auth_token = request.args.get("_token")
if not auth_token:
raise Unauthorized("Invalid Authorization token.")
else:
if " " not in auth_header:
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
auth_scheme, auth_token = auth_header.split(None, 1)
auth_scheme = auth_scheme.lower()
if auth_scheme != "bearer":
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
decoded = PassportService().verify(auth_token)
user_id = decoded.get("user_id")
account = AccountService.load_logged_in_account(account_id=user_id, token=auth_token)
if account:
contexts.tenant_id.set(account.current_tenant_id)
return account
@login_manager.unauthorized_handler
def unauthorized_handler():
"""Handle unauthorized requests."""
return Response(
json.dumps({"code": "unauthorized", "message": "Unauthorized."}),
status=401,
content_type="application/json",
)
# register blueprint routers
def register_blueprints(app):
from controllers.console import bp as console_app_bp
from controllers.files import bp as files_bp
from controllers.inner_api import bp as inner_api_bp
from controllers.service_api import bp as service_api_bp
from controllers.web import bp as web_bp
CORS(
service_api_bp,
allow_headers=["Content-Type", "Authorization", "X-App-Code"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
)
app.register_blueprint(service_api_bp)
CORS(
web_bp,
resources={r"/*": {"origins": app.config["WEB_API_CORS_ALLOW_ORIGINS"]}},
supports_credentials=True,
allow_headers=["Content-Type", "Authorization", "X-App-Code"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
expose_headers=["X-Version", "X-Env"],
)
app.register_blueprint(web_bp)
CORS(
console_app_bp,
resources={r"/*": {"origins": app.config["CONSOLE_CORS_ALLOW_ORIGINS"]}},
supports_credentials=True,
allow_headers=["Content-Type", "Authorization"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
expose_headers=["X-Version", "X-Env"],
)
app.register_blueprint(console_app_bp)
CORS(files_bp, allow_headers=["Content-Type"], methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"])
app.register_blueprint(files_bp)
app.register_blueprint(inner_api_bp)
# create app # create app
app = create_app() app = create_app()
celery = app.extensions["celery"] celery = app.extensions["celery"]
if dify_config.TESTING: if app.config.get("TESTING"):
print("App is running in TESTING mode") print("App is running in TESTING mode")
@app.after_request @app.after_request
def after_request(response): def after_request(response):
"""Add Version headers to the response.""" """Add Version headers to the response."""
response.headers.add("X-Version", dify_config.CURRENT_VERSION) response.set_cookie("remember_token", "", expires=0)
response.headers.add("X-Env", dify_config.DEPLOY_ENV) response.headers.add("X-Version", app.config["CURRENT_VERSION"])
response.headers.add("X-Env", app.config["DEPLOY_ENV"])
return response return response
@app.route("/health") @app.route("/health")
def health(): def health():
return Response( return Response(
json.dumps({"pid": os.getpid(), "status": "ok", "version": dify_config.CURRENT_VERSION}), json.dumps({"pid": os.getpid(), "status": "ok", "version": app.config["CURRENT_VERSION"]}),
status=200, status=200,
content_type="application/json", content_type="application/json",
) )

View File

@ -1,178 +0,0 @@
import os
from configs import dify_config
if not dify_config.DEBUG:
from gevent import monkey
monkey.patch_all()
import grpc.experimental.gevent
grpc.experimental.gevent.init_gevent()
import json
from flask import Flask, Response, request
from flask_cors import CORS
from werkzeug.exceptions import Unauthorized
import contexts
from commands import register_commands
from configs import dify_config
from extensions import (
ext_celery,
ext_code_based_extension,
ext_compress,
ext_database,
ext_hosting_provider,
ext_logging,
ext_login,
ext_mail,
ext_migrate,
ext_proxy_fix,
ext_redis,
ext_sentry,
ext_storage,
)
from extensions.ext_database import db
from extensions.ext_login import login_manager
from libs.passport import PassportService
from services.account_service import AccountService
class DifyApp(Flask):
pass
# ----------------------------
# Application Factory Function
# ----------------------------
def create_flask_app_with_configs() -> Flask:
"""
create a raw flask app
with configs loaded from .env file
"""
dify_app = DifyApp(__name__)
dify_app.config.from_mapping(dify_config.model_dump())
# populate configs into system environment variables
for key, value in dify_app.config.items():
if isinstance(value, str):
os.environ[key] = value
elif isinstance(value, int | float | bool):
os.environ[key] = str(value)
elif value is None:
os.environ[key] = ""
return dify_app
def create_app() -> Flask:
app = create_flask_app_with_configs()
app.secret_key = dify_config.SECRET_KEY
initialize_extensions(app)
register_blueprints(app)
register_commands(app)
return app
def initialize_extensions(app):
# Since the application instance is now created, pass it to each Flask
# extension instance to bind it to the Flask application instance (app)
ext_logging.init_app(app)
ext_compress.init_app(app)
ext_code_based_extension.init()
ext_database.init_app(app)
ext_migrate.init(app, db)
ext_redis.init_app(app)
ext_storage.init_app(app)
ext_celery.init_app(app)
ext_login.init_app(app)
ext_mail.init_app(app)
ext_hosting_provider.init_app(app)
ext_sentry.init_app(app)
ext_proxy_fix.init_app(app)
# Flask-Login configuration
@login_manager.request_loader
def load_user_from_request(request_from_flask_login):
"""Load user based on the request."""
if request.blueprint not in {"console", "inner_api"}:
return None
# Check if the user_id contains a dot, indicating the old format
auth_header = request.headers.get("Authorization", "")
if not auth_header:
auth_token = request.args.get("_token")
if not auth_token:
raise Unauthorized("Invalid Authorization token.")
else:
if " " not in auth_header:
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
auth_scheme, auth_token = auth_header.split(None, 1)
auth_scheme = auth_scheme.lower()
if auth_scheme != "bearer":
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
decoded = PassportService().verify(auth_token)
user_id = decoded.get("user_id")
logged_in_account = AccountService.load_logged_in_account(account_id=user_id)
if logged_in_account:
contexts.tenant_id.set(logged_in_account.current_tenant_id)
return logged_in_account
@login_manager.unauthorized_handler
def unauthorized_handler():
"""Handle unauthorized requests."""
return Response(
json.dumps({"code": "unauthorized", "message": "Unauthorized."}),
status=401,
content_type="application/json",
)
# register blueprint routers
def register_blueprints(app):
from controllers.console import bp as console_app_bp
from controllers.files import bp as files_bp
from controllers.inner_api import bp as inner_api_bp
from controllers.service_api import bp as service_api_bp
from controllers.web import bp as web_bp
CORS(
service_api_bp,
allow_headers=["Content-Type", "Authorization", "X-App-Code"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
)
app.register_blueprint(service_api_bp)
CORS(
web_bp,
resources={r"/*": {"origins": dify_config.WEB_API_CORS_ALLOW_ORIGINS}},
supports_credentials=True,
allow_headers=["Content-Type", "Authorization", "X-App-Code"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
expose_headers=["X-Version", "X-Env"],
)
app.register_blueprint(web_bp)
CORS(
console_app_bp,
resources={r"/*": {"origins": dify_config.CONSOLE_CORS_ALLOW_ORIGINS}},
supports_credentials=True,
allow_headers=["Content-Type", "Authorization"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
expose_headers=["X-Version", "X-Env"],
)
app.register_blueprint(console_app_bp)
CORS(files_bp, allow_headers=["Content-Type"], methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"])
app.register_blueprint(files_bp)
app.register_blueprint(inner_api_bp)

View File

@ -19,7 +19,7 @@ from extensions.ext_redis import redis_client
from libs.helper import email as email_validate from libs.helper import email as email_validate
from libs.password import hash_password, password_pattern, valid_password from libs.password import hash_password, password_pattern, valid_password
from libs.rsa import generate_key_pair from libs.rsa import generate_key_pair
from models import Tenant from models.account import Tenant
from models.dataset import Dataset, DatasetCollectionBinding, DocumentSegment from models.dataset import Dataset, DatasetCollectionBinding, DocumentSegment
from models.dataset import Document as DatasetDocument from models.dataset import Document as DatasetDocument
from models.model import Account, App, AppAnnotationSetting, AppMode, Conversation, MessageAnnotation from models.model import Account, App, AppAnnotationSetting, AppMode, Conversation, MessageAnnotation
@ -28,28 +28,28 @@ from services.account_service import RegisterService, TenantService
@click.command("reset-password", help="Reset the account password.") @click.command("reset-password", help="Reset the account password.")
@click.option("--email", prompt=True, help="Account email to reset password for") @click.option("--email", prompt=True, help="The email address of the account whose password you need to reset")
@click.option("--new-password", prompt=True, help="New password") @click.option("--new-password", prompt=True, help="the new password.")
@click.option("--password-confirm", prompt=True, help="Confirm new password") @click.option("--password-confirm", prompt=True, help="the new password confirm.")
def reset_password(email, new_password, password_confirm): def reset_password(email, new_password, password_confirm):
""" """
Reset password of owner account Reset password of owner account
Only available in SELF_HOSTED mode Only available in SELF_HOSTED mode
""" """
if str(new_password).strip() != str(password_confirm).strip(): if str(new_password).strip() != str(password_confirm).strip():
click.echo(click.style("Passwords do not match.", fg="red")) click.echo(click.style("sorry. The two passwords do not match.", fg="red"))
return return
account = db.session.query(Account).filter(Account.email == email).one_or_none() account = db.session.query(Account).filter(Account.email == email).one_or_none()
if not account: if not account:
click.echo(click.style("Account not found for email: {}".format(email), fg="red")) click.echo(click.style("sorry. the account: [{}] not exist .".format(email), fg="red"))
return return
try: try:
valid_password(new_password) valid_password(new_password)
except: except:
click.echo(click.style("Invalid password. Must match {}".format(password_pattern), fg="red")) click.echo(click.style("sorry. The passwords must match {} ".format(password_pattern), fg="red"))
return return
# generate password salt # generate password salt
@ -62,37 +62,37 @@ def reset_password(email, new_password, password_confirm):
account.password = base64_password_hashed account.password = base64_password_hashed
account.password_salt = base64_salt account.password_salt = base64_salt
db.session.commit() db.session.commit()
click.echo(click.style("Password reset successfully.", fg="green")) click.echo(click.style("Congratulations! Password has been reset.", fg="green"))
@click.command("reset-email", help="Reset the account email.") @click.command("reset-email", help="Reset the account email.")
@click.option("--email", prompt=True, help="Current account email") @click.option("--email", prompt=True, help="The old email address of the account whose email you need to reset")
@click.option("--new-email", prompt=True, help="New email") @click.option("--new-email", prompt=True, help="the new email.")
@click.option("--email-confirm", prompt=True, help="Confirm new email") @click.option("--email-confirm", prompt=True, help="the new email confirm.")
def reset_email(email, new_email, email_confirm): def reset_email(email, new_email, email_confirm):
""" """
Replace account email Replace account email
:return: :return:
""" """
if str(new_email).strip() != str(email_confirm).strip(): if str(new_email).strip() != str(email_confirm).strip():
click.echo(click.style("New emails do not match.", fg="red")) click.echo(click.style("Sorry, new email and confirm email do not match.", fg="red"))
return return
account = db.session.query(Account).filter(Account.email == email).one_or_none() account = db.session.query(Account).filter(Account.email == email).one_or_none()
if not account: if not account:
click.echo(click.style("Account not found for email: {}".format(email), fg="red")) click.echo(click.style("sorry. the account: [{}] not exist .".format(email), fg="red"))
return return
try: try:
email_validate(new_email) email_validate(new_email)
except: except:
click.echo(click.style("Invalid email: {}".format(new_email), fg="red")) click.echo(click.style("sorry. {} is not a valid email. ".format(email), fg="red"))
return return
account.email = new_email account.email = new_email
db.session.commit() db.session.commit()
click.echo(click.style("Email updated successfully.", fg="green")) click.echo(click.style("Congratulations!, email has been reset.", fg="green"))
@click.command( @click.command(
@ -104,7 +104,7 @@ def reset_email(email, new_email, email_confirm):
) )
@click.confirmation_option( @click.confirmation_option(
prompt=click.style( prompt=click.style(
"Are you sure you want to reset encrypt key pair? This operation cannot be rolled back!", fg="red" "Are you sure you want to reset encrypt key pair? this operation cannot be rolled back!", fg="red"
) )
) )
def reset_encrypt_key_pair(): def reset_encrypt_key_pair():
@ -114,13 +114,13 @@ def reset_encrypt_key_pair():
Only support SELF_HOSTED mode. Only support SELF_HOSTED mode.
""" """
if dify_config.EDITION != "SELF_HOSTED": if dify_config.EDITION != "SELF_HOSTED":
click.echo(click.style("This command is only for SELF_HOSTED installations.", fg="red")) click.echo(click.style("Sorry, only support SELF_HOSTED mode.", fg="red"))
return return
tenants = db.session.query(Tenant).all() tenants = db.session.query(Tenant).all()
for tenant in tenants: for tenant in tenants:
if not tenant: if not tenant:
click.echo(click.style("No workspaces found. Run /install first.", fg="red")) click.echo(click.style("Sorry, no workspace found. Please enter /install to initialize.", fg="red"))
return return
tenant.encrypt_public_key = generate_key_pair(tenant.id) tenant.encrypt_public_key = generate_key_pair(tenant.id)
@ -137,7 +137,7 @@ def reset_encrypt_key_pair():
) )
@click.command("vdb-migrate", help="Migrate vector db.") @click.command("vdb-migrate", help="migrate vector db.")
@click.option("--scope", default="all", prompt=False, help="The scope of vector database to migrate, Default is All.") @click.option("--scope", default="all", prompt=False, help="The scope of vector database to migrate, Default is All.")
def vdb_migrate(scope: str): def vdb_migrate(scope: str):
if scope in {"knowledge", "all"}: if scope in {"knowledge", "all"}:
@ -150,7 +150,7 @@ def migrate_annotation_vector_database():
""" """
Migrate annotation datas to target vector database . Migrate annotation datas to target vector database .
""" """
click.echo(click.style("Starting annotation data migration.", fg="green")) click.echo(click.style("Start migrate annotation data.", fg="green"))
create_count = 0 create_count = 0
skipped_count = 0 skipped_count = 0
total_count = 0 total_count = 0
@ -174,14 +174,14 @@ def migrate_annotation_vector_database():
f"Processing the {total_count} app {app.id}. " + f"{create_count} created, {skipped_count} skipped." f"Processing the {total_count} app {app.id}. " + f"{create_count} created, {skipped_count} skipped."
) )
try: try:
click.echo("Creating app annotation index: {}".format(app.id)) click.echo("Create app annotation index: {}".format(app.id))
app_annotation_setting = ( app_annotation_setting = (
db.session.query(AppAnnotationSetting).filter(AppAnnotationSetting.app_id == app.id).first() db.session.query(AppAnnotationSetting).filter(AppAnnotationSetting.app_id == app.id).first()
) )
if not app_annotation_setting: if not app_annotation_setting:
skipped_count = skipped_count + 1 skipped_count = skipped_count + 1
click.echo("App annotation setting disabled: {}".format(app.id)) click.echo("App annotation setting is disabled: {}".format(app.id))
continue continue
# get dataset_collection_binding info # get dataset_collection_binding info
dataset_collection_binding = ( dataset_collection_binding = (
@ -190,7 +190,7 @@ def migrate_annotation_vector_database():
.first() .first()
) )
if not dataset_collection_binding: if not dataset_collection_binding:
click.echo("App annotation collection binding not found: {}".format(app.id)) click.echo("App annotation collection binding is not exist: {}".format(app.id))
continue continue
annotations = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app.id).all() annotations = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app.id).all()
dataset = Dataset( dataset = Dataset(
@ -211,11 +211,11 @@ def migrate_annotation_vector_database():
documents.append(document) documents.append(document)
vector = Vector(dataset, attributes=["doc_id", "annotation_id", "app_id"]) vector = Vector(dataset, attributes=["doc_id", "annotation_id", "app_id"])
click.echo(f"Migrating annotations for app: {app.id}.") click.echo(f"Start to migrate annotation, app_id: {app.id}.")
try: try:
vector.delete() vector.delete()
click.echo(click.style(f"Deleted vector index for app {app.id}.", fg="green")) click.echo(click.style(f"Successfully delete vector index for app: {app.id}.", fg="green"))
except Exception as e: except Exception as e:
click.echo(click.style(f"Failed to delete vector index for app {app.id}.", fg="red")) click.echo(click.style(f"Failed to delete vector index for app {app.id}.", fg="red"))
raise e raise e
@ -223,12 +223,12 @@ def migrate_annotation_vector_database():
try: try:
click.echo( click.echo(
click.style( click.style(
f"Creating vector index with {len(documents)} annotations for app {app.id}.", f"Start to created vector index with {len(documents)} annotations for app {app.id}.",
fg="green", fg="green",
) )
) )
vector.create(documents) vector.create(documents)
click.echo(click.style(f"Created vector index for app {app.id}.", fg="green")) click.echo(click.style(f"Successfully created vector index for app {app.id}.", fg="green"))
except Exception as e: except Exception as e:
click.echo(click.style(f"Failed to created vector index for app {app.id}.", fg="red")) click.echo(click.style(f"Failed to created vector index for app {app.id}.", fg="red"))
raise e raise e
@ -237,14 +237,14 @@ def migrate_annotation_vector_database():
except Exception as e: except Exception as e:
click.echo( click.echo(
click.style( click.style(
"Error creating app annotation index: {} {}".format(e.__class__.__name__, str(e)), fg="red" "Create app annotation index error: {} {}".format(e.__class__.__name__, str(e)), fg="red"
) )
) )
continue continue
click.echo( click.echo(
click.style( click.style(
f"Migration complete. Created {create_count} app annotation indexes. Skipped {skipped_count} apps.", f"Congratulations! Create {create_count} app annotation indexes, and skipped {skipped_count} apps.",
fg="green", fg="green",
) )
) )
@ -254,33 +254,11 @@ def migrate_knowledge_vector_database():
""" """
Migrate vector database datas to target vector database . Migrate vector database datas to target vector database .
""" """
click.echo(click.style("Starting vector database migration.", fg="green")) click.echo(click.style("Start migrate vector db.", fg="green"))
create_count = 0 create_count = 0
skipped_count = 0 skipped_count = 0
total_count = 0 total_count = 0
vector_type = dify_config.VECTOR_STORE vector_type = dify_config.VECTOR_STORE
upper_colletion_vector_types = {
VectorType.MILVUS,
VectorType.PGVECTOR,
VectorType.RELYT,
VectorType.WEAVIATE,
VectorType.ORACLE,
VectorType.ELASTICSEARCH,
}
lower_colletion_vector_types = {
VectorType.ANALYTICDB,
VectorType.CHROMA,
VectorType.MYSCALE,
VectorType.PGVECTO_RS,
VectorType.TIDB_VECTOR,
VectorType.OPENSEARCH,
VectorType.TENCENT,
VectorType.BAIDU,
VectorType.VIKINGDB,
VectorType.UPSTASH,
VectorType.COUCHBASE,
VectorType.OCEANBASE,
}
page = 1 page = 1
while True: while True:
try: try:
@ -300,15 +278,17 @@ def migrate_knowledge_vector_database():
f"Processing the {total_count} dataset {dataset.id}. {create_count} created, {skipped_count} skipped." f"Processing the {total_count} dataset {dataset.id}. {create_count} created, {skipped_count} skipped."
) )
try: try:
click.echo("Creating dataset vector database index: {}".format(dataset.id)) click.echo("Create dataset vdb index: {}".format(dataset.id))
if dataset.index_struct_dict: if dataset.index_struct_dict:
if dataset.index_struct_dict["type"] == vector_type: if dataset.index_struct_dict["type"] == vector_type:
skipped_count = skipped_count + 1 skipped_count = skipped_count + 1
continue continue
collection_name = "" collection_name = ""
dataset_id = dataset.id if vector_type == VectorType.WEAVIATE:
if vector_type in upper_colletion_vector_types: dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id) collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {"type": VectorType.WEAVIATE, "vector_store": {"class_prefix": collection_name}}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type == VectorType.QDRANT: elif vector_type == VectorType.QDRANT:
if dataset.collection_binding_id: if dataset.collection_binding_id:
dataset_collection_binding = ( dataset_collection_binding = (
@ -319,24 +299,66 @@ def migrate_knowledge_vector_database():
if dataset_collection_binding: if dataset_collection_binding:
collection_name = dataset_collection_binding.collection_name collection_name = dataset_collection_binding.collection_name
else: else:
raise ValueError("Dataset Collection Binding not found") raise ValueError("Dataset Collection Bindings is not exist!")
else: else:
dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id) collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {"type": VectorType.QDRANT, "vector_store": {"class_prefix": collection_name}}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type in lower_colletion_vector_types: elif vector_type == VectorType.MILVUS:
collection_name = Dataset.gen_collection_name_by_id(dataset_id).lower() dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {"type": VectorType.MILVUS, "vector_store": {"class_prefix": collection_name}}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type == VectorType.RELYT:
dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {"type": "relyt", "vector_store": {"class_prefix": collection_name}}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type == VectorType.TENCENT:
dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {"type": VectorType.TENCENT, "vector_store": {"class_prefix": collection_name}}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type == VectorType.PGVECTOR:
dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {"type": VectorType.PGVECTOR, "vector_store": {"class_prefix": collection_name}}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type == VectorType.OPENSEARCH:
dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {
"type": VectorType.OPENSEARCH,
"vector_store": {"class_prefix": collection_name},
}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type == VectorType.ANALYTICDB:
dataset_id = dataset.id
collection_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {
"type": VectorType.ANALYTICDB,
"vector_store": {"class_prefix": collection_name},
}
dataset.index_struct = json.dumps(index_struct_dict)
elif vector_type == VectorType.ELASTICSEARCH:
dataset_id = dataset.id
index_name = Dataset.gen_collection_name_by_id(dataset_id)
index_struct_dict = {"type": "elasticsearch", "vector_store": {"class_prefix": index_name}}
dataset.index_struct = json.dumps(index_struct_dict)
else: else:
raise ValueError(f"Vector store {vector_type} is not supported.") raise ValueError(f"Vector store {vector_type} is not supported.")
index_struct_dict = {"type": vector_type, "vector_store": {"class_prefix": collection_name}}
dataset.index_struct = json.dumps(index_struct_dict)
vector = Vector(dataset) vector = Vector(dataset)
click.echo(f"Migrating dataset {dataset.id}.") click.echo(f"Start to migrate dataset {dataset.id}.")
try: try:
vector.delete() vector.delete()
click.echo( click.echo(
click.style(f"Deleted vector index {collection_name} for dataset {dataset.id}.", fg="green") click.style(
f"Successfully delete vector index {collection_name} for dataset {dataset.id}.", fg="green"
)
) )
except Exception as e: except Exception as e:
click.echo( click.echo(
@ -388,13 +410,15 @@ def migrate_knowledge_vector_database():
try: try:
click.echo( click.echo(
click.style( click.style(
f"Creating vector index with {len(documents)} documents of {segments_count}" f"Start to created vector index with {len(documents)} documents of {segments_count}"
f" segments for dataset {dataset.id}.", f" segments for dataset {dataset.id}.",
fg="green", fg="green",
) )
) )
vector.create(documents) vector.create(documents)
click.echo(click.style(f"Created vector index for dataset {dataset.id}.", fg="green")) click.echo(
click.style(f"Successfully created vector index for dataset {dataset.id}.", fg="green")
)
except Exception as e: except Exception as e:
click.echo(click.style(f"Failed to created vector index for dataset {dataset.id}.", fg="red")) click.echo(click.style(f"Failed to created vector index for dataset {dataset.id}.", fg="red"))
raise e raise e
@ -405,13 +429,13 @@ def migrate_knowledge_vector_database():
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
click.echo( click.echo(
click.style("Error creating dataset index: {} {}".format(e.__class__.__name__, str(e)), fg="red") click.style("Create dataset index error: {} {}".format(e.__class__.__name__, str(e)), fg="red")
) )
continue continue
click.echo( click.echo(
click.style( click.style(
f"Migration complete. Created {create_count} dataset indexes. Skipped {skipped_count} datasets.", fg="green" f"Congratulations! Create {create_count} dataset indexes, and skipped {skipped_count} datasets.", fg="green"
) )
) )
@ -421,7 +445,7 @@ def convert_to_agent_apps():
""" """
Convert Agent Assistant to Agent App. Convert Agent Assistant to Agent App.
""" """
click.echo(click.style("Starting convert to agent apps.", fg="green")) click.echo(click.style("Start convert to agent apps.", fg="green"))
proceeded_app_ids = [] proceeded_app_ids = []
@ -429,14 +453,14 @@ def convert_to_agent_apps():
# fetch first 1000 apps # fetch first 1000 apps
sql_query = """SELECT a.id AS id FROM apps a sql_query = """SELECT a.id AS id FROM apps a
INNER JOIN app_model_configs am ON a.app_model_config_id=am.id INNER JOIN app_model_configs am ON a.app_model_config_id=am.id
WHERE a.mode = 'chat' WHERE a.mode = 'chat'
AND am.agent_mode is not null AND am.agent_mode is not null
AND ( AND (
am.agent_mode like '%"strategy": "function_call"%' am.agent_mode like '%"strategy": "function_call"%'
OR am.agent_mode like '%"strategy": "react"%' OR am.agent_mode like '%"strategy": "react"%'
) )
AND ( AND (
am.agent_mode like '{"enabled": true%' am.agent_mode like '{"enabled": true%'
OR am.agent_mode like '{"max_iteration": %' OR am.agent_mode like '{"max_iteration": %'
) ORDER BY a.created_at DESC LIMIT 1000 ) ORDER BY a.created_at DESC LIMIT 1000
""" """
@ -472,23 +496,23 @@ def convert_to_agent_apps():
except Exception as e: except Exception as e:
click.echo(click.style("Convert app error: {} {}".format(e.__class__.__name__, str(e)), fg="red")) click.echo(click.style("Convert app error: {} {}".format(e.__class__.__name__, str(e)), fg="red"))
click.echo(click.style("Conversion complete. Converted {} agent apps.".format(len(proceeded_app_ids)), fg="green")) click.echo(click.style("Congratulations! Converted {} agent apps.".format(len(proceeded_app_ids)), fg="green"))
@click.command("add-qdrant-doc-id-index", help="Add Qdrant doc_id index.") @click.command("add-qdrant-doc-id-index", help="add qdrant doc_id index.")
@click.option("--field", default="metadata.doc_id", prompt=False, help="Index field , default is metadata.doc_id.") @click.option("--field", default="metadata.doc_id", prompt=False, help="index field , default is metadata.doc_id.")
def add_qdrant_doc_id_index(field: str): def add_qdrant_doc_id_index(field: str):
click.echo(click.style("Starting Qdrant doc_id index creation.", fg="green")) click.echo(click.style("Start add qdrant doc_id index.", fg="green"))
vector_type = dify_config.VECTOR_STORE vector_type = dify_config.VECTOR_STORE
if vector_type != "qdrant": if vector_type != "qdrant":
click.echo(click.style("This command only supports Qdrant vector store.", fg="red")) click.echo(click.style("Sorry, only support qdrant vector store.", fg="red"))
return return
create_count = 0 create_count = 0
try: try:
bindings = db.session.query(DatasetCollectionBinding).all() bindings = db.session.query(DatasetCollectionBinding).all()
if not bindings: if not bindings:
click.echo(click.style("No dataset collection bindings found.", fg="red")) click.echo(click.style("Sorry, no dataset collection bindings found.", fg="red"))
return return
import qdrant_client import qdrant_client
from qdrant_client.http.exceptions import UnexpectedResponse from qdrant_client.http.exceptions import UnexpectedResponse
@ -498,7 +522,7 @@ def add_qdrant_doc_id_index(field: str):
for binding in bindings: for binding in bindings:
if dify_config.QDRANT_URL is None: if dify_config.QDRANT_URL is None:
raise ValueError("Qdrant URL is required.") raise ValueError("Qdrant url is required.")
qdrant_config = QdrantConfig( qdrant_config = QdrantConfig(
endpoint=dify_config.QDRANT_URL, endpoint=dify_config.QDRANT_URL,
api_key=dify_config.QDRANT_API_KEY, api_key=dify_config.QDRANT_API_KEY,
@ -515,39 +539,41 @@ def add_qdrant_doc_id_index(field: str):
except UnexpectedResponse as e: except UnexpectedResponse as e:
# Collection does not exist, so return # Collection does not exist, so return
if e.status_code == 404: if e.status_code == 404:
click.echo(click.style(f"Collection not found: {binding.collection_name}.", fg="red")) click.echo(
click.style(f"Collection not found, collection_name:{binding.collection_name}.", fg="red")
)
continue continue
# Some other error occurred, so re-raise the exception # Some other error occurred, so re-raise the exception
else: else:
click.echo( click.echo(
click.style( click.style(
f"Failed to create Qdrant index for collection: {binding.collection_name}.", fg="red" f"Failed to create qdrant index, collection_name:{binding.collection_name}.", fg="red"
) )
) )
except Exception as e: except Exception as e:
click.echo(click.style("Failed to create Qdrant client.", fg="red")) click.echo(click.style("Failed to create qdrant client.", fg="red"))
click.echo(click.style(f"Index creation complete. Created {create_count} collection indexes.", fg="green")) click.echo(click.style(f"Congratulations! Create {create_count} collection indexes.", fg="green"))
@click.command("create-tenant", help="Create account and tenant.") @click.command("create-tenant", help="Create account and tenant.")
@click.option("--email", prompt=True, help="Tenant account email.") @click.option("--email", prompt=True, help="The email address of the tenant account.")
@click.option("--name", prompt=True, help="Workspace name.") @click.option("--name", prompt=True, help="The workspace name of the tenant account.")
@click.option("--language", prompt=True, help="Account language, default: en-US.") @click.option("--language", prompt=True, help="Account language, default: en-US.")
def create_tenant(email: str, language: Optional[str] = None, name: Optional[str] = None): def create_tenant(email: str, language: Optional[str] = None, name: Optional[str] = None):
""" """
Create tenant account Create tenant account
""" """
if not email: if not email:
click.echo(click.style("Email is required.", fg="red")) click.echo(click.style("Sorry, email is required.", fg="red"))
return return
# Create account # Create account
email = email.strip() email = email.strip()
if "@" not in email: if "@" not in email:
click.echo(click.style("Invalid email address.", fg="red")) click.echo(click.style("Sorry, invalid email address.", fg="red"))
return return
account_name = email.split("@")[0] account_name = email.split("@")[0]
@ -567,19 +593,19 @@ def create_tenant(email: str, language: Optional[str] = None, name: Optional[str
click.echo( click.echo(
click.style( click.style(
"Account and tenant created.\nAccount: {}\nPassword: {}".format(email, new_password), "Congratulations! Account and tenant created.\nAccount: {}\nPassword: {}".format(email, new_password),
fg="green", fg="green",
) )
) )
@click.command("upgrade-db", help="Upgrade the database") @click.command("upgrade-db", help="upgrade the database")
def upgrade_db(): def upgrade_db():
click.echo("Preparing database migration...") click.echo("Preparing database migration...")
lock = redis_client.lock(name="db_upgrade_lock", timeout=60) lock = redis_client.lock(name="db_upgrade_lock", timeout=60)
if lock.acquire(blocking=False): if lock.acquire(blocking=False):
try: try:
click.echo(click.style("Starting database migration.", fg="green")) click.echo(click.style("Start database migration.", fg="green"))
# run db migration # run db migration
import flask_migrate import flask_migrate
@ -589,7 +615,7 @@ def upgrade_db():
click.echo(click.style("Database migration successful!", fg="green")) click.echo(click.style("Database migration successful!", fg="green"))
except Exception as e: except Exception as e:
logging.exception("Failed to execute database migration") logging.exception(f"Database migration failed, error: {e}")
finally: finally:
lock.release() lock.release()
else: else:
@ -601,7 +627,7 @@ def fix_app_site_missing():
""" """
Fix app related site missing issue. Fix app related site missing issue.
""" """
click.echo(click.style("Starting fix for missing app-related sites.", fg="green")) click.echo(click.style("Start fix app related site missing issue.", fg="green"))
failed_app_ids = [] failed_app_ids = []
while True: while True:
@ -624,22 +650,22 @@ where sites.id is null limit 1000"""
if tenant: if tenant:
accounts = tenant.get_accounts() accounts = tenant.get_accounts()
if not accounts: if not accounts:
print("Fix failed for app {}".format(app.id)) print("Fix app {} failed.".format(app.id))
continue continue
account = accounts[0] account = accounts[0]
print("Fixing missing site for app {}".format(app.id)) print("Fix app {} related site missing issue.".format(app.id))
app_was_created.send(app, account=account) app_was_created.send(app, account=account)
except Exception as e: except Exception as e:
failed_app_ids.append(app_id) failed_app_ids.append(app_id)
click.echo(click.style("Failed to fix missing site for app {}".format(app_id), fg="red")) click.echo(click.style("Fix app {} related site missing issue failed!".format(app_id), fg="red"))
logging.exception(f"Failed to fix app related site missing issue, app_id: {app_id}") logging.exception(f"Fix app related site missing issue failed, error: {e}")
continue continue
if not processed_count: if not processed_count:
break break
click.echo(click.style("Fix for missing app-related sites completed successfully!", fg="green")) click.echo(click.style("Congratulations! Fix app related site missing issue successful!", fg="green"))
def register_commands(app): def register_commands(app):

View File

@ -4,30 +4,30 @@ from pydantic_settings import BaseSettings
class DeploymentConfig(BaseSettings): class DeploymentConfig(BaseSettings):
""" """
Configuration settings for application deployment Deployment configs
""" """
APPLICATION_NAME: str = Field( APPLICATION_NAME: str = Field(
description="Name of the application, used for identification and logging purposes", description="application name",
default="langgenius/dify", default="langgenius/dify",
) )
DEBUG: bool = Field( DEBUG: bool = Field(
description="Enable debug mode for additional logging and development features", description="whether to enable debug mode.",
default=False, default=False,
) )
TESTING: bool = Field( TESTING: bool = Field(
description="Enable testing mode for running automated tests", description="",
default=False, default=False,
) )
EDITION: str = Field( EDITION: str = Field(
description="Deployment edition of the application (e.g., 'SELF_HOSTED', 'CLOUD')", description="deployment edition",
default="SELF_HOSTED", default="SELF_HOSTED",
) )
DEPLOY_ENV: str = Field( DEPLOY_ENV: str = Field(
description="Deployment environment (e.g., 'PRODUCTION', 'DEVELOPMENT'), default to PRODUCTION", description="deployment environment, default to PRODUCTION.",
default="PRODUCTION", default="PRODUCTION",
) )

View File

@ -4,17 +4,17 @@ from pydantic_settings import BaseSettings
class EnterpriseFeatureConfig(BaseSettings): class EnterpriseFeatureConfig(BaseSettings):
""" """
Configuration for enterprise-level features. Enterprise feature configs.
**Before using, please contact business@dify.ai by email to inquire about licensing matters.** **Before using, please contact business@dify.ai by email to inquire about licensing matters.**
""" """
ENTERPRISE_ENABLED: bool = Field( ENTERPRISE_ENABLED: bool = Field(
description="Enable or disable enterprise-level features." description="whether to enable enterprise features."
"Before using, please contact business@dify.ai by email to inquire about licensing matters.", "Before using, please contact business@dify.ai by email to inquire about licensing matters.",
default=False, default=False,
) )
CAN_REPLACE_LOGO: bool = Field( CAN_REPLACE_LOGO: bool = Field(
description="Allow customization of the enterprise logo.", description="whether to allow replacing enterprise logo.",
default=False, default=False,
) )

View File

@ -6,31 +6,30 @@ from pydantic_settings import BaseSettings
class NotionConfig(BaseSettings): class NotionConfig(BaseSettings):
""" """
Configuration settings for Notion integration Notion integration configs
""" """
NOTION_CLIENT_ID: Optional[str] = Field( NOTION_CLIENT_ID: Optional[str] = Field(
description="Client ID for Notion API authentication. Required for OAuth 2.0 flow.", description="Notion client ID",
default=None, default=None,
) )
NOTION_CLIENT_SECRET: Optional[str] = Field( NOTION_CLIENT_SECRET: Optional[str] = Field(
description="Client secret for Notion API authentication. Required for OAuth 2.0 flow.", description="Notion client secret key",
default=None, default=None,
) )
NOTION_INTEGRATION_TYPE: Optional[str] = Field( NOTION_INTEGRATION_TYPE: Optional[str] = Field(
description="Type of Notion integration." description="Notion integration type, default to None, available values: internal.",
" Set to 'internal' for internal integrations, or None for public integrations.",
default=None, default=None,
) )
NOTION_INTERNAL_SECRET: Optional[str] = Field( NOTION_INTERNAL_SECRET: Optional[str] = Field(
description="Secret key for internal Notion integrations. Required when NOTION_INTEGRATION_TYPE is 'internal'.", description="Notion internal secret key",
default=None, default=None,
) )
NOTION_INTEGRATION_TOKEN: Optional[str] = Field( NOTION_INTEGRATION_TOKEN: Optional[str] = Field(
description="Integration token for Notion API access. Used for direct API calls without OAuth flow.", description="Notion integration token",
default=None, default=None,
) )

View File

@ -6,23 +6,20 @@ from pydantic_settings import BaseSettings
class SentryConfig(BaseSettings): class SentryConfig(BaseSettings):
""" """
Configuration settings for Sentry error tracking and performance monitoring Sentry configs
""" """
SENTRY_DSN: Optional[str] = Field( SENTRY_DSN: Optional[str] = Field(
description="Sentry Data Source Name (DSN)." description="Sentry DSN",
" This is the unique identifier of your Sentry project, used to send events to the correct project.",
default=None, default=None,
) )
SENTRY_TRACES_SAMPLE_RATE: NonNegativeFloat = Field( SENTRY_TRACES_SAMPLE_RATE: NonNegativeFloat = Field(
description="Sample rate for Sentry performance monitoring traces." description="Sentry trace sample rate",
" Value between 0.0 and 1.0, where 1.0 means 100% of traces are sent to Sentry.",
default=1.0, default=1.0,
) )
SENTRY_PROFILES_SAMPLE_RATE: NonNegativeFloat = Field( SENTRY_PROFILES_SAMPLE_RATE: NonNegativeFloat = Field(
description="Sample rate for Sentry profiling." description="Sentry profiles sample rate",
" Value between 0.0 and 1.0, where 1.0 means 100% of profiles are sent to Sentry.",
default=1.0, default=1.0,
) )

View File

@ -1,15 +1,6 @@
from typing import Annotated, Literal, Optional from typing import Annotated, Optional
from pydantic import ( from pydantic import AliasChoices, Field, HttpUrl, NegativeInt, NonNegativeInt, PositiveInt, computed_field
AliasChoices,
Field,
HttpUrl,
NegativeInt,
NonNegativeInt,
PositiveFloat,
PositiveInt,
computed_field,
)
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from configs.feature.hosted_service import HostedServiceConfig from configs.feature.hosted_service import HostedServiceConfig
@ -17,158 +8,145 @@ from configs.feature.hosted_service import HostedServiceConfig
class SecurityConfig(BaseSettings): class SecurityConfig(BaseSettings):
""" """
Security-related configurations for the application Secret Key configs
""" """
SECRET_KEY: str = Field( SECRET_KEY: Optional[str] = Field(
description="Secret key for secure session cookie signing." description="Your App secret key will be used for securely signing the session cookie"
"Make sure you are changing this key for your deployment with a strong key." "Make sure you are changing this key for your deployment with a strong key."
"Generate a strong key using `openssl rand -base64 42` or set via the `SECRET_KEY` environment variable.", "You can generate a strong key using `openssl rand -base64 42`."
default="", "Alternatively you can set it with `SECRET_KEY` environment variable.",
)
RESET_PASSWORD_TOKEN_EXPIRY_MINUTES: PositiveInt = Field(
description="Duration in minutes for which a password reset token remains valid",
default=5,
)
LOGIN_DISABLED: bool = Field(
description="Whether to disable login checks",
default=False,
)
ADMIN_API_KEY_ENABLE: bool = Field(
description="Whether to enable admin api key for authentication",
default=False,
)
ADMIN_API_KEY: Optional[str] = Field(
description="admin api key for authentication",
default=None, default=None,
) )
RESET_PASSWORD_TOKEN_EXPIRY_HOURS: PositiveInt = Field(
description="Expiry time in hours for reset token",
default=24,
)
class AppExecutionConfig(BaseSettings): class AppExecutionConfig(BaseSettings):
""" """
Configuration parameters for application execution App Execution configs
""" """
APP_MAX_EXECUTION_TIME: PositiveInt = Field( APP_MAX_EXECUTION_TIME: PositiveInt = Field(
description="Maximum allowed execution time for the application in seconds", description="execution timeout in seconds for app execution",
default=1200, default=1200,
) )
APP_MAX_ACTIVE_REQUESTS: NonNegativeInt = Field( APP_MAX_ACTIVE_REQUESTS: NonNegativeInt = Field(
description="Maximum number of concurrent active requests per app (0 for unlimited)", description="max active request per app, 0 means unlimited",
default=0, default=0,
) )
class CodeExecutionSandboxConfig(BaseSettings): class CodeExecutionSandboxConfig(BaseSettings):
""" """
Configuration for the code execution sandbox environment Code Execution Sandbox configs
""" """
CODE_EXECUTION_ENDPOINT: HttpUrl = Field( CODE_EXECUTION_ENDPOINT: HttpUrl = Field(
description="URL endpoint for the code execution service", description="endpoint URL of code execution service",
default="http://sandbox:8194", default="http://sandbox:8194",
) )
CODE_EXECUTION_API_KEY: str = Field( CODE_EXECUTION_API_KEY: str = Field(
description="API key for accessing the code execution service", description="API key for code execution service",
default="dify-sandbox", default="dify-sandbox",
) )
CODE_EXECUTION_CONNECT_TIMEOUT: Optional[float] = Field( CODE_EXECUTION_CONNECT_TIMEOUT: Optional[float] = Field(
description="Connection timeout in seconds for code execution requests", description="connect timeout in seconds for code execution request",
default=10.0, default=10.0,
) )
CODE_EXECUTION_READ_TIMEOUT: Optional[float] = Field( CODE_EXECUTION_READ_TIMEOUT: Optional[float] = Field(
description="Read timeout in seconds for code execution requests", description="read timeout in seconds for code execution request",
default=60.0, default=60.0,
) )
CODE_EXECUTION_WRITE_TIMEOUT: Optional[float] = Field( CODE_EXECUTION_WRITE_TIMEOUT: Optional[float] = Field(
description="Write timeout in seconds for code execution request", description="write timeout in seconds for code execution request",
default=10.0, default=10.0,
) )
CODE_MAX_NUMBER: PositiveInt = Field( CODE_MAX_NUMBER: PositiveInt = Field(
description="Maximum allowed numeric value in code execution", description="max depth for code execution",
default=9223372036854775807, default=9223372036854775807,
) )
CODE_MIN_NUMBER: NegativeInt = Field( CODE_MIN_NUMBER: NegativeInt = Field(
description="Minimum allowed numeric value in code execution", description="",
default=-9223372036854775807, default=-9223372036854775807,
) )
CODE_MAX_DEPTH: PositiveInt = Field( CODE_MAX_DEPTH: PositiveInt = Field(
description="Maximum allowed depth for nested structures in code execution", description="max depth for code execution",
default=5, default=5,
) )
CODE_MAX_PRECISION: PositiveInt = Field( CODE_MAX_PRECISION: PositiveInt = Field(
description="Maximum number of decimal places for floating-point numbers in code execution", description="max precision digits for float type in code execution",
default=20, default=20,
) )
CODE_MAX_STRING_LENGTH: PositiveInt = Field( CODE_MAX_STRING_LENGTH: PositiveInt = Field(
description="Maximum allowed length for strings in code execution", description="max string length for code execution",
default=80000, default=80000,
) )
CODE_MAX_STRING_ARRAY_LENGTH: PositiveInt = Field( CODE_MAX_STRING_ARRAY_LENGTH: PositiveInt = Field(
description="Maximum allowed length for string arrays in code execution", description="",
default=30, default=30,
) )
CODE_MAX_OBJECT_ARRAY_LENGTH: PositiveInt = Field( CODE_MAX_OBJECT_ARRAY_LENGTH: PositiveInt = Field(
description="Maximum allowed length for object arrays in code execution", description="",
default=30, default=30,
) )
CODE_MAX_NUMBER_ARRAY_LENGTH: PositiveInt = Field( CODE_MAX_NUMBER_ARRAY_LENGTH: PositiveInt = Field(
description="Maximum allowed length for numeric arrays in code execution", description="",
default=1000, default=1000,
) )
class EndpointConfig(BaseSettings): class EndpointConfig(BaseSettings):
""" """
Configuration for various application endpoints and URLs Module URL configs
""" """
CONSOLE_API_URL: str = Field( CONSOLE_API_URL: str = Field(
description="Base URL for the console API," description="The backend URL prefix of the console API."
"used for login authentication callback or notion integration callbacks", "used to concatenate the login authorization callback or notion integration callback.",
default="", default="",
) )
CONSOLE_WEB_URL: str = Field( CONSOLE_WEB_URL: str = Field(
description="Base URL for the console web interface," "used for frontend references and CORS configuration", description="The front-end URL prefix of the console web."
"used to concatenate some front-end addresses and for CORS configuration use.",
default="", default="",
) )
SERVICE_API_URL: str = Field( SERVICE_API_URL: str = Field(
description="Base URL for the service API, displayed to users for API access", description="Service API Url prefix. used to display Service API Base Url to the front-end.",
default="", default="",
) )
APP_WEB_URL: str = Field( APP_WEB_URL: str = Field(
description="Base URL for the web application, used for frontend references", description="WebApp Url prefix. used to display WebAPP API Base Url to the front-end.",
default="", default="",
) )
class FileAccessConfig(BaseSettings): class FileAccessConfig(BaseSettings):
""" """
Configuration for file access and handling File Access configs
""" """
FILES_URL: str = Field( FILES_URL: str = Field(
description="Base URL for file preview or download," description="File preview or download Url prefix."
" used for frontend display and multi-model inputs" " used to display File preview or download Url to the front-end or as Multi-model inputs;"
"Url is signed and has expiration time.", "Url is signed and has expiration time.",
validation_alias=AliasChoices("FILES_URL", "CONSOLE_API_URL"), validation_alias=AliasChoices("FILES_URL", "CONSOLE_API_URL"),
alias_priority=1, alias_priority=1,
@ -176,64 +154,49 @@ class FileAccessConfig(BaseSettings):
) )
FILES_ACCESS_TIMEOUT: int = Field( FILES_ACCESS_TIMEOUT: int = Field(
description="Expiration time in seconds for file access URLs", description="timeout in seconds for file accessing",
default=300, default=300,
) )
class FileUploadConfig(BaseSettings): class FileUploadConfig(BaseSettings):
""" """
Configuration for file upload limitations File Uploading configs
""" """
UPLOAD_FILE_SIZE_LIMIT: NonNegativeInt = Field( UPLOAD_FILE_SIZE_LIMIT: NonNegativeInt = Field(
description="Maximum allowed file size for uploads in megabytes", description="size limit in Megabytes for uploading files",
default=15, default=15,
) )
UPLOAD_FILE_BATCH_LIMIT: NonNegativeInt = Field( UPLOAD_FILE_BATCH_LIMIT: NonNegativeInt = Field(
description="Maximum number of files allowed in a single upload batch", description="batch size limit for uploading files",
default=5, default=5,
) )
UPLOAD_IMAGE_FILE_SIZE_LIMIT: NonNegativeInt = Field( UPLOAD_IMAGE_FILE_SIZE_LIMIT: NonNegativeInt = Field(
description="Maximum allowed image file size for uploads in megabytes", description="image file size limit in Megabytes for uploading files",
default=10, default=10,
) )
UPLOAD_VIDEO_FILE_SIZE_LIMIT: NonNegativeInt = Field(
description="video file size limit in Megabytes for uploading files",
default=100,
)
UPLOAD_AUDIO_FILE_SIZE_LIMIT: NonNegativeInt = Field(
description="audio file size limit in Megabytes for uploading files",
default=50,
)
BATCH_UPLOAD_LIMIT: NonNegativeInt = Field( BATCH_UPLOAD_LIMIT: NonNegativeInt = Field(
description="Maximum number of files allowed in a batch upload operation", description="", # todo: to be clarified
default=20, default=20,
) )
WORKFLOW_FILE_UPLOAD_LIMIT: PositiveInt = Field(
description="Maximum number of files allowed in a workflow upload operation",
default=10,
)
class HttpConfig(BaseSettings): class HttpConfig(BaseSettings):
""" """
HTTP-related configurations for the application HTTP configs
""" """
API_COMPRESSION_ENABLED: bool = Field( API_COMPRESSION_ENABLED: bool = Field(
description="Enable or disable gzip compression for HTTP responses", description="whether to enable HTTP response compression of gzip",
default=False, default=False,
) )
inner_CONSOLE_CORS_ALLOW_ORIGINS: str = Field( inner_CONSOLE_CORS_ALLOW_ORIGINS: str = Field(
description="Comma-separated list of allowed origins for CORS in the console", description="",
validation_alias=AliasChoices("CONSOLE_CORS_ALLOW_ORIGINS", "CONSOLE_WEB_URL"), validation_alias=AliasChoices("CONSOLE_CORS_ALLOW_ORIGINS", "CONSOLE_WEB_URL"),
default="", default="",
) )
@ -255,442 +218,359 @@ class HttpConfig(BaseSettings):
return self.inner_WEB_API_CORS_ALLOW_ORIGINS.split(",") return self.inner_WEB_API_CORS_ALLOW_ORIGINS.split(",")
HTTP_REQUEST_MAX_CONNECT_TIMEOUT: Annotated[ HTTP_REQUEST_MAX_CONNECT_TIMEOUT: Annotated[
PositiveInt, Field(ge=10, description="Maximum connection timeout in seconds for HTTP requests") PositiveInt, Field(ge=10, description="connect timeout in seconds for HTTP request")
] = 10 ] = 10
HTTP_REQUEST_MAX_READ_TIMEOUT: Annotated[ HTTP_REQUEST_MAX_READ_TIMEOUT: Annotated[
PositiveInt, Field(ge=60, description="Maximum read timeout in seconds for HTTP requests") PositiveInt, Field(ge=60, description="read timeout in seconds for HTTP request")
] = 60 ] = 60
HTTP_REQUEST_MAX_WRITE_TIMEOUT: Annotated[ HTTP_REQUEST_MAX_WRITE_TIMEOUT: Annotated[
PositiveInt, Field(ge=10, description="Maximum write timeout in seconds for HTTP requests") PositiveInt, Field(ge=10, description="read timeout in seconds for HTTP request")
] = 20 ] = 20
HTTP_REQUEST_NODE_MAX_BINARY_SIZE: PositiveInt = Field( HTTP_REQUEST_NODE_MAX_BINARY_SIZE: PositiveInt = Field(
description="Maximum allowed size in bytes for binary data in HTTP requests", description="",
default=10 * 1024 * 1024, default=10 * 1024 * 1024,
) )
HTTP_REQUEST_NODE_MAX_TEXT_SIZE: PositiveInt = Field( HTTP_REQUEST_NODE_MAX_TEXT_SIZE: PositiveInt = Field(
description="Maximum allowed size in bytes for text data in HTTP requests", description="",
default=1 * 1024 * 1024, default=1 * 1024 * 1024,
) )
SSRF_DEFAULT_MAX_RETRIES: PositiveInt = Field(
description="Maximum number of retries for network requests (SSRF)",
default=3,
)
SSRF_PROXY_ALL_URL: Optional[str] = Field(
description="Proxy URL for HTTP or HTTPS requests to prevent Server-Side Request Forgery (SSRF)",
default=None,
)
SSRF_PROXY_HTTP_URL: Optional[str] = Field( SSRF_PROXY_HTTP_URL: Optional[str] = Field(
description="Proxy URL for HTTP requests to prevent Server-Side Request Forgery (SSRF)", description="HTTP URL for SSRF proxy",
default=None, default=None,
) )
SSRF_PROXY_HTTPS_URL: Optional[str] = Field( SSRF_PROXY_HTTPS_URL: Optional[str] = Field(
description="Proxy URL for HTTPS requests to prevent Server-Side Request Forgery (SSRF)", description="HTTPS URL for SSRF proxy",
default=None, default=None,
) )
SSRF_DEFAULT_TIME_OUT: PositiveFloat = Field(
description="The default timeout period used for network requests (SSRF)",
default=5,
)
SSRF_DEFAULT_CONNECT_TIME_OUT: PositiveFloat = Field(
description="The default connect timeout period used for network requests (SSRF)",
default=5,
)
SSRF_DEFAULT_READ_TIME_OUT: PositiveFloat = Field(
description="The default read timeout period used for network requests (SSRF)",
default=5,
)
SSRF_DEFAULT_WRITE_TIME_OUT: PositiveFloat = Field(
description="The default write timeout period used for network requests (SSRF)",
default=5,
)
RESPECT_XFORWARD_HEADERS_ENABLED: bool = Field(
description="Enable or disable the X-Forwarded-For Proxy Fix middleware from Werkzeug"
" to respect X-* headers to redirect clients",
default=False,
)
class InnerAPIConfig(BaseSettings): class InnerAPIConfig(BaseSettings):
""" """
Configuration for internal API functionality Inner API configs
""" """
INNER_API: bool = Field( INNER_API: bool = Field(
description="Enable or disable the internal API", description="whether to enable the inner API",
default=False, default=False,
) )
INNER_API_KEY: Optional[str] = Field( INNER_API_KEY: Optional[str] = Field(
description="API key for accessing the internal API", description="The inner API key is used to authenticate the inner API",
default=None, default=None,
) )
class LoggingConfig(BaseSettings): class LoggingConfig(BaseSettings):
""" """
Configuration for application logging Logging configs
""" """
LOG_LEVEL: str = Field( LOG_LEVEL: str = Field(
description="Logging level, default to INFO. Set to ERROR for production environments.", description="Log output level, default to INFO. It is recommended to set it to ERROR for production.",
default="INFO", default="INFO",
) )
LOG_FILE: Optional[str] = Field( LOG_FILE: Optional[str] = Field(
description="File path for log output.", description="logging output file path",
default=None, default=None,
) )
LOG_FILE_MAX_SIZE: PositiveInt = Field(
description="Maximum file size for file rotation retention, the unit is megabytes (MB)",
default=20,
)
LOG_FILE_BACKUP_COUNT: PositiveInt = Field(
description="Maximum file backup count file rotation retention",
default=5,
)
LOG_FORMAT: str = Field( LOG_FORMAT: str = Field(
description="Format string for log messages", description="log format",
default="%(asctime)s.%(msecs)03d %(levelname)s [%(threadName)s] [%(filename)s:%(lineno)d] - %(message)s", default="%(asctime)s.%(msecs)03d %(levelname)s [%(threadName)s] [%(filename)s:%(lineno)d] - %(message)s",
) )
LOG_DATEFORMAT: Optional[str] = Field( LOG_DATEFORMAT: Optional[str] = Field(
description="Date format string for log timestamps", description="log date format",
default=None, default=None,
) )
LOG_TZ: Optional[str] = Field( LOG_TZ: Optional[str] = Field(
description="Timezone for log timestamps (e.g., 'America/New_York')", description="specify log timezone, eg: America/New_York",
default="UTC", default=None,
) )
class ModelLoadBalanceConfig(BaseSettings): class ModelLoadBalanceConfig(BaseSettings):
""" """
Configuration for model load balancing Model load balance configs
""" """
MODEL_LB_ENABLED: bool = Field( MODEL_LB_ENABLED: bool = Field(
description="Enable or disable load balancing for models", description="whether to enable model load balancing",
default=False, default=False,
) )
class BillingConfig(BaseSettings): class BillingConfig(BaseSettings):
""" """
Configuration for platform billing features Platform Billing Configurations
""" """
BILLING_ENABLED: bool = Field( BILLING_ENABLED: bool = Field(
description="Enable or disable billing functionality", description="whether to enable billing",
default=False, default=False,
) )
class UpdateConfig(BaseSettings): class UpdateConfig(BaseSettings):
""" """
Configuration for application update checks Update configs
""" """
CHECK_UPDATE_URL: str = Field( CHECK_UPDATE_URL: str = Field(
description="URL to check for application updates", description="url for checking updates",
default="https://updates.dify.ai", default="https://updates.dify.ai",
) )
class WorkflowConfig(BaseSettings): class WorkflowConfig(BaseSettings):
""" """
Configuration for workflow execution Workflow feature configs
""" """
WORKFLOW_MAX_EXECUTION_STEPS: PositiveInt = Field( WORKFLOW_MAX_EXECUTION_STEPS: PositiveInt = Field(
description="Maximum number of steps allowed in a single workflow execution", description="max execution steps in single workflow execution",
default=500, default=500,
) )
WORKFLOW_MAX_EXECUTION_TIME: PositiveInt = Field( WORKFLOW_MAX_EXECUTION_TIME: PositiveInt = Field(
description="Maximum execution time in seconds for a single workflow", description="max execution time in seconds in single workflow execution",
default=1200, default=1200,
) )
WORKFLOW_CALL_MAX_DEPTH: PositiveInt = Field( WORKFLOW_CALL_MAX_DEPTH: PositiveInt = Field(
description="Maximum allowed depth for nested workflow calls", description="max depth of calling in single workflow execution",
default=5, default=5,
) )
MAX_VARIABLE_SIZE: PositiveInt = Field( MAX_VARIABLE_SIZE: PositiveInt = Field(
description="Maximum size in bytes for a single variable in workflows. Default to 200 KB.", description="The maximum size in bytes of a variable. default to 5KB.",
default=200 * 1024, default=5 * 1024,
) )
class AuthConfig(BaseSettings): class OAuthConfig(BaseSettings):
""" """
Configuration for authentication and OAuth oauth configs
""" """
OAUTH_REDIRECT_PATH: str = Field( OAUTH_REDIRECT_PATH: str = Field(
description="Redirect path for OAuth authentication callbacks", description="redirect path for OAuth",
default="/console/api/oauth/authorize", default="/console/api/oauth/authorize",
) )
GITHUB_CLIENT_ID: Optional[str] = Field( GITHUB_CLIENT_ID: Optional[str] = Field(
description="GitHub OAuth client ID", description="GitHub client id for OAuth",
default=None, default=None,
) )
GITHUB_CLIENT_SECRET: Optional[str] = Field( GITHUB_CLIENT_SECRET: Optional[str] = Field(
description="GitHub OAuth client secret", description="GitHub client secret key for OAuth",
default=None, default=None,
) )
GOOGLE_CLIENT_ID: Optional[str] = Field( GOOGLE_CLIENT_ID: Optional[str] = Field(
description="Google OAuth client ID", description="Google client id for OAuth",
default=None, default=None,
) )
GOOGLE_CLIENT_SECRET: Optional[str] = Field( GOOGLE_CLIENT_SECRET: Optional[str] = Field(
description="Google OAuth client secret", description="Google client secret key for OAuth",
default=None, default=None,
) )
ACCESS_TOKEN_EXPIRE_MINUTES: PositiveInt = Field(
description="Expiration time for access tokens in minutes",
default=60,
)
class ModerationConfig(BaseSettings): class ModerationConfig(BaseSettings):
""" """
Configuration for content moderation Moderation in app configs.
""" """
MODERATION_BUFFER_SIZE: PositiveInt = Field( MODERATION_BUFFER_SIZE: PositiveInt = Field(
description="Size of the buffer for content moderation processing", description="buffer size for moderation",
default=300, default=300,
) )
class ToolConfig(BaseSettings): class ToolConfig(BaseSettings):
""" """
Configuration for tool management Tool configs
""" """
TOOL_ICON_CACHE_MAX_AGE: PositiveInt = Field( TOOL_ICON_CACHE_MAX_AGE: PositiveInt = Field(
description="Maximum age in seconds for caching tool icons", description="max age in seconds for tool icon caching",
default=3600, default=3600,
) )
class MailConfig(BaseSettings): class MailConfig(BaseSettings):
""" """
Configuration for email services Mail Configurations
""" """
MAIL_TYPE: Optional[str] = Field( MAIL_TYPE: Optional[str] = Field(
description="Email service provider type ('smtp' or 'resend'), default to None.", description="Mail provider type name, default to None, available values are `smtp` and `resend`.",
default=None, default=None,
) )
MAIL_DEFAULT_SEND_FROM: Optional[str] = Field( MAIL_DEFAULT_SEND_FROM: Optional[str] = Field(
description="Default email address to use as the sender", description="default email address for sending from ",
default=None, default=None,
) )
RESEND_API_KEY: Optional[str] = Field( RESEND_API_KEY: Optional[str] = Field(
description="API key for Resend email service", description="API key for Resend",
default=None, default=None,
) )
RESEND_API_URL: Optional[str] = Field( RESEND_API_URL: Optional[str] = Field(
description="API URL for Resend email service", description="API URL for Resend",
default=None, default=None,
) )
SMTP_SERVER: Optional[str] = Field( SMTP_SERVER: Optional[str] = Field(
description="SMTP server hostname", description="smtp server host",
default=None, default=None,
) )
SMTP_PORT: Optional[int] = Field( SMTP_PORT: Optional[int] = Field(
description="SMTP server port number", description="smtp server port",
default=465, default=465,
) )
SMTP_USERNAME: Optional[str] = Field( SMTP_USERNAME: Optional[str] = Field(
description="Username for SMTP authentication", description="smtp server username",
default=None, default=None,
) )
SMTP_PASSWORD: Optional[str] = Field( SMTP_PASSWORD: Optional[str] = Field(
description="Password for SMTP authentication", description="smtp server password",
default=None, default=None,
) )
SMTP_USE_TLS: bool = Field( SMTP_USE_TLS: bool = Field(
description="Enable TLS encryption for SMTP connections", description="whether to use TLS connection to smtp server",
default=False, default=False,
) )
SMTP_OPPORTUNISTIC_TLS: bool = Field( SMTP_OPPORTUNISTIC_TLS: bool = Field(
description="Enable opportunistic TLS for SMTP connections", description="whether to use opportunistic TLS connection to smtp server",
default=False, default=False,
) )
EMAIL_SEND_IP_LIMIT_PER_MINUTE: PositiveInt = Field(
description="Maximum number of emails allowed to be sent from the same IP address in a minute",
default=50,
)
class RagEtlConfig(BaseSettings): class RagEtlConfig(BaseSettings):
""" """
Configuration for RAG ETL processes RAG ETL Configurations.
""" """
# TODO: This config is not only for rag etl, it is also for file upload, we should move it to file upload config
ETL_TYPE: str = Field( ETL_TYPE: str = Field(
description="RAG ETL type ('dify' or 'Unstructured'), default to 'dify'", description="RAG ETL type name, default to `dify`, available values are `dify` and `Unstructured`. ",
default="dify", default="dify",
) )
KEYWORD_DATA_SOURCE_TYPE: str = Field( KEYWORD_DATA_SOURCE_TYPE: str = Field(
description="Data source type for keyword extraction" description="source type for keyword data, default to `database`, available values are `database` .",
" ('database' or other supported types), default to 'database'",
default="database", default="database",
) )
UNSTRUCTURED_API_URL: Optional[str] = Field( UNSTRUCTURED_API_URL: Optional[str] = Field(
description="API URL for Unstructured.io service", description="API URL for Unstructured",
default=None, default=None,
) )
UNSTRUCTURED_API_KEY: Optional[str] = Field( UNSTRUCTURED_API_KEY: Optional[str] = Field(
description="API key for Unstructured.io service", description="API key for Unstructured",
default=None, default=None,
) )
class DataSetConfig(BaseSettings): class DataSetConfig(BaseSettings):
""" """
Configuration for dataset management Dataset configs
""" """
PLAN_SANDBOX_CLEAN_DAY_SETTING: PositiveInt = Field( CLEAN_DAY_SETTING: PositiveInt = Field(
description="Interval in days for dataset cleanup operations - plan: sandbox", description="interval in days for cleaning up dataset",
default=30, default=30,
) )
PLAN_PRO_CLEAN_DAY_SETTING: PositiveInt = Field(
description="Interval in days for dataset cleanup operations - plan: pro and team",
default=7,
)
DATASET_OPERATOR_ENABLED: bool = Field( DATASET_OPERATOR_ENABLED: bool = Field(
description="Enable or disable dataset operator functionality", description="whether to enable dataset operator",
default=False, default=False,
) )
TIDB_SERVERLESS_NUMBER: PositiveInt = Field(
description="number of tidb serverless cluster",
default=500,
)
CREATE_TIDB_SERVICE_JOB_ENABLED: bool = Field(
description="Enable or disable create tidb service job",
default=False,
)
PLAN_SANDBOX_CLEAN_MESSAGE_DAY_SETTING: PositiveInt = Field(
description="Interval in days for message cleanup operations - plan: sandbox",
default=30,
)
class WorkspaceConfig(BaseSettings): class WorkspaceConfig(BaseSettings):
""" """
Configuration for workspace management Workspace configs
""" """
INVITE_EXPIRY_HOURS: PositiveInt = Field( INVITE_EXPIRY_HOURS: PositiveInt = Field(
description="Expiration time in hours for workspace invitation links", description="workspaces invitation expiration in hours",
default=72, default=72,
) )
class IndexingConfig(BaseSettings): class IndexingConfig(BaseSettings):
""" """
Configuration for indexing operations Indexing configs.
""" """
INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: PositiveInt = Field( INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: PositiveInt = Field(
description="Maximum token length for text segmentation during indexing", description="max segmentation token length for indexing",
default=1000, default=1000,
) )
class VisionFormatConfig(BaseSettings): class ImageFormatConfig(BaseSettings):
MULTIMODAL_SEND_IMAGE_FORMAT: Literal["base64", "url"] = Field( MULTIMODAL_SEND_IMAGE_FORMAT: str = Field(
description="Format for sending images in multimodal contexts ('base64' or 'url'), default is base64", description="multi model send image format, support base64, url, default is base64",
default="base64",
)
MULTIMODAL_SEND_VIDEO_FORMAT: Literal["base64", "url"] = Field(
description="Format for sending videos in multimodal contexts ('base64' or 'url'), default is base64",
default="base64", default="base64",
) )
class CeleryBeatConfig(BaseSettings): class CeleryBeatConfig(BaseSettings):
CELERY_BEAT_SCHEDULER_TIME: int = Field( CELERY_BEAT_SCHEDULER_TIME: int = Field(
description="Interval in days for Celery Beat scheduler execution, default to 1 day", description="the time of the celery scheduler, default to 1 day",
default=1, default=1,
) )
class PositionConfig(BaseSettings): class PositionConfig(BaseSettings):
POSITION_PROVIDER_PINS: str = Field( POSITION_PROVIDER_PINS: str = Field(
description="Comma-separated list of pinned model providers", description="The heads of model providers",
default="", default="",
) )
POSITION_PROVIDER_INCLUDES: str = Field( POSITION_PROVIDER_INCLUDES: str = Field(
description="Comma-separated list of included model providers", description="The included model providers",
default="", default="",
) )
POSITION_PROVIDER_EXCLUDES: str = Field( POSITION_PROVIDER_EXCLUDES: str = Field(
description="Comma-separated list of excluded model providers", description="The excluded model providers",
default="", default="",
) )
POSITION_TOOL_PINS: str = Field( POSITION_TOOL_PINS: str = Field(
description="Comma-separated list of pinned tools", description="The heads of tools",
default="", default="",
) )
POSITION_TOOL_INCLUDES: str = Field( POSITION_TOOL_INCLUDES: str = Field(
description="Comma-separated list of included tools", description="The included tools",
default="", default="",
) )
POSITION_TOOL_EXCLUDES: str = Field( POSITION_TOOL_EXCLUDES: str = Field(
description="Comma-separated list of excluded tools", description="The excluded tools",
default="", default="",
) )
@ -719,37 +599,9 @@ class PositionConfig(BaseSettings):
return {item.strip() for item in self.POSITION_TOOL_EXCLUDES.split(",") if item.strip() != ""} return {item.strip() for item in self.POSITION_TOOL_EXCLUDES.split(",") if item.strip() != ""}
class LoginConfig(BaseSettings):
ENABLE_EMAIL_CODE_LOGIN: bool = Field(
description="whether to enable email code login",
default=False,
)
ENABLE_EMAIL_PASSWORD_LOGIN: bool = Field(
description="whether to enable email password login",
default=True,
)
ENABLE_SOCIAL_OAUTH_LOGIN: bool = Field(
description="whether to enable github/google oauth login",
default=False,
)
EMAIL_CODE_LOGIN_TOKEN_EXPIRY_MINUTES: PositiveInt = Field(
description="expiry time in minutes for email code login token",
default=5,
)
ALLOW_REGISTER: bool = Field(
description="whether to enable register",
default=False,
)
ALLOW_CREATE_WORKSPACE: bool = Field(
description="whether to enable create workspace",
default=False,
)
class FeatureConfig( class FeatureConfig(
# place the configs in alphabet order # place the configs in alphabet order
AppExecutionConfig, AppExecutionConfig,
AuthConfig, # Changed from OAuthConfig to AuthConfig
BillingConfig, BillingConfig,
CodeExecutionSandboxConfig, CodeExecutionSandboxConfig,
DataSetConfig, DataSetConfig,
@ -757,21 +609,21 @@ class FeatureConfig(
FileAccessConfig, FileAccessConfig,
FileUploadConfig, FileUploadConfig,
HttpConfig, HttpConfig,
VisionFormatConfig, ImageFormatConfig,
InnerAPIConfig, InnerAPIConfig,
IndexingConfig, IndexingConfig,
LoggingConfig, LoggingConfig,
MailConfig, MailConfig,
ModelLoadBalanceConfig, ModelLoadBalanceConfig,
ModerationConfig, ModerationConfig,
PositionConfig, OAuthConfig,
RagEtlConfig, RagEtlConfig,
SecurityConfig, SecurityConfig,
ToolConfig, ToolConfig,
UpdateConfig, UpdateConfig,
WorkflowConfig, WorkflowConfig,
WorkspaceConfig, WorkspaceConfig,
LoginConfig, PositionConfig,
# hosted services config # hosted services config
HostedServiceConfig, HostedServiceConfig,
CeleryBeatConfig, CeleryBeatConfig,

View File

@ -6,31 +6,31 @@ from pydantic_settings import BaseSettings
class HostedOpenAiConfig(BaseSettings): class HostedOpenAiConfig(BaseSettings):
""" """
Configuration for hosted OpenAI service Hosted OpenAI service config
""" """
HOSTED_OPENAI_API_KEY: Optional[str] = Field( HOSTED_OPENAI_API_KEY: Optional[str] = Field(
description="API key for hosted OpenAI service", description="",
default=None, default=None,
) )
HOSTED_OPENAI_API_BASE: Optional[str] = Field( HOSTED_OPENAI_API_BASE: Optional[str] = Field(
description="Base URL for hosted OpenAI API", description="",
default=None, default=None,
) )
HOSTED_OPENAI_API_ORGANIZATION: Optional[str] = Field( HOSTED_OPENAI_API_ORGANIZATION: Optional[str] = Field(
description="Organization ID for hosted OpenAI service", description="",
default=None, default=None,
) )
HOSTED_OPENAI_TRIAL_ENABLED: bool = Field( HOSTED_OPENAI_TRIAL_ENABLED: bool = Field(
description="Enable trial access to hosted OpenAI service", description="",
default=False, default=False,
) )
HOSTED_OPENAI_TRIAL_MODELS: str = Field( HOSTED_OPENAI_TRIAL_MODELS: str = Field(
description="Comma-separated list of available models for trial access", description="",
default="gpt-3.5-turbo," default="gpt-3.5-turbo,"
"gpt-3.5-turbo-1106," "gpt-3.5-turbo-1106,"
"gpt-3.5-turbo-instruct," "gpt-3.5-turbo-instruct,"
@ -42,17 +42,17 @@ class HostedOpenAiConfig(BaseSettings):
) )
HOSTED_OPENAI_QUOTA_LIMIT: NonNegativeInt = Field( HOSTED_OPENAI_QUOTA_LIMIT: NonNegativeInt = Field(
description="Quota limit for hosted OpenAI service usage", description="",
default=200, default=200,
) )
HOSTED_OPENAI_PAID_ENABLED: bool = Field( HOSTED_OPENAI_PAID_ENABLED: bool = Field(
description="Enable paid access to hosted OpenAI service", description="",
default=False, default=False,
) )
HOSTED_OPENAI_PAID_MODELS: str = Field( HOSTED_OPENAI_PAID_MODELS: str = Field(
description="Comma-separated list of available models for paid access", description="",
default="gpt-4," default="gpt-4,"
"gpt-4-turbo-preview," "gpt-4-turbo-preview,"
"gpt-4-turbo-2024-04-09," "gpt-4-turbo-2024-04-09,"
@ -71,122 +71,124 @@ class HostedOpenAiConfig(BaseSettings):
class HostedAzureOpenAiConfig(BaseSettings): class HostedAzureOpenAiConfig(BaseSettings):
""" """
Configuration for hosted Azure OpenAI service Hosted OpenAI service config
""" """
HOSTED_AZURE_OPENAI_ENABLED: bool = Field( HOSTED_AZURE_OPENAI_ENABLED: bool = Field(
description="Enable hosted Azure OpenAI service", description="",
default=False, default=False,
) )
HOSTED_AZURE_OPENAI_API_KEY: Optional[str] = Field( HOSTED_AZURE_OPENAI_API_KEY: Optional[str] = Field(
description="API key for hosted Azure OpenAI service", description="",
default=None, default=None,
) )
HOSTED_AZURE_OPENAI_API_BASE: Optional[str] = Field( HOSTED_AZURE_OPENAI_API_BASE: Optional[str] = Field(
description="Base URL for hosted Azure OpenAI API", description="",
default=None, default=None,
) )
HOSTED_AZURE_OPENAI_QUOTA_LIMIT: NonNegativeInt = Field( HOSTED_AZURE_OPENAI_QUOTA_LIMIT: NonNegativeInt = Field(
description="Quota limit for hosted Azure OpenAI service usage", description="",
default=200, default=200,
) )
class HostedAnthropicConfig(BaseSettings): class HostedAnthropicConfig(BaseSettings):
""" """
Configuration for hosted Anthropic service Hosted Azure OpenAI service config
""" """
HOSTED_ANTHROPIC_API_BASE: Optional[str] = Field( HOSTED_ANTHROPIC_API_BASE: Optional[str] = Field(
description="Base URL for hosted Anthropic API", description="",
default=None, default=None,
) )
HOSTED_ANTHROPIC_API_KEY: Optional[str] = Field( HOSTED_ANTHROPIC_API_KEY: Optional[str] = Field(
description="API key for hosted Anthropic service", description="",
default=None, default=None,
) )
HOSTED_ANTHROPIC_TRIAL_ENABLED: bool = Field( HOSTED_ANTHROPIC_TRIAL_ENABLED: bool = Field(
description="Enable trial access to hosted Anthropic service", description="",
default=False, default=False,
) )
HOSTED_ANTHROPIC_QUOTA_LIMIT: NonNegativeInt = Field( HOSTED_ANTHROPIC_QUOTA_LIMIT: NonNegativeInt = Field(
description="Quota limit for hosted Anthropic service usage", description="",
default=600000, default=600000,
) )
HOSTED_ANTHROPIC_PAID_ENABLED: bool = Field( HOSTED_ANTHROPIC_PAID_ENABLED: bool = Field(
description="Enable paid access to hosted Anthropic service", description="",
default=False, default=False,
) )
class HostedMinmaxConfig(BaseSettings): class HostedMinmaxConfig(BaseSettings):
""" """
Configuration for hosted Minmax service Hosted Minmax service config
""" """
HOSTED_MINIMAX_ENABLED: bool = Field( HOSTED_MINIMAX_ENABLED: bool = Field(
description="Enable hosted Minmax service", description="",
default=False, default=False,
) )
class HostedSparkConfig(BaseSettings): class HostedSparkConfig(BaseSettings):
""" """
Configuration for hosted Spark service Hosted Spark service config
""" """
HOSTED_SPARK_ENABLED: bool = Field( HOSTED_SPARK_ENABLED: bool = Field(
description="Enable hosted Spark service", description="",
default=False, default=False,
) )
class HostedZhipuAIConfig(BaseSettings): class HostedZhipuAIConfig(BaseSettings):
""" """
Configuration for hosted ZhipuAI service Hosted Minmax service config
""" """
HOSTED_ZHIPUAI_ENABLED: bool = Field( HOSTED_ZHIPUAI_ENABLED: bool = Field(
description="Enable hosted ZhipuAI service", description="",
default=False, default=False,
) )
class HostedModerationConfig(BaseSettings): class HostedModerationConfig(BaseSettings):
""" """
Configuration for hosted Moderation service Hosted Moderation service config
""" """
HOSTED_MODERATION_ENABLED: bool = Field( HOSTED_MODERATION_ENABLED: bool = Field(
description="Enable hosted Moderation service", description="",
default=False, default=False,
) )
HOSTED_MODERATION_PROVIDERS: str = Field( HOSTED_MODERATION_PROVIDERS: str = Field(
description="Comma-separated list of moderation providers", description="",
default="", default="",
) )
class HostedFetchAppTemplateConfig(BaseSettings): class HostedFetchAppTemplateConfig(BaseSettings):
""" """
Configuration for fetching app templates Hosted Moderation service config
""" """
HOSTED_FETCH_APP_TEMPLATES_MODE: str = Field( HOSTED_FETCH_APP_TEMPLATES_MODE: str = Field(
description="Mode for fetching app templates: remote, db, or builtin" " default to remote,", description="the mode for fetching app templates,"
" default to remote,"
" available values: remote, db, builtin",
default="remote", default="remote",
) )
HOSTED_FETCH_APP_TEMPLATES_REMOTE_DOMAIN: str = Field( HOSTED_FETCH_APP_TEMPLATES_REMOTE_DOMAIN: str = Field(
description="Domain for fetching remote app templates", description="the domain for fetching remote app templates",
default="https://tmpl.dify.ai", default="https://tmpl.dify.ai",
) )

View File

@ -8,22 +8,16 @@ from configs.middleware.cache.redis_config import RedisConfig
from configs.middleware.storage.aliyun_oss_storage_config import AliyunOSSStorageConfig from configs.middleware.storage.aliyun_oss_storage_config import AliyunOSSStorageConfig
from configs.middleware.storage.amazon_s3_storage_config import S3StorageConfig from configs.middleware.storage.amazon_s3_storage_config import S3StorageConfig
from configs.middleware.storage.azure_blob_storage_config import AzureBlobStorageConfig from configs.middleware.storage.azure_blob_storage_config import AzureBlobStorageConfig
from configs.middleware.storage.baidu_obs_storage_config import BaiduOBSStorageConfig
from configs.middleware.storage.google_cloud_storage_config import GoogleCloudStorageConfig from configs.middleware.storage.google_cloud_storage_config import GoogleCloudStorageConfig
from configs.middleware.storage.huawei_obs_storage_config import HuaweiCloudOBSStorageConfig from configs.middleware.storage.huawei_obs_storage_config import HuaweiCloudOBSStorageConfig
from configs.middleware.storage.oci_storage_config import OCIStorageConfig from configs.middleware.storage.oci_storage_config import OCIStorageConfig
from configs.middleware.storage.supabase_storage_config import SupabaseStorageConfig
from configs.middleware.storage.tencent_cos_storage_config import TencentCloudCOSStorageConfig from configs.middleware.storage.tencent_cos_storage_config import TencentCloudCOSStorageConfig
from configs.middleware.storage.volcengine_tos_storage_config import VolcengineTOSStorageConfig from configs.middleware.storage.volcengine_tos_storage_config import VolcengineTOSStorageConfig
from configs.middleware.vdb.analyticdb_config import AnalyticdbConfig from configs.middleware.vdb.analyticdb_config import AnalyticdbConfig
from configs.middleware.vdb.baidu_vector_config import BaiduVectorDBConfig
from configs.middleware.vdb.chroma_config import ChromaConfig from configs.middleware.vdb.chroma_config import ChromaConfig
from configs.middleware.vdb.couchbase_config import CouchbaseConfig
from configs.middleware.vdb.elasticsearch_config import ElasticsearchConfig from configs.middleware.vdb.elasticsearch_config import ElasticsearchConfig
from configs.middleware.vdb.lindorm_config import LindormConfig
from configs.middleware.vdb.milvus_config import MilvusConfig from configs.middleware.vdb.milvus_config import MilvusConfig
from configs.middleware.vdb.myscale_config import MyScaleConfig from configs.middleware.vdb.myscale_config import MyScaleConfig
from configs.middleware.vdb.oceanbase_config import OceanBaseVectorConfig
from configs.middleware.vdb.opensearch_config import OpenSearchConfig from configs.middleware.vdb.opensearch_config import OpenSearchConfig
from configs.middleware.vdb.oracle_config import OracleConfig from configs.middleware.vdb.oracle_config import OracleConfig
from configs.middleware.vdb.pgvector_config import PGVectorConfig from configs.middleware.vdb.pgvector_config import PGVectorConfig
@ -31,86 +25,76 @@ from configs.middleware.vdb.pgvectors_config import PGVectoRSConfig
from configs.middleware.vdb.qdrant_config import QdrantConfig from configs.middleware.vdb.qdrant_config import QdrantConfig
from configs.middleware.vdb.relyt_config import RelytConfig from configs.middleware.vdb.relyt_config import RelytConfig
from configs.middleware.vdb.tencent_vector_config import TencentVectorDBConfig from configs.middleware.vdb.tencent_vector_config import TencentVectorDBConfig
from configs.middleware.vdb.tidb_on_qdrant_config import TidbOnQdrantConfig
from configs.middleware.vdb.tidb_vector_config import TiDBVectorConfig from configs.middleware.vdb.tidb_vector_config import TiDBVectorConfig
from configs.middleware.vdb.upstash_config import UpstashConfig
from configs.middleware.vdb.vikingdb_config import VikingDBConfig
from configs.middleware.vdb.weaviate_config import WeaviateConfig from configs.middleware.vdb.weaviate_config import WeaviateConfig
class StorageConfig(BaseSettings): class StorageConfig(BaseSettings):
STORAGE_TYPE: str = Field( STORAGE_TYPE: str = Field(
description="Type of storage to use." description="storage type,"
" Options: 'local', 's3', 'aliyun-oss', 'azure-blob', 'baidu-obs', 'google-storage', 'huawei-obs', " " default to `local`,"
"'oci-storage', 'tencent-cos', 'volcengine-tos', 'supabase'. Default is 'local'.", " available values are `local`, `s3`, `azure-blob`, `aliyun-oss`, `google-storage`.",
default="local", default="local",
) )
STORAGE_LOCAL_PATH: str = Field( STORAGE_LOCAL_PATH: str = Field(
description="Path for local storage when STORAGE_TYPE is set to 'local'.", description="local storage path",
default="storage", default="storage",
) )
class VectorStoreConfig(BaseSettings): class VectorStoreConfig(BaseSettings):
VECTOR_STORE: Optional[str] = Field( VECTOR_STORE: Optional[str] = Field(
description="Type of vector store to use for efficient similarity search." description="vector store type",
" Set to None if not using a vector store.",
default=None, default=None,
) )
VECTOR_STORE_WHITELIST_ENABLE: Optional[bool] = Field(
description="Enable whitelist for vector store.",
default=False,
)
class KeywordStoreConfig(BaseSettings): class KeywordStoreConfig(BaseSettings):
KEYWORD_STORE: str = Field( KEYWORD_STORE: str = Field(
description="Method for keyword extraction and storage." description="keyword store type",
" Default is 'jieba', a Chinese text segmentation library.",
default="jieba", default="jieba",
) )
class DatabaseConfig: class DatabaseConfig:
DB_HOST: str = Field( DB_HOST: str = Field(
description="Hostname or IP address of the database server.", description="db host",
default="localhost", default="localhost",
) )
DB_PORT: PositiveInt = Field( DB_PORT: PositiveInt = Field(
description="Port number for database connection.", description="db port",
default=5432, default=5432,
) )
DB_USERNAME: str = Field( DB_USERNAME: str = Field(
description="Username for database authentication.", description="db username",
default="postgres", default="postgres",
) )
DB_PASSWORD: str = Field( DB_PASSWORD: str = Field(
description="Password for database authentication.", description="db password",
default="", default="",
) )
DB_DATABASE: str = Field( DB_DATABASE: str = Field(
description="Name of the database to connect to.", description="db database",
default="dify", default="dify",
) )
DB_CHARSET: str = Field( DB_CHARSET: str = Field(
description="Character set for database connection.", description="db charset",
default="", default="",
) )
DB_EXTRAS: str = Field( DB_EXTRAS: str = Field(
description="Additional database connection parameters. Example: 'keepalives_idle=60&keepalives=1'", description="db extras options. Example: keepalives_idle=60&keepalives=1",
default="", default="",
) )
SQLALCHEMY_DATABASE_URI_SCHEME: str = Field( SQLALCHEMY_DATABASE_URI_SCHEME: str = Field(
description="Database URI scheme for SQLAlchemy connection.", description="db uri scheme",
default="postgresql", default="postgresql",
) )
@ -128,27 +112,27 @@ class DatabaseConfig:
) )
SQLALCHEMY_POOL_SIZE: NonNegativeInt = Field( SQLALCHEMY_POOL_SIZE: NonNegativeInt = Field(
description="Maximum number of database connections in the pool.", description="pool size of SqlAlchemy",
default=30, default=30,
) )
SQLALCHEMY_MAX_OVERFLOW: NonNegativeInt = Field( SQLALCHEMY_MAX_OVERFLOW: NonNegativeInt = Field(
description="Maximum number of connections that can be created beyond the pool_size.", description="max overflows for SqlAlchemy",
default=10, default=10,
) )
SQLALCHEMY_POOL_RECYCLE: NonNegativeInt = Field( SQLALCHEMY_POOL_RECYCLE: NonNegativeInt = Field(
description="Number of seconds after which a connection is automatically recycled.", description="SqlAlchemy pool recycle",
default=3600, default=3600,
) )
SQLALCHEMY_POOL_PRE_PING: bool = Field( SQLALCHEMY_POOL_PRE_PING: bool = Field(
description="If True, enables connection pool pre-ping feature to check connections.", description="whether to enable pool pre-ping in SqlAlchemy",
default=False, default=False,
) )
SQLALCHEMY_ECHO: bool | str = Field( SQLALCHEMY_ECHO: bool | str = Field(
description="If True, SQLAlchemy will log all SQL statements.", description="whether to enable SqlAlchemy echo",
default=False, default=False,
) )
@ -166,27 +150,27 @@ class DatabaseConfig:
class CeleryConfig(DatabaseConfig): class CeleryConfig(DatabaseConfig):
CELERY_BACKEND: str = Field( CELERY_BACKEND: str = Field(
description="Backend for Celery task results. Options: 'database', 'redis'.", description="Celery backend, available values are `database`, `redis`",
default="database", default="database",
) )
CELERY_BROKER_URL: Optional[str] = Field( CELERY_BROKER_URL: Optional[str] = Field(
description="URL of the message broker for Celery tasks.", description="CELERY_BROKER_URL",
default=None, default=None,
) )
CELERY_USE_SENTINEL: Optional[bool] = Field( CELERY_USE_SENTINEL: Optional[bool] = Field(
description="Whether to use Redis Sentinel for high availability.", description="Whether to use Redis Sentinel mode",
default=False, default=False,
) )
CELERY_SENTINEL_MASTER_NAME: Optional[str] = Field( CELERY_SENTINEL_MASTER_NAME: Optional[str] = Field(
description="Name of the Redis Sentinel master.", description="Redis Sentinel master name",
default=None, default=None,
) )
CELERY_SENTINEL_SOCKET_TIMEOUT: Optional[PositiveFloat] = Field( CELERY_SENTINEL_SOCKET_TIMEOUT: Optional[PositiveFloat] = Field(
description="Timeout for Redis Sentinel socket operations in seconds.", description="Redis Sentinel socket timeout",
default=0.1, default=0.1,
) )
@ -205,22 +189,6 @@ class CeleryConfig(DatabaseConfig):
return self.CELERY_BROKER_URL.startswith("rediss://") if self.CELERY_BROKER_URL else False return self.CELERY_BROKER_URL.startswith("rediss://") if self.CELERY_BROKER_URL else False
class InternalTestConfig(BaseSettings):
"""
Configuration settings for Internal Test
"""
AWS_SECRET_ACCESS_KEY: Optional[str] = Field(
description="Internal test AWS secret access key",
default=None,
)
AWS_ACCESS_KEY_ID: Optional[str] = Field(
description="Internal test AWS access key ID",
default=None,
)
class MiddlewareConfig( class MiddlewareConfig(
# place the configs in alphabet order # place the configs in alphabet order
CeleryConfig, CeleryConfig,
@ -231,14 +199,12 @@ class MiddlewareConfig(
StorageConfig, StorageConfig,
AliyunOSSStorageConfig, AliyunOSSStorageConfig,
AzureBlobStorageConfig, AzureBlobStorageConfig,
BaiduOBSStorageConfig,
GoogleCloudStorageConfig, GoogleCloudStorageConfig,
HuaweiCloudOBSStorageConfig,
OCIStorageConfig,
S3StorageConfig,
SupabaseStorageConfig,
TencentCloudCOSStorageConfig, TencentCloudCOSStorageConfig,
HuaweiCloudOBSStorageConfig,
VolcengineTOSStorageConfig, VolcengineTOSStorageConfig,
S3StorageConfig,
OCIStorageConfig,
# configs of vdb and vdb providers # configs of vdb and vdb providers
VectorStoreConfig, VectorStoreConfig,
AnalyticdbConfig, AnalyticdbConfig,
@ -255,13 +221,5 @@ class MiddlewareConfig(
TiDBVectorConfig, TiDBVectorConfig,
WeaviateConfig, WeaviateConfig,
ElasticsearchConfig, ElasticsearchConfig,
CouchbaseConfig,
InternalTestConfig,
VikingDBConfig,
UpstashConfig,
TidbOnQdrantConfig,
LindormConfig,
OceanBaseVectorConfig,
BaiduVectorDBConfig,
): ):
pass pass

View File

@ -6,65 +6,65 @@ from pydantic_settings import BaseSettings
class RedisConfig(BaseSettings): class RedisConfig(BaseSettings):
""" """
Configuration settings for Redis connection Redis configs
""" """
REDIS_HOST: str = Field( REDIS_HOST: str = Field(
description="Hostname or IP address of the Redis server", description="Redis host",
default="localhost", default="localhost",
) )
REDIS_PORT: PositiveInt = Field( REDIS_PORT: PositiveInt = Field(
description="Port number on which the Redis server is listening", description="Redis port",
default=6379, default=6379,
) )
REDIS_USERNAME: Optional[str] = Field( REDIS_USERNAME: Optional[str] = Field(
description="Username for Redis authentication (if required)", description="Redis username",
default=None, default=None,
) )
REDIS_PASSWORD: Optional[str] = Field( REDIS_PASSWORD: Optional[str] = Field(
description="Password for Redis authentication (if required)", description="Redis password",
default=None, default=None,
) )
REDIS_DB: NonNegativeInt = Field( REDIS_DB: NonNegativeInt = Field(
description="Redis database number to use (0-15)", description="Redis database id, default to 0",
default=0, default=0,
) )
REDIS_USE_SSL: bool = Field( REDIS_USE_SSL: bool = Field(
description="Enable SSL/TLS for the Redis connection", description="whether to use SSL for Redis connection",
default=False, default=False,
) )
REDIS_USE_SENTINEL: Optional[bool] = Field( REDIS_USE_SENTINEL: Optional[bool] = Field(
description="Enable Redis Sentinel mode for high availability", description="Whether to use Redis Sentinel mode",
default=False, default=False,
) )
REDIS_SENTINELS: Optional[str] = Field( REDIS_SENTINELS: Optional[str] = Field(
description="Comma-separated list of Redis Sentinel nodes (host:port)", description="Redis Sentinel nodes",
default=None, default=None,
) )
REDIS_SENTINEL_SERVICE_NAME: Optional[str] = Field( REDIS_SENTINEL_SERVICE_NAME: Optional[str] = Field(
description="Name of the Redis Sentinel service to monitor", description="Redis Sentinel service name",
default=None, default=None,
) )
REDIS_SENTINEL_USERNAME: Optional[str] = Field( REDIS_SENTINEL_USERNAME: Optional[str] = Field(
description="Username for Redis Sentinel authentication (if required)", description="Redis Sentinel username",
default=None, default=None,
) )
REDIS_SENTINEL_PASSWORD: Optional[str] = Field( REDIS_SENTINEL_PASSWORD: Optional[str] = Field(
description="Password for Redis Sentinel authentication (if required)", description="Redis Sentinel password",
default=None, default=None,
) )
REDIS_SENTINEL_SOCKET_TIMEOUT: Optional[PositiveFloat] = Field( REDIS_SENTINEL_SOCKET_TIMEOUT: Optional[PositiveFloat] = Field(
description="Socket timeout in seconds for Redis Sentinel connections", description="Redis Sentinel socket timeout",
default=0.1, default=0.1,
) )

View File

@ -6,40 +6,40 @@ from pydantic_settings import BaseSettings
class AliyunOSSStorageConfig(BaseSettings): class AliyunOSSStorageConfig(BaseSettings):
""" """
Configuration settings for Aliyun Object Storage Service (OSS) Aliyun storage configs
""" """
ALIYUN_OSS_BUCKET_NAME: Optional[str] = Field( ALIYUN_OSS_BUCKET_NAME: Optional[str] = Field(
description="Name of the Aliyun OSS bucket to store and retrieve objects", description="Aliyun OSS bucket name",
default=None, default=None,
) )
ALIYUN_OSS_ACCESS_KEY: Optional[str] = Field( ALIYUN_OSS_ACCESS_KEY: Optional[str] = Field(
description="Access key ID for authenticating with Aliyun OSS", description="Aliyun OSS access key",
default=None, default=None,
) )
ALIYUN_OSS_SECRET_KEY: Optional[str] = Field( ALIYUN_OSS_SECRET_KEY: Optional[str] = Field(
description="Secret access key for authenticating with Aliyun OSS", description="Aliyun OSS secret key",
default=None, default=None,
) )
ALIYUN_OSS_ENDPOINT: Optional[str] = Field( ALIYUN_OSS_ENDPOINT: Optional[str] = Field(
description="URL of the Aliyun OSS endpoint for your chosen region", description="Aliyun OSS endpoint URL",
default=None, default=None,
) )
ALIYUN_OSS_REGION: Optional[str] = Field( ALIYUN_OSS_REGION: Optional[str] = Field(
description="Aliyun OSS region where your bucket is located (e.g., 'oss-cn-hangzhou')", description="Aliyun OSS region",
default=None, default=None,
) )
ALIYUN_OSS_AUTH_VERSION: Optional[str] = Field( ALIYUN_OSS_AUTH_VERSION: Optional[str] = Field(
description="Version of the authentication protocol to use with Aliyun OSS (e.g., 'v4')", description="Aliyun OSS authentication version",
default=None, default=None,
) )
ALIYUN_OSS_PATH: Optional[str] = Field( ALIYUN_OSS_PATH: Optional[str] = Field(
description="Base path within the bucket to store objects (e.g., 'my-app-data/')", description="Aliyun OSS path",
default=None, default=None,
) )

View File

@ -6,40 +6,40 @@ from pydantic_settings import BaseSettings
class S3StorageConfig(BaseSettings): class S3StorageConfig(BaseSettings):
""" """
Configuration settings for S3-compatible object storage S3 storage configs
""" """
S3_ENDPOINT: Optional[str] = Field( S3_ENDPOINT: Optional[str] = Field(
description="URL of the S3-compatible storage endpoint (e.g., 'https://s3.amazonaws.com')", description="S3 storage endpoint",
default=None, default=None,
) )
S3_REGION: Optional[str] = Field( S3_REGION: Optional[str] = Field(
description="Region where the S3 bucket is located (e.g., 'us-east-1')", description="S3 storage region",
default=None, default=None,
) )
S3_BUCKET_NAME: Optional[str] = Field( S3_BUCKET_NAME: Optional[str] = Field(
description="Name of the S3 bucket to store and retrieve objects", description="S3 storage bucket name",
default=None, default=None,
) )
S3_ACCESS_KEY: Optional[str] = Field( S3_ACCESS_KEY: Optional[str] = Field(
description="Access key ID for authenticating with the S3 service", description="S3 storage access key",
default=None, default=None,
) )
S3_SECRET_KEY: Optional[str] = Field( S3_SECRET_KEY: Optional[str] = Field(
description="Secret access key for authenticating with the S3 service", description="S3 storage secret key",
default=None, default=None,
) )
S3_ADDRESS_STYLE: str = Field( S3_ADDRESS_STYLE: str = Field(
description="S3 addressing style: 'auto', 'path', or 'virtual'", description="S3 storage address style",
default="auto", default="auto",
) )
S3_USE_AWS_MANAGED_IAM: bool = Field( S3_USE_AWS_MANAGED_IAM: bool = Field(
description="Use AWS managed IAM roles for authentication instead of access/secret keys", description="whether to use aws managed IAM for S3",
default=False, default=False,
) )

View File

@ -6,25 +6,25 @@ from pydantic_settings import BaseSettings
class AzureBlobStorageConfig(BaseSettings): class AzureBlobStorageConfig(BaseSettings):
""" """
Configuration settings for Azure Blob Storage Azure Blob storage configs
""" """
AZURE_BLOB_ACCOUNT_NAME: Optional[str] = Field( AZURE_BLOB_ACCOUNT_NAME: Optional[str] = Field(
description="Name of the Azure Storage account (e.g., 'mystorageaccount')", description="Azure Blob account name",
default=None, default=None,
) )
AZURE_BLOB_ACCOUNT_KEY: Optional[str] = Field( AZURE_BLOB_ACCOUNT_KEY: Optional[str] = Field(
description="Access key for authenticating with the Azure Storage account", description="Azure Blob account key",
default=None, default=None,
) )
AZURE_BLOB_CONTAINER_NAME: Optional[str] = Field( AZURE_BLOB_CONTAINER_NAME: Optional[str] = Field(
description="Name of the Azure Blob container to store and retrieve objects", description="Azure Blob container name",
default=None, default=None,
) )
AZURE_BLOB_ACCOUNT_URL: Optional[str] = Field( AZURE_BLOB_ACCOUNT_URL: Optional[str] = Field(
description="URL of the Azure Blob storage endpoint (e.g., 'https://mystorageaccount.blob.core.windows.net')", description="Azure Blob account URL",
default=None, default=None,
) )

View File

@ -1,29 +0,0 @@
from typing import Optional
from pydantic import BaseModel, Field
class BaiduOBSStorageConfig(BaseModel):
"""
Configuration settings for Baidu Object Storage Service (OBS)
"""
BAIDU_OBS_BUCKET_NAME: Optional[str] = Field(
description="Name of the Baidu OBS bucket to store and retrieve objects (e.g., 'my-obs-bucket')",
default=None,
)
BAIDU_OBS_ACCESS_KEY: Optional[str] = Field(
description="Access Key ID for authenticating with Baidu OBS",
default=None,
)
BAIDU_OBS_SECRET_KEY: Optional[str] = Field(
description="Secret Access Key for authenticating with Baidu OBS",
default=None,
)
BAIDU_OBS_ENDPOINT: Optional[str] = Field(
description="URL of the Baidu OSS endpoint for your chosen region (e.g., 'https://.bj.bcebos.com')",
default=None,
)

View File

@ -6,15 +6,15 @@ from pydantic_settings import BaseSettings
class GoogleCloudStorageConfig(BaseSettings): class GoogleCloudStorageConfig(BaseSettings):
""" """
Configuration settings for Google Cloud Storage Google Cloud storage configs
""" """
GOOGLE_STORAGE_BUCKET_NAME: Optional[str] = Field( GOOGLE_STORAGE_BUCKET_NAME: Optional[str] = Field(
description="Name of the Google Cloud Storage bucket to store and retrieve objects (e.g., 'my-gcs-bucket')", description="Google Cloud storage bucket name",
default=None, default=None,
) )
GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64: Optional[str] = Field( GOOGLE_STORAGE_SERVICE_ACCOUNT_JSON_BASE64: Optional[str] = Field(
description="Base64-encoded JSON key file for Google Cloud service account authentication", description="Google Cloud storage service account json base64",
default=None, default=None,
) )

View File

@ -5,25 +5,25 @@ from pydantic import BaseModel, Field
class HuaweiCloudOBSStorageConfig(BaseModel): class HuaweiCloudOBSStorageConfig(BaseModel):
""" """
Configuration settings for Huawei Cloud Object Storage Service (OBS) Huawei Cloud OBS storage configs
""" """
HUAWEI_OBS_BUCKET_NAME: Optional[str] = Field( HUAWEI_OBS_BUCKET_NAME: Optional[str] = Field(
description="Name of the Huawei Cloud OBS bucket to store and retrieve objects (e.g., 'my-obs-bucket')", description="Huawei Cloud OBS bucket name",
default=None, default=None,
) )
HUAWEI_OBS_ACCESS_KEY: Optional[str] = Field( HUAWEI_OBS_ACCESS_KEY: Optional[str] = Field(
description="Access Key ID for authenticating with Huawei Cloud OBS", description="Huawei Cloud OBS Access key",
default=None, default=None,
) )
HUAWEI_OBS_SECRET_KEY: Optional[str] = Field( HUAWEI_OBS_SECRET_KEY: Optional[str] = Field(
description="Secret Access Key for authenticating with Huawei Cloud OBS", description="Huawei Cloud OBS Secret key",
default=None, default=None,
) )
HUAWEI_OBS_SERVER: Optional[str] = Field( HUAWEI_OBS_SERVER: Optional[str] = Field(
description="Endpoint URL for Huawei Cloud OBS (e.g., 'https://obs.cn-north-4.myhuaweicloud.com')", description="Huawei Cloud OBS server URL",
default=None, default=None,
) )

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class OCIStorageConfig(BaseSettings): class OCIStorageConfig(BaseSettings):
""" """
Configuration settings for Oracle Cloud Infrastructure (OCI) Object Storage OCI storage configs
""" """
OCI_ENDPOINT: Optional[str] = Field( OCI_ENDPOINT: Optional[str] = Field(
description="URL of the OCI Object Storage endpoint (e.g., 'https://objectstorage.us-phoenix-1.oraclecloud.com')", description="OCI storage endpoint",
default=None, default=None,
) )
OCI_REGION: Optional[str] = Field( OCI_REGION: Optional[str] = Field(
description="OCI region where the bucket is located (e.g., 'us-phoenix-1')", description="OCI storage region",
default=None, default=None,
) )
OCI_BUCKET_NAME: Optional[str] = Field( OCI_BUCKET_NAME: Optional[str] = Field(
description="Name of the OCI Object Storage bucket to store and retrieve objects (e.g., 'my-oci-bucket')", description="OCI storage bucket name",
default=None, default=None,
) )
OCI_ACCESS_KEY: Optional[str] = Field( OCI_ACCESS_KEY: Optional[str] = Field(
description="Access key (also known as API key) for authenticating with OCI Object Storage", description="OCI storage access key",
default=None, default=None,
) )
OCI_SECRET_KEY: Optional[str] = Field( OCI_SECRET_KEY: Optional[str] = Field(
description="Secret key associated with the access key for authenticating with OCI Object Storage", description="OCI storage secret key",
default=None, default=None,
) )

View File

@ -1,24 +0,0 @@
from typing import Optional
from pydantic import BaseModel, Field
class SupabaseStorageConfig(BaseModel):
"""
Configuration settings for Supabase Object Storage Service
"""
SUPABASE_BUCKET_NAME: Optional[str] = Field(
description="Name of the Supabase bucket to store and retrieve objects (e.g., 'dify-bucket')",
default=None,
)
SUPABASE_API_KEY: Optional[str] = Field(
description="API KEY for authenticating with Supabase",
default=None,
)
SUPABASE_URL: Optional[str] = Field(
description="URL of the Supabase",
default=None,
)

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class TencentCloudCOSStorageConfig(BaseSettings): class TencentCloudCOSStorageConfig(BaseSettings):
""" """
Configuration settings for Tencent Cloud Object Storage (COS) Tencent Cloud COS storage configs
""" """
TENCENT_COS_BUCKET_NAME: Optional[str] = Field( TENCENT_COS_BUCKET_NAME: Optional[str] = Field(
description="Name of the Tencent Cloud COS bucket to store and retrieve objects", description="Tencent Cloud COS bucket name",
default=None, default=None,
) )
TENCENT_COS_REGION: Optional[str] = Field( TENCENT_COS_REGION: Optional[str] = Field(
description="Tencent Cloud region where the COS bucket is located (e.g., 'ap-guangzhou')", description="Tencent Cloud COS region",
default=None, default=None,
) )
TENCENT_COS_SECRET_ID: Optional[str] = Field( TENCENT_COS_SECRET_ID: Optional[str] = Field(
description="SecretId for authenticating with Tencent Cloud COS (part of API credentials)", description="Tencent Cloud COS secret id",
default=None, default=None,
) )
TENCENT_COS_SECRET_KEY: Optional[str] = Field( TENCENT_COS_SECRET_KEY: Optional[str] = Field(
description="SecretKey for authenticating with Tencent Cloud COS (part of API credentials)", description="Tencent Cloud COS secret key",
default=None, default=None,
) )
TENCENT_COS_SCHEME: Optional[str] = Field( TENCENT_COS_SCHEME: Optional[str] = Field(
description="Protocol scheme for COS requests: 'https' (recommended) or 'http'", description="Tencent Cloud COS scheme",
default=None, default=None,
) )

View File

@ -5,30 +5,30 @@ from pydantic import BaseModel, Field
class VolcengineTOSStorageConfig(BaseModel): class VolcengineTOSStorageConfig(BaseModel):
""" """
Configuration settings for Volcengine Tinder Object Storage (TOS) Volcengine tos storage configs
""" """
VOLCENGINE_TOS_BUCKET_NAME: Optional[str] = Field( VOLCENGINE_TOS_BUCKET_NAME: Optional[str] = Field(
description="Name of the Volcengine TOS bucket to store and retrieve objects (e.g., 'my-tos-bucket')", description="Volcengine TOS Bucket Name",
default=None, default=None,
) )
VOLCENGINE_TOS_ACCESS_KEY: Optional[str] = Field( VOLCENGINE_TOS_ACCESS_KEY: Optional[str] = Field(
description="Access Key ID for authenticating with Volcengine TOS", description="Volcengine TOS Access Key",
default=None, default=None,
) )
VOLCENGINE_TOS_SECRET_KEY: Optional[str] = Field( VOLCENGINE_TOS_SECRET_KEY: Optional[str] = Field(
description="Secret Access Key for authenticating with Volcengine TOS", description="Volcengine TOS Secret Key",
default=None, default=None,
) )
VOLCENGINE_TOS_ENDPOINT: Optional[str] = Field( VOLCENGINE_TOS_ENDPOINT: Optional[str] = Field(
description="URL of the Volcengine TOS endpoint (e.g., 'https://tos-cn-beijing.volces.com')", description="Volcengine TOS Endpoint URL",
default=None, default=None,
) )
VOLCENGINE_TOS_REGION: Optional[str] = Field( VOLCENGINE_TOS_REGION: Optional[str] = Field(
description="Volcengine region where the TOS bucket is located (e.g., 'cn-beijing')", description="Volcengine TOS Region",
default=None, default=None,
) )

View File

@ -5,38 +5,33 @@ from pydantic import BaseModel, Field
class AnalyticdbConfig(BaseModel): class AnalyticdbConfig(BaseModel):
""" """
Configuration for connecting to Alibaba Cloud AnalyticDB for PostgreSQL. Configuration for connecting to AnalyticDB.
Refer to the following documentation for details on obtaining credentials: Refer to the following documentation for details on obtaining credentials:
https://www.alibabacloud.com/help/en/analyticdb-for-postgresql/getting-started/create-an-instance-instances-with-vector-engine-optimization-enabled https://www.alibabacloud.com/help/en/analyticdb-for-postgresql/getting-started/create-an-instance-instances-with-vector-engine-optimization-enabled
""" """
ANALYTICDB_KEY_ID: Optional[str] = Field( ANALYTICDB_KEY_ID: Optional[str] = Field(
default=None, description="The Access Key ID provided by Alibaba Cloud for API authentication." default=None, description="The Access Key ID provided by Alibaba Cloud for authentication."
) )
ANALYTICDB_KEY_SECRET: Optional[str] = Field( ANALYTICDB_KEY_SECRET: Optional[str] = Field(
default=None, description="The Secret Access Key corresponding to the Access Key ID for secure API access." default=None, description="The Secret Access Key corresponding to the Access Key ID for secure access."
) )
ANALYTICDB_REGION_ID: Optional[str] = Field( ANALYTICDB_REGION_ID: Optional[str] = Field(
default=None, default=None, description="The region where the AnalyticDB instance is deployed (e.g., 'cn-hangzhou')."
description="The region where the AnalyticDB instance is deployed (e.g., 'cn-hangzhou', 'ap-southeast-1').",
) )
ANALYTICDB_INSTANCE_ID: Optional[str] = Field( ANALYTICDB_INSTANCE_ID: Optional[str] = Field(
default=None, default=None,
description="The unique identifier of the AnalyticDB instance you want to connect to.", description="The unique identifier of the AnalyticDB instance you want to connect to (e.g., 'gp-ab123456')..",
) )
ANALYTICDB_ACCOUNT: Optional[str] = Field( ANALYTICDB_ACCOUNT: Optional[str] = Field(
default=None, default=None, description="The account name used to log in to the AnalyticDB instance."
description="The account name used to log in to the AnalyticDB instance"
" (usually the initial account created with the instance).",
) )
ANALYTICDB_PASSWORD: Optional[str] = Field( ANALYTICDB_PASSWORD: Optional[str] = Field(
default=None, description="The password associated with the AnalyticDB account for database authentication." default=None, description="The password associated with the AnalyticDB account for authentication."
) )
ANALYTICDB_NAMESPACE: Optional[str] = Field( ANALYTICDB_NAMESPACE: Optional[str] = Field(
default=None, description="The namespace within AnalyticDB for schema isolation (if using namespace feature)." default=None, description="The namespace within AnalyticDB for schema isolation."
) )
ANALYTICDB_NAMESPACE_PASSWORD: Optional[str] = Field( ANALYTICDB_NAMESPACE_PASSWORD: Optional[str] = Field(
default=None, default=None, description="The password for accessing the specified namespace within the AnalyticDB instance."
description="The password for accessing the specified namespace within the AnalyticDB instance"
" (if namespace feature is enabled).",
) )

View File

@ -1,45 +0,0 @@
from typing import Optional
from pydantic import Field, NonNegativeInt, PositiveInt
from pydantic_settings import BaseSettings
class BaiduVectorDBConfig(BaseSettings):
"""
Configuration settings for Baidu Vector Database
"""
BAIDU_VECTOR_DB_ENDPOINT: Optional[str] = Field(
description="URL of the Baidu Vector Database service (e.g., 'http://vdb.bj.baidubce.com')",
default=None,
)
BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS: PositiveInt = Field(
description="Timeout in milliseconds for Baidu Vector Database operations (default is 30000 milliseconds)",
default=30000,
)
BAIDU_VECTOR_DB_ACCOUNT: Optional[str] = Field(
description="Account for authenticating with the Baidu Vector Database",
default=None,
)
BAIDU_VECTOR_DB_API_KEY: Optional[str] = Field(
description="API key for authenticating with the Baidu Vector Database service",
default=None,
)
BAIDU_VECTOR_DB_DATABASE: Optional[str] = Field(
description="Name of the specific Baidu Vector Database to connect to",
default=None,
)
BAIDU_VECTOR_DB_SHARD: PositiveInt = Field(
description="Number of shards for the Baidu Vector Database (default is 1)",
default=1,
)
BAIDU_VECTOR_DB_REPLICAS: NonNegativeInt = Field(
description="Number of replicas for the Baidu Vector Database (default is 3)",
default=3,
)

View File

@ -6,35 +6,35 @@ from pydantic_settings import BaseSettings
class ChromaConfig(BaseSettings): class ChromaConfig(BaseSettings):
""" """
Configuration settings for Chroma vector database Chroma configs
""" """
CHROMA_HOST: Optional[str] = Field( CHROMA_HOST: Optional[str] = Field(
description="Hostname or IP address of the Chroma server (e.g., 'localhost' or '192.168.1.100')", description="Chroma host",
default=None, default=None,
) )
CHROMA_PORT: PositiveInt = Field( CHROMA_PORT: PositiveInt = Field(
description="Port number on which the Chroma server is listening (default is 8000)", description="Chroma port",
default=8000, default=8000,
) )
CHROMA_TENANT: Optional[str] = Field( CHROMA_TENANT: Optional[str] = Field(
description="Tenant identifier for multi-tenancy support in Chroma", description="Chroma database",
default=None, default=None,
) )
CHROMA_DATABASE: Optional[str] = Field( CHROMA_DATABASE: Optional[str] = Field(
description="Name of the Chroma database to connect to", description="Chroma database",
default=None, default=None,
) )
CHROMA_AUTH_PROVIDER: Optional[str] = Field( CHROMA_AUTH_PROVIDER: Optional[str] = Field(
description="Authentication provider for Chroma (e.g., 'basic', 'token', or a custom provider)", description="Chroma authentication provider",
default=None, default=None,
) )
CHROMA_AUTH_CREDENTIALS: Optional[str] = Field( CHROMA_AUTH_CREDENTIALS: Optional[str] = Field(
description="Authentication credentials for Chroma (format depends on the auth provider)", description="Chroma authentication credentials",
default=None, default=None,
) )

View File

@ -1,34 +0,0 @@
from typing import Optional
from pydantic import BaseModel, Field
class CouchbaseConfig(BaseModel):
"""
Couchbase configs
"""
COUCHBASE_CONNECTION_STRING: Optional[str] = Field(
description="COUCHBASE connection string",
default=None,
)
COUCHBASE_USER: Optional[str] = Field(
description="COUCHBASE user",
default=None,
)
COUCHBASE_PASSWORD: Optional[str] = Field(
description="COUCHBASE password",
default=None,
)
COUCHBASE_BUCKET_NAME: Optional[str] = Field(
description="COUCHBASE bucket name",
default=None,
)
COUCHBASE_SCOPE_NAME: Optional[str] = Field(
description="COUCHBASE scope name",
default=None,
)

View File

@ -6,25 +6,25 @@ from pydantic_settings import BaseSettings
class ElasticsearchConfig(BaseSettings): class ElasticsearchConfig(BaseSettings):
""" """
Configuration settings for Elasticsearch Elasticsearch configs
""" """
ELASTICSEARCH_HOST: Optional[str] = Field( ELASTICSEARCH_HOST: Optional[str] = Field(
description="Hostname or IP address of the Elasticsearch server (e.g., 'localhost' or '192.168.1.100')", description="Elasticsearch host",
default="127.0.0.1", default="127.0.0.1",
) )
ELASTICSEARCH_PORT: PositiveInt = Field( ELASTICSEARCH_PORT: PositiveInt = Field(
description="Port number on which the Elasticsearch server is listening (default is 9200)", description="Elasticsearch port",
default=9200, default=9200,
) )
ELASTICSEARCH_USERNAME: Optional[str] = Field( ELASTICSEARCH_USERNAME: Optional[str] = Field(
description="Username for authenticating with Elasticsearch (default is 'elastic')", description="Elasticsearch username",
default="elastic", default="elastic",
) )
ELASTICSEARCH_PASSWORD: Optional[str] = Field( ELASTICSEARCH_PASSWORD: Optional[str] = Field(
description="Password for authenticating with Elasticsearch (default is 'elastic')", description="Elasticsearch password",
default="elastic", default="elastic",
) )

View File

@ -1,23 +0,0 @@
from typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings
class LindormConfig(BaseSettings):
"""
Lindorm configs
"""
LINDORM_URL: Optional[str] = Field(
description="Lindorm url",
default=None,
)
LINDORM_USERNAME: Optional[str] = Field(
description="Lindorm user",
default=None,
)
LINDORM_PASSWORD: Optional[str] = Field(
description="Lindorm password",
default=None,
)

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class MilvusConfig(BaseSettings): class MilvusConfig(BaseSettings):
""" """
Configuration settings for Milvus vector database Milvus configs
""" """
MILVUS_URI: Optional[str] = Field( MILVUS_URI: Optional[str] = Field(
description="URI for connecting to the Milvus server (e.g., 'http://localhost:19530' or 'https://milvus-instance.example.com:19530')", description="Milvus uri",
default="http://127.0.0.1:19530", default="http://127.0.0.1:19530",
) )
MILVUS_TOKEN: Optional[str] = Field( MILVUS_TOKEN: Optional[str] = Field(
description="Authentication token for Milvus, if token-based authentication is enabled", description="Milvus token",
default=None, default=None,
) )
MILVUS_USER: Optional[str] = Field( MILVUS_USER: Optional[str] = Field(
description="Username for authenticating with Milvus, if username/password authentication is enabled", description="Milvus user",
default=None, default=None,
) )
MILVUS_PASSWORD: Optional[str] = Field( MILVUS_PASSWORD: Optional[str] = Field(
description="Password for authenticating with Milvus, if username/password authentication is enabled", description="Milvus password",
default=None, default=None,
) )
MILVUS_DATABASE: str = Field( MILVUS_DATABASE: str = Field(
description="Name of the Milvus database to connect to (default is 'default')", description="Milvus database, default to `default`",
default="default", default="default",
) )

View File

@ -3,35 +3,35 @@ from pydantic import BaseModel, Field, PositiveInt
class MyScaleConfig(BaseModel): class MyScaleConfig(BaseModel):
""" """
Configuration settings for MyScale vector database MyScale configs
""" """
MYSCALE_HOST: str = Field( MYSCALE_HOST: str = Field(
description="Hostname or IP address of the MyScale server (e.g., 'localhost' or 'myscale.example.com')", description="MyScale host",
default="localhost", default="localhost",
) )
MYSCALE_PORT: PositiveInt = Field( MYSCALE_PORT: PositiveInt = Field(
description="Port number on which the MyScale server is listening (default is 8123)", description="MyScale port",
default=8123, default=8123,
) )
MYSCALE_USER: str = Field( MYSCALE_USER: str = Field(
description="Username for authenticating with MyScale (default is 'default')", description="MyScale user",
default="default", default="default",
) )
MYSCALE_PASSWORD: str = Field( MYSCALE_PASSWORD: str = Field(
description="Password for authenticating with MyScale (default is an empty string)", description="MyScale password",
default="", default="",
) )
MYSCALE_DATABASE: str = Field( MYSCALE_DATABASE: str = Field(
description="Name of the MyScale database to connect to (default is 'default')", description="MyScale database name",
default="default", default="default",
) )
MYSCALE_FTS_PARAMS: str = Field( MYSCALE_FTS_PARAMS: str = Field(
description="Additional parameters for MyScale Full Text Search index)", description="MyScale fts index parameters",
default="", default="",
) )

View File

@ -1,35 +0,0 @@
from typing import Optional
from pydantic import Field, PositiveInt
from pydantic_settings import BaseSettings
class OceanBaseVectorConfig(BaseSettings):
"""
Configuration settings for OceanBase Vector database
"""
OCEANBASE_VECTOR_HOST: Optional[str] = Field(
description="Hostname or IP address of the OceanBase Vector server (e.g. 'localhost')",
default=None,
)
OCEANBASE_VECTOR_PORT: Optional[PositiveInt] = Field(
description="Port number on which the OceanBase Vector server is listening (default is 2881)",
default=2881,
)
OCEANBASE_VECTOR_USER: Optional[str] = Field(
description="Username for authenticating with the OceanBase Vector database",
default=None,
)
OCEANBASE_VECTOR_PASSWORD: Optional[str] = Field(
description="Password for authenticating with the OceanBase Vector database",
default=None,
)
OCEANBASE_VECTOR_DATABASE: Optional[str] = Field(
description="Name of the OceanBase Vector database to connect to",
default=None,
)

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class OpenSearchConfig(BaseSettings): class OpenSearchConfig(BaseSettings):
""" """
Configuration settings for OpenSearch OpenSearch configs
""" """
OPENSEARCH_HOST: Optional[str] = Field( OPENSEARCH_HOST: Optional[str] = Field(
description="Hostname or IP address of the OpenSearch server (e.g., 'localhost' or 'opensearch.example.com')", description="OpenSearch host",
default=None, default=None,
) )
OPENSEARCH_PORT: PositiveInt = Field( OPENSEARCH_PORT: PositiveInt = Field(
description="Port number on which the OpenSearch server is listening (default is 9200)", description="OpenSearch port",
default=9200, default=9200,
) )
OPENSEARCH_USER: Optional[str] = Field( OPENSEARCH_USER: Optional[str] = Field(
description="Username for authenticating with OpenSearch", description="OpenSearch user",
default=None, default=None,
) )
OPENSEARCH_PASSWORD: Optional[str] = Field( OPENSEARCH_PASSWORD: Optional[str] = Field(
description="Password for authenticating with OpenSearch", description="OpenSearch password",
default=None, default=None,
) )
OPENSEARCH_SECURE: bool = Field( OPENSEARCH_SECURE: bool = Field(
description="Whether to use SSL/TLS encrypted connection for OpenSearch (True for HTTPS, False for HTTP)", description="whether to use SSL connection for OpenSearch",
default=False, default=False,
) )

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class OracleConfig(BaseSettings): class OracleConfig(BaseSettings):
""" """
Configuration settings for Oracle database ORACLE configs
""" """
ORACLE_HOST: Optional[str] = Field( ORACLE_HOST: Optional[str] = Field(
description="Hostname or IP address of the Oracle database server (e.g., 'localhost' or 'oracle.example.com')", description="ORACLE host",
default=None, default=None,
) )
ORACLE_PORT: PositiveInt = Field( ORACLE_PORT: Optional[PositiveInt] = Field(
description="Port number on which the Oracle database server is listening (default is 1521)", description="ORACLE port",
default=1521, default=1521,
) )
ORACLE_USER: Optional[str] = Field( ORACLE_USER: Optional[str] = Field(
description="Username for authenticating with the Oracle database", description="ORACLE user",
default=None, default=None,
) )
ORACLE_PASSWORD: Optional[str] = Field( ORACLE_PASSWORD: Optional[str] = Field(
description="Password for authenticating with the Oracle database", description="ORACLE password",
default=None, default=None,
) )
ORACLE_DATABASE: Optional[str] = Field( ORACLE_DATABASE: Optional[str] = Field(
description="Name of the Oracle database or service to connect to (e.g., 'ORCL' or 'pdborcl')", description="ORACLE database",
default=None, default=None,
) )

View File

@ -6,40 +6,30 @@ from pydantic_settings import BaseSettings
class PGVectorConfig(BaseSettings): class PGVectorConfig(BaseSettings):
""" """
Configuration settings for PGVector (PostgreSQL with vector extension) PGVector configs
""" """
PGVECTOR_HOST: Optional[str] = Field( PGVECTOR_HOST: Optional[str] = Field(
description="Hostname or IP address of the PostgreSQL server with PGVector extension (e.g., 'localhost')", description="PGVector host",
default=None, default=None,
) )
PGVECTOR_PORT: PositiveInt = Field( PGVECTOR_PORT: Optional[PositiveInt] = Field(
description="Port number on which the PostgreSQL server is listening (default is 5433)", description="PGVector port",
default=5433, default=5433,
) )
PGVECTOR_USER: Optional[str] = Field( PGVECTOR_USER: Optional[str] = Field(
description="Username for authenticating with the PostgreSQL database", description="PGVector user",
default=None, default=None,
) )
PGVECTOR_PASSWORD: Optional[str] = Field( PGVECTOR_PASSWORD: Optional[str] = Field(
description="Password for authenticating with the PostgreSQL database", description="PGVector password",
default=None, default=None,
) )
PGVECTOR_DATABASE: Optional[str] = Field( PGVECTOR_DATABASE: Optional[str] = Field(
description="Name of the PostgreSQL database to connect to", description="PGVector database",
default=None, default=None,
) )
PGVECTOR_MIN_CONNECTION: PositiveInt = Field(
description="Min connection of the PostgreSQL database",
default=1,
)
PGVECTOR_MAX_CONNECTION: PositiveInt = Field(
description="Max connection of the PostgreSQL database",
default=5,
)

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class PGVectoRSConfig(BaseSettings): class PGVectoRSConfig(BaseSettings):
""" """
Configuration settings for PGVecto.RS (Rust-based vector extension for PostgreSQL) PGVectoRS configs
""" """
PGVECTO_RS_HOST: Optional[str] = Field( PGVECTO_RS_HOST: Optional[str] = Field(
description="Hostname or IP address of the PostgreSQL server with PGVecto.RS extension (e.g., 'localhost')", description="PGVectoRS host",
default=None, default=None,
) )
PGVECTO_RS_PORT: PositiveInt = Field( PGVECTO_RS_PORT: Optional[PositiveInt] = Field(
description="Port number on which the PostgreSQL server with PGVecto.RS is listening (default is 5431)", description="PGVectoRS port",
default=5431, default=5431,
) )
PGVECTO_RS_USER: Optional[str] = Field( PGVECTO_RS_USER: Optional[str] = Field(
description="Username for authenticating with the PostgreSQL database using PGVecto.RS", description="PGVectoRS user",
default=None, default=None,
) )
PGVECTO_RS_PASSWORD: Optional[str] = Field( PGVECTO_RS_PASSWORD: Optional[str] = Field(
description="Password for authenticating with the PostgreSQL database using PGVecto.RS", description="PGVectoRS password",
default=None, default=None,
) )
PGVECTO_RS_DATABASE: Optional[str] = Field( PGVECTO_RS_DATABASE: Optional[str] = Field(
description="Name of the PostgreSQL database with PGVecto.RS extension to connect to", description="PGVectoRS database",
default=None, default=None,
) )

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class QdrantConfig(BaseSettings): class QdrantConfig(BaseSettings):
""" """
Configuration settings for Qdrant vector database Qdrant configs
""" """
QDRANT_URL: Optional[str] = Field( QDRANT_URL: Optional[str] = Field(
description="URL of the Qdrant server (e.g., 'http://localhost:6333' or 'https://qdrant.example.com')", description="Qdrant url",
default=None, default=None,
) )
QDRANT_API_KEY: Optional[str] = Field( QDRANT_API_KEY: Optional[str] = Field(
description="API key for authenticating with the Qdrant server", description="Qdrant api key",
default=None, default=None,
) )
QDRANT_CLIENT_TIMEOUT: NonNegativeInt = Field( QDRANT_CLIENT_TIMEOUT: NonNegativeInt = Field(
description="Timeout in seconds for Qdrant client operations (default is 20 seconds)", description="Qdrant client timeout in seconds",
default=20, default=20,
) )
QDRANT_GRPC_ENABLED: bool = Field( QDRANT_GRPC_ENABLED: bool = Field(
description="Whether to enable gRPC support for Qdrant connection (True for gRPC, False for HTTP)", description="whether enable grpc support for Qdrant connection",
default=False, default=False,
) )
QDRANT_GRPC_PORT: PositiveInt = Field( QDRANT_GRPC_PORT: PositiveInt = Field(
description="Port number for gRPC connection to Qdrant server (default is 6334)", description="Qdrant grpc port",
default=6334, default=6334,
) )

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class RelytConfig(BaseSettings): class RelytConfig(BaseSettings):
""" """
Configuration settings for Relyt database Relyt configs
""" """
RELYT_HOST: Optional[str] = Field( RELYT_HOST: Optional[str] = Field(
description="Hostname or IP address of the Relyt server (e.g., 'localhost' or 'relyt.example.com')", description="Relyt host",
default=None, default=None,
) )
RELYT_PORT: PositiveInt = Field( RELYT_PORT: PositiveInt = Field(
description="Port number on which the Relyt server is listening (default is 9200)", description="Relyt port",
default=9200, default=9200,
) )
RELYT_USER: Optional[str] = Field( RELYT_USER: Optional[str] = Field(
description="Username for authenticating with the Relyt database", description="Relyt user",
default=None, default=None,
) )
RELYT_PASSWORD: Optional[str] = Field( RELYT_PASSWORD: Optional[str] = Field(
description="Password for authenticating with the Relyt database", description="Relyt password",
default=None, default=None,
) )
RELYT_DATABASE: Optional[str] = Field( RELYT_DATABASE: Optional[str] = Field(
description="Name of the Relyt database to connect to (default is 'default')", description="Relyt database",
default="default", default="default",
) )

View File

@ -6,45 +6,45 @@ from pydantic_settings import BaseSettings
class TencentVectorDBConfig(BaseSettings): class TencentVectorDBConfig(BaseSettings):
""" """
Configuration settings for Tencent Vector Database Tencent Vector configs
""" """
TENCENT_VECTOR_DB_URL: Optional[str] = Field( TENCENT_VECTOR_DB_URL: Optional[str] = Field(
description="URL of the Tencent Vector Database service (e.g., 'https://vectordb.tencentcloudapi.com')", description="Tencent Vector URL",
default=None, default=None,
) )
TENCENT_VECTOR_DB_API_KEY: Optional[str] = Field( TENCENT_VECTOR_DB_API_KEY: Optional[str] = Field(
description="API key for authenticating with the Tencent Vector Database service", description="Tencent Vector API key",
default=None, default=None,
) )
TENCENT_VECTOR_DB_TIMEOUT: PositiveInt = Field( TENCENT_VECTOR_DB_TIMEOUT: PositiveInt = Field(
description="Timeout in seconds for Tencent Vector Database operations (default is 30 seconds)", description="Tencent Vector timeout in seconds",
default=30, default=30,
) )
TENCENT_VECTOR_DB_USERNAME: Optional[str] = Field( TENCENT_VECTOR_DB_USERNAME: Optional[str] = Field(
description="Username for authenticating with the Tencent Vector Database (if required)", description="Tencent Vector username",
default=None, default=None,
) )
TENCENT_VECTOR_DB_PASSWORD: Optional[str] = Field( TENCENT_VECTOR_DB_PASSWORD: Optional[str] = Field(
description="Password for authenticating with the Tencent Vector Database (if required)", description="Tencent Vector password",
default=None, default=None,
) )
TENCENT_VECTOR_DB_SHARD: PositiveInt = Field( TENCENT_VECTOR_DB_SHARD: PositiveInt = Field(
description="Number of shards for the Tencent Vector Database (default is 1)", description="Tencent Vector sharding number",
default=1, default=1,
) )
TENCENT_VECTOR_DB_REPLICAS: NonNegativeInt = Field( TENCENT_VECTOR_DB_REPLICAS: NonNegativeInt = Field(
description="Number of replicas for the Tencent Vector Database (default is 2)", description="Tencent Vector replicas",
default=2, default=2,
) )
TENCENT_VECTOR_DB_DATABASE: Optional[str] = Field( TENCENT_VECTOR_DB_DATABASE: Optional[str] = Field(
description="Name of the specific Tencent Vector Database to connect to", description="Tencent Vector Database",
default=None, default=None,
) )

View File

@ -1,70 +0,0 @@
from typing import Optional
from pydantic import Field, NonNegativeInt, PositiveInt
from pydantic_settings import BaseSettings
class TidbOnQdrantConfig(BaseSettings):
"""
Tidb on Qdrant configs
"""
TIDB_ON_QDRANT_URL: Optional[str] = Field(
description="Tidb on Qdrant url",
default=None,
)
TIDB_ON_QDRANT_API_KEY: Optional[str] = Field(
description="Tidb on Qdrant api key",
default=None,
)
TIDB_ON_QDRANT_CLIENT_TIMEOUT: NonNegativeInt = Field(
description="Tidb on Qdrant client timeout in seconds",
default=20,
)
TIDB_ON_QDRANT_GRPC_ENABLED: bool = Field(
description="whether enable grpc support for Tidb on Qdrant connection",
default=False,
)
TIDB_ON_QDRANT_GRPC_PORT: PositiveInt = Field(
description="Tidb on Qdrant grpc port",
default=6334,
)
TIDB_PUBLIC_KEY: Optional[str] = Field(
description="Tidb account public key",
default=None,
)
TIDB_PRIVATE_KEY: Optional[str] = Field(
description="Tidb account private key",
default=None,
)
TIDB_API_URL: Optional[str] = Field(
description="Tidb API url",
default=None,
)
TIDB_IAM_API_URL: Optional[str] = Field(
description="Tidb IAM API url",
default=None,
)
TIDB_REGION: Optional[str] = Field(
description="Tidb serverless region",
default="regions/aws-us-east-1",
)
TIDB_PROJECT_ID: Optional[str] = Field(
description="Tidb project id",
default=None,
)
TIDB_SPEND_LIMIT: Optional[int] = Field(
description="Tidb spend limit",
default=100,
)

View File

@ -6,30 +6,30 @@ from pydantic_settings import BaseSettings
class TiDBVectorConfig(BaseSettings): class TiDBVectorConfig(BaseSettings):
""" """
Configuration settings for TiDB Vector database TiDB Vector configs
""" """
TIDB_VECTOR_HOST: Optional[str] = Field( TIDB_VECTOR_HOST: Optional[str] = Field(
description="Hostname or IP address of the TiDB Vector server (e.g., 'localhost' or 'tidb.example.com')", description="TiDB Vector host",
default=None, default=None,
) )
TIDB_VECTOR_PORT: Optional[PositiveInt] = Field( TIDB_VECTOR_PORT: Optional[PositiveInt] = Field(
description="Port number on which the TiDB Vector server is listening (default is 4000)", description="TiDB Vector port",
default=4000, default=4000,
) )
TIDB_VECTOR_USER: Optional[str] = Field( TIDB_VECTOR_USER: Optional[str] = Field(
description="Username for authenticating with the TiDB Vector database", description="TiDB Vector user",
default=None, default=None,
) )
TIDB_VECTOR_PASSWORD: Optional[str] = Field( TIDB_VECTOR_PASSWORD: Optional[str] = Field(
description="Password for authenticating with the TiDB Vector database", description="TiDB Vector password",
default=None, default=None,
) )
TIDB_VECTOR_DATABASE: Optional[str] = Field( TIDB_VECTOR_DATABASE: Optional[str] = Field(
description="Name of the TiDB Vector database to connect to", description="TiDB Vector database",
default=None, default=None,
) )

View File

@ -1,20 +0,0 @@
from typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings
class UpstashConfig(BaseSettings):
"""
Configuration settings for Upstash vector database
"""
UPSTASH_VECTOR_URL: Optional[str] = Field(
description="URL of the upstash server (e.g., 'https://vector.upstash.io')",
default=None,
)
UPSTASH_VECTOR_TOKEN: Optional[str] = Field(
description="Token for authenticating with the upstash server",
default=None,
)

View File

@ -1,49 +0,0 @@
from typing import Optional
from pydantic import BaseModel, Field
class VikingDBConfig(BaseModel):
"""
Configuration for connecting to Volcengine VikingDB.
Refer to the following documentation for details on obtaining credentials:
https://www.volcengine.com/docs/6291/65568
"""
VIKINGDB_ACCESS_KEY: Optional[str] = Field(
description="The Access Key provided by Volcengine VikingDB for API authentication."
"Refer to the following documentation for details on obtaining credentials:"
"https://www.volcengine.com/docs/6291/65568",
default=None,
)
VIKINGDB_SECRET_KEY: Optional[str] = Field(
description="The Secret Key provided by Volcengine VikingDB for API authentication.",
default=None,
)
VIKINGDB_REGION: str = Field(
description="The region of the Volcengine VikingDB service.(e.g., 'cn-shanghai', 'cn-beijing').",
default="cn-shanghai",
)
VIKINGDB_HOST: str = Field(
description="The host of the Volcengine VikingDB service.(e.g., 'api-vikingdb.volces.com', \
'api-vikingdb.mlp.cn-shanghai.volces.com')",
default="api-vikingdb.mlp.cn-shanghai.volces.com",
)
VIKINGDB_SCHEME: str = Field(
description="The scheme of the Volcengine VikingDB service.(e.g., 'http', 'https').",
default="http",
)
VIKINGDB_CONNECTION_TIMEOUT: int = Field(
description="The connection timeout of the Volcengine VikingDB service.",
default=30,
)
VIKINGDB_SOCKET_TIMEOUT: int = Field(
description="The socket timeout of the Volcengine VikingDB service.",
default=30,
)

View File

@ -6,25 +6,25 @@ from pydantic_settings import BaseSettings
class WeaviateConfig(BaseSettings): class WeaviateConfig(BaseSettings):
""" """
Configuration settings for Weaviate vector database Weaviate configs
""" """
WEAVIATE_ENDPOINT: Optional[str] = Field( WEAVIATE_ENDPOINT: Optional[str] = Field(
description="URL of the Weaviate server (e.g., 'http://localhost:8080' or 'https://weaviate.example.com')", description="Weaviate endpoint URL",
default=None, default=None,
) )
WEAVIATE_API_KEY: Optional[str] = Field( WEAVIATE_API_KEY: Optional[str] = Field(
description="API key for authenticating with the Weaviate server", description="Weaviate API key",
default=None, default=None,
) )
WEAVIATE_GRPC_ENABLED: bool = Field( WEAVIATE_GRPC_ENABLED: bool = Field(
description="Whether to enable gRPC for Weaviate connection (True for gRPC, False for HTTP)", description="whether to enable gRPC for Weaviate connection",
default=True, default=True,
) )
WEAVIATE_BATCH_SIZE: PositiveInt = Field( WEAVIATE_BATCH_SIZE: PositiveInt = Field(
description="Number of objects to be processed in a single batch operation (default is 100)", description="Weaviate batch size",
default=100, default=100,
) )

View File

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

View File

@ -1,24 +1 @@
from configs import dify_config
HIDDEN_VALUE = "[__HIDDEN__]" HIDDEN_VALUE = "[__HIDDEN__]"
UUID_NIL = "00000000-0000-0000-0000-000000000000"
IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "webp", "gif", "svg"]
IMAGE_EXTENSIONS.extend([ext.upper() for ext in IMAGE_EXTENSIONS])
VIDEO_EXTENSIONS = ["mp4", "mov", "mpeg", "mpga"]
VIDEO_EXTENSIONS.extend([ext.upper() for ext in VIDEO_EXTENSIONS])
AUDIO_EXTENSIONS = ["mp3", "m4a", "wav", "webm", "amr"]
AUDIO_EXTENSIONS.extend([ext.upper() for ext in AUDIO_EXTENSIONS])
if dify_config.ETL_TYPE == "Unstructured":
DOCUMENT_EXTENSIONS = ["txt", "markdown", "md", "pdf", "html", "htm", "xlsx", "xls"]
DOCUMENT_EXTENSIONS.extend(("docx", "csv", "eml", "msg", "pptx", "xml", "epub"))
if dify_config.UNSTRUCTURED_API_URL:
DOCUMENT_EXTENSIONS.append("ppt")
DOCUMENT_EXTENSIONS.extend([ext.upper() for ext in DOCUMENT_EXTENSIONS])
else:
DOCUMENT_EXTENSIONS = ["txt", "markdown", "md", "pdf", "html", "htm", "xlsx", "xls", "docx", "csv"]
DOCUMENT_EXTENSIONS.extend([ext.upper() for ext in DOCUMENT_EXTENSIONS])

View File

@ -17,7 +17,6 @@ language_timezone_mapping = {
"hi-IN": "Asia/Kolkata", "hi-IN": "Asia/Kolkata",
"tr-TR": "Europe/Istanbul", "tr-TR": "Europe/Istanbul",
"fa-IR": "Asia/Tehran", "fa-IR": "Asia/Tehran",
"sl-SI": "Europe/Ljubljana",
} }
languages = list(language_timezone_mapping.keys()) languages = list(language_timezone_mapping.keys())

View File

@ -1,9 +1,7 @@
from contextvars import ContextVar from contextvars import ContextVar
from typing import TYPE_CHECKING
if TYPE_CHECKING: from core.workflow.entities.variable_pool import VariablePool
from core.workflow.entities.variable_pool import VariablePool
tenant_id: ContextVar[str] = ContextVar("tenant_id") tenant_id: ContextVar[str] = ContextVar("tenant_id")
workflow_variable_pool: ContextVar["VariablePool"] = ContextVar("workflow_variable_pool") workflow_variable_pool: ContextVar[VariablePool] = ContextVar("workflow_variable_pool")

View File

@ -1,6 +0,0 @@
from werkzeug.exceptions import HTTPException
class FilenameNotExistsError(HTTPException):
code = 400
description = "The specified filename does not exist."

View File

@ -1,24 +0,0 @@
from flask_restful import fields
parameters__system_parameters = {
"image_file_size_limit": fields.Integer,
"video_file_size_limit": fields.Integer,
"audio_file_size_limit": fields.Integer,
"file_size_limit": fields.Integer,
"workflow_file_upload_limit": fields.Integer,
}
parameters_fields = {
"opening_statement": fields.String,
"suggested_questions": fields.Raw,
"suggested_questions_after_answer": fields.Raw,
"speech_to_text": fields.Raw,
"text_to_speech": fields.Raw,
"retriever_resource": fields.Raw,
"annotation_reply": fields.Raw,
"more_like_this": fields.Raw,
"user_input_form": fields.Raw,
"sensitive_word_avoidance": fields.Raw,
"file_upload": fields.Raw,
"system_parameters": fields.Nested(parameters__system_parameters),
}

View File

@ -1,97 +0,0 @@
import mimetypes
import os
import re
import urllib.parse
from collections.abc import Mapping
from typing import Any
from uuid import uuid4
import httpx
from pydantic import BaseModel
from configs import dify_config
class FileInfo(BaseModel):
filename: str
extension: str
mimetype: str
size: int
def guess_file_info_from_response(response: httpx.Response):
url = str(response.url)
# Try to extract filename from URL
parsed_url = urllib.parse.urlparse(url)
url_path = parsed_url.path
filename = os.path.basename(url_path)
# If filename couldn't be extracted, use Content-Disposition header
if not filename:
content_disposition = response.headers.get("Content-Disposition")
if content_disposition:
filename_match = re.search(r'filename="?(.+)"?', content_disposition)
if filename_match:
filename = filename_match.group(1)
# If still no filename, generate a unique one
if not filename:
unique_name = str(uuid4())
filename = f"{unique_name}"
# Guess MIME type from filename first, then URL
mimetype, _ = mimetypes.guess_type(filename)
if mimetype is None:
mimetype, _ = mimetypes.guess_type(url)
if mimetype is None:
# If guessing fails, use Content-Type from response headers
mimetype = response.headers.get("Content-Type", "application/octet-stream")
extension = os.path.splitext(filename)[1]
# Ensure filename has an extension
if not extension:
extension = mimetypes.guess_extension(mimetype) or ".bin"
filename = f"{filename}{extension}"
return FileInfo(
filename=filename,
extension=extension,
mimetype=mimetype,
size=int(response.headers.get("Content-Length", -1)),
)
def get_parameters_from_feature_dict(*, features_dict: Mapping[str, Any], user_input_form: list[dict[str, Any]]):
return {
"opening_statement": features_dict.get("opening_statement"),
"suggested_questions": features_dict.get("suggested_questions", []),
"suggested_questions_after_answer": features_dict.get("suggested_questions_after_answer", {"enabled": False}),
"speech_to_text": features_dict.get("speech_to_text", {"enabled": False}),
"text_to_speech": features_dict.get("text_to_speech", {"enabled": False}),
"retriever_resource": features_dict.get("retriever_resource", {"enabled": False}),
"annotation_reply": features_dict.get("annotation_reply", {"enabled": False}),
"more_like_this": features_dict.get("more_like_this", {"enabled": False}),
"user_input_form": user_input_form,
"sensitive_word_avoidance": features_dict.get(
"sensitive_word_avoidance", {"enabled": False, "type": "", "configs": []}
),
"file_upload": features_dict.get(
"file_upload",
{
"image": {
"enabled": False,
"number_limits": 3,
"detail": "high",
"transfer_methods": ["remote_url", "local_file"],
}
},
),
"system_parameters": {
"image_file_size_limit": dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT,
"video_file_size_limit": dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT,
"audio_file_size_limit": dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT,
"file_size_limit": dify_config.UPLOAD_FILE_SIZE_LIMIT,
"workflow_file_upload_limit": dify_config.WORKFLOW_FILE_UPLOAD_LIMIT,
},
}

View File

@ -2,21 +2,9 @@ from flask import Blueprint
from libs.external_api import ExternalApi from libs.external_api import ExternalApi
from .files import FileApi, FilePreviewApi, FileSupportTypeApi
from .remote_files import RemoteFileInfoApi, RemoteFileUploadApi
bp = Blueprint("console", __name__, url_prefix="/console/api") bp = Blueprint("console", __name__, url_prefix="/console/api")
api = ExternalApi(bp) api = ExternalApi(bp)
# File
api.add_resource(FileApi, "/files/upload")
api.add_resource(FilePreviewApi, "/files/<uuid:file_id>/preview")
api.add_resource(FileSupportTypeApi, "/files/support-type")
# Remote files
api.add_resource(RemoteFileInfoApi, "/remote-files/<path:url>")
api.add_resource(RemoteFileUploadApi, "/remote-files/upload")
# Import other controllers # Import other controllers
from . import admin, apikey, extension, feature, ping, setup, version from . import admin, apikey, extension, feature, ping, setup, version
@ -49,15 +37,7 @@ from .auth import activate, data_source_bearer_auth, data_source_oauth, forgot_p
from .billing import billing from .billing import billing
# Import datasets controllers # Import datasets controllers
from .datasets import ( from .datasets import data_source, datasets, datasets_document, datasets_segments, external, file, hit_testing, website
data_source,
datasets,
datasets_document,
datasets_segments,
external,
hit_testing,
website,
)
# Import explore controllers # Import explore controllers
from .explore import ( from .explore import (

View File

@ -1,10 +1,10 @@
import os
from functools import wraps from functools import wraps
from flask import request from flask import request
from flask_restful import Resource, reqparse from flask_restful import Resource, reqparse
from werkzeug.exceptions import NotFound, Unauthorized from werkzeug.exceptions import NotFound, Unauthorized
from configs import dify_config
from constants.languages import supported_language from constants.languages import supported_language
from controllers.console import api from controllers.console import api
from controllers.console.wraps import only_edition_cloud from controllers.console.wraps import only_edition_cloud
@ -15,7 +15,7 @@ from models.model import App, InstalledApp, RecommendedApp
def admin_required(view): def admin_required(view):
@wraps(view) @wraps(view)
def decorated(*args, **kwargs): def decorated(*args, **kwargs):
if not dify_config.ADMIN_API_KEY: if not os.getenv("ADMIN_API_KEY"):
raise Unauthorized("API key is invalid.") raise Unauthorized("API key is invalid.")
auth_header = request.headers.get("Authorization") auth_header = request.headers.get("Authorization")
@ -31,7 +31,7 @@ def admin_required(view):
if auth_scheme != "bearer": if auth_scheme != "bearer":
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.") raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
if dify_config.ADMIN_API_KEY != auth_token: if os.getenv("ADMIN_API_KEY") != auth_token:
raise Unauthorized("API key is invalid.") raise Unauthorized("API key is invalid.")
return view(*args, **kwargs) return view(*args, **kwargs)

View File

@ -10,7 +10,8 @@ from models.dataset import Dataset
from models.model import ApiToken, App from models.model import ApiToken, App
from . import api from . import api
from .wraps import account_initialization_required, setup_required from .setup import setup_required
from .wraps import account_initialization_required
api_key_fields = { api_key_fields = {
"id": fields.String, "id": fields.String,

View File

@ -1,7 +1,8 @@
from flask_restful import Resource, reqparse from flask_restful import Resource, reqparse
from controllers.console import api from controllers.console import api
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from libs.login import login_required from libs.login import login_required
from services.advanced_prompt_template_service import AdvancedPromptTemplateService from services.advanced_prompt_template_service import AdvancedPromptTemplateService

View File

@ -2,7 +2,8 @@ from flask_restful import Resource, reqparse
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from libs.helper import uuid_value from libs.helper import uuid_value
from libs.login import login_required from libs.login import login_required
from models.model import AppMode from models.model import AppMode

View File

@ -6,11 +6,8 @@ from werkzeug.exceptions import Forbidden
from controllers.console import api from controllers.console import api
from controllers.console.app.error import NoFileUploadedError from controllers.console.app.error import NoFileUploadedError
from controllers.console.datasets.error import TooManyFilesError from controllers.console.datasets.error import TooManyFilesError
from controllers.console.wraps import ( from controllers.console.setup import setup_required
account_initialization_required, from controllers.console.wraps import account_initialization_required, cloud_edition_billing_resource_check
cloud_edition_billing_resource_check,
setup_required,
)
from extensions.ext_redis import redis_client from extensions.ext_redis import redis_client
from fields.annotation_fields import ( from fields.annotation_fields import (
annotation_fields, annotation_fields,

View File

@ -6,12 +6,8 @@ from werkzeug.exceptions import BadRequest, Forbidden, abort
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import ( from controllers.console.setup import setup_required
account_initialization_required, from controllers.console.wraps import account_initialization_required, cloud_edition_billing_resource_check
cloud_edition_billing_resource_check,
enterprise_license_required,
setup_required,
)
from core.ops.ops_trace_manager import OpsTraceManager from core.ops.ops_trace_manager import OpsTraceManager
from fields.app_fields import ( from fields.app_fields import (
app_detail_fields, app_detail_fields,
@ -29,7 +25,6 @@ class AppListApi(Resource):
@setup_required @setup_required
@login_required @login_required
@account_initialization_required @account_initialization_required
@enterprise_license_required
def get(self): def get(self):
"""Get app list""" """Get app list"""
@ -151,7 +146,6 @@ class AppApi(Resource):
@setup_required @setup_required
@login_required @login_required
@account_initialization_required @account_initialization_required
@enterprise_license_required
@get_app_model @get_app_model
@marshal_with(app_detail_fields_with_site) @marshal_with(app_detail_fields_with_site)
def get(self, app_model): def get(self, app_model):

View File

@ -18,7 +18,8 @@ from controllers.console.app.error import (
UnsupportedAudioTypeError, UnsupportedAudioTypeError,
) )
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
from core.model_runtime.errors.invoke import InvokeError from core.model_runtime.errors.invoke import InvokeError
from libs.login import login_required from libs.login import login_required
@ -70,7 +71,7 @@ class ChatMessageAudioApi(Resource):
except ValueError as e: except ValueError as e:
raise e raise e
except Exception as e: except Exception as e:
logging.exception("Failed to handle post request to ChatMessageAudioApi") logging.exception(f"internal server error, {str(e)}.")
raise InternalServerError() raise InternalServerError()
@ -128,7 +129,7 @@ class ChatMessageTextApi(Resource):
except ValueError as e: except ValueError as e:
raise e raise e
except Exception as e: except Exception as e:
logging.exception("Failed to handle post request to ChatMessageTextApi") logging.exception(f"internal server error, {str(e)}.")
raise InternalServerError() raise InternalServerError()
@ -170,7 +171,7 @@ class TextModesApi(Resource):
except ValueError as e: except ValueError as e:
raise e raise e
except Exception as e: except Exception as e:
logging.exception("Failed to handle get request to TextModesApi") logging.exception(f"internal server error, {str(e)}.")
raise InternalServerError() raise InternalServerError()

View File

@ -15,7 +15,8 @@ from controllers.console.app.error import (
ProviderQuotaExceededError, ProviderQuotaExceededError,
) )
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError
from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.apps.base_app_queue_manager import AppQueueManager
from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.app_invoke_entities import InvokeFrom
@ -108,7 +109,6 @@ class ChatMessageApi(Resource):
parser.add_argument("files", type=list, required=False, location="json") parser.add_argument("files", type=list, required=False, location="json")
parser.add_argument("model_config", type=dict, required=True, location="json") parser.add_argument("model_config", type=dict, required=True, location="json")
parser.add_argument("conversation_id", type=uuid_value, location="json") parser.add_argument("conversation_id", type=uuid_value, location="json")
parser.add_argument("parent_message_id", type=uuid_value, required=False, location="json")
parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json") parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json")
parser.add_argument("retriever_from", type=str, required=False, default="dev", location="json") parser.add_argument("retriever_from", type=str, required=False, default="dev", location="json")
args = parser.parse_args() args = parser.parse_args()

View File

@ -10,7 +10,8 @@ from werkzeug.exceptions import Forbidden, NotFound
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.app_invoke_entities import InvokeFrom
from extensions.ext_database import db from extensions.ext_database import db
from fields.conversation_fields import ( from fields.conversation_fields import (
@ -21,8 +22,7 @@ from fields.conversation_fields import (
) )
from libs.helper import DatetimeString from libs.helper import DatetimeString
from libs.login import login_required from libs.login import login_required
from models import Conversation, EndUser, Message, MessageAnnotation from models.model import AppMode, Conversation, EndUser, Message, MessageAnnotation
from models.model import AppMode
class CompletionConversationApi(Resource): class CompletionConversationApi(Resource):
@ -188,7 +188,6 @@ class ChatConversationApi(Resource):
subquery.c.from_end_user_session_id.ilike(keyword_filter), subquery.c.from_end_user_session_id.ilike(keyword_filter),
), ),
) )
.group_by(Conversation.id)
) )
account = current_user account = current_user

View File

@ -4,7 +4,8 @@ from sqlalchemy.orm import Session
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from extensions.ext_database import db from extensions.ext_database import db
from fields.conversation_variable_fields import paginated_conversation_variable_fields from fields.conversation_variable_fields import paginated_conversation_variable_fields
from libs.login import login_required from libs.login import login_required

View File

@ -10,7 +10,8 @@ from controllers.console.app.error import (
ProviderNotInitializeError, ProviderNotInitializeError,
ProviderQuotaExceededError, ProviderQuotaExceededError,
) )
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
from core.llm_generator.llm_generator import LLMGenerator from core.llm_generator.llm_generator import LLMGenerator
from core.model_runtime.errors.invoke import InvokeError from core.model_runtime.errors.invoke import InvokeError
@ -51,39 +52,4 @@ class RuleGenerateApi(Resource):
return rules return rules
class RuleCodeGenerateApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("instruction", type=str, required=True, nullable=False, location="json")
parser.add_argument("model_config", type=dict, required=True, nullable=False, location="json")
parser.add_argument("no_variable", type=bool, required=True, default=False, location="json")
parser.add_argument("code_language", type=str, required=False, default="javascript", location="json")
args = parser.parse_args()
account = current_user
CODE_GENERATION_MAX_TOKENS = int(os.getenv("CODE_GENERATION_MAX_TOKENS", "1024"))
try:
code_result = LLMGenerator.generate_code(
tenant_id=account.current_tenant_id,
instruction=args["instruction"],
model_config=args["model_config"],
code_language=args["code_language"],
max_tokens=CODE_GENERATION_MAX_TOKENS,
)
except ProviderTokenNotInitError as ex:
raise ProviderNotInitializeError(ex.description)
except QuotaExceededError:
raise ProviderQuotaExceededError()
except ModelCurrentlyNotSupportError:
raise ProviderModelCurrentlyNotSupportError()
except InvokeError as e:
raise CompletionRequestError(e.description)
return code_result
api.add_resource(RuleGenerateApi, "/rule-generate") api.add_resource(RuleGenerateApi, "/rule-generate")
api.add_resource(RuleCodeGenerateApi, "/rule-code-generate")

View File

@ -14,11 +14,8 @@ from controllers.console.app.error import (
) )
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.explore.error import AppSuggestedQuestionsAfterAnswerDisabledError from controllers.console.explore.error import AppSuggestedQuestionsAfterAnswerDisabledError
from controllers.console.wraps import ( from controllers.console.setup import setup_required
account_initialization_required, from controllers.console.wraps import account_initialization_required, cloud_edition_billing_resource_check
cloud_edition_billing_resource_check,
setup_required,
)
from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.app_invoke_entities import InvokeFrom
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
from core.model_runtime.errors.invoke import InvokeError from core.model_runtime.errors.invoke import InvokeError

View File

@ -6,7 +6,8 @@ from flask_restful import Resource
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from core.agent.entities import AgentToolEntity from core.agent.entities import AgentToolEntity
from core.tools.tool_manager import ToolManager from core.tools.tool_manager import ToolManager
from core.tools.utils.configuration import ToolParameterConfigurationManager from core.tools.utils.configuration import ToolParameterConfigurationManager

View File

@ -2,7 +2,8 @@ from flask_restful import Resource, reqparse
from controllers.console import api from controllers.console import api
from controllers.console.app.error import TracingConfigCheckError, TracingConfigIsExist, TracingConfigNotExist from controllers.console.app.error import TracingConfigCheckError, TracingConfigIsExist, TracingConfigNotExist
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from libs.login import login_required from libs.login import login_required
from services.ops_service import OpsService from services.ops_service import OpsService

View File

@ -7,11 +7,12 @@ from werkzeug.exceptions import Forbidden, NotFound
from constants.languages import supported_language from constants.languages import supported_language
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from extensions.ext_database import db from extensions.ext_database import db
from fields.app_fields import app_site_fields from fields.app_fields import app_site_fields
from libs.login import login_required from libs.login import login_required
from models import Site from models.model import Site
def parse_app_site_args(): def parse_app_site_args():

View File

@ -8,7 +8,8 @@ from flask_restful import Resource, reqparse
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from extensions.ext_database import db from extensions.ext_database import db
from libs.helper import DatetimeString from libs.helper import DatetimeString
from libs.login import login_required from libs.login import login_required

View File

@ -9,17 +9,18 @@ import services
from controllers.console import api from controllers.console import api
from controllers.console.app.error import ConversationCompletedError, DraftWorkflowNotExist, DraftWorkflowNotSync from controllers.console.app.error import ConversationCompletedError, DraftWorkflowNotExist, DraftWorkflowNotSync
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from core.app.apps.base_app_queue_manager import AppQueueManager from core.app.apps.base_app_queue_manager import AppQueueManager
from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.app_invoke_entities import InvokeFrom
from factories import variable_factory from core.app.segments import factory
from core.errors.error import AppInvokeQuotaExceededError
from fields.workflow_fields import workflow_fields from fields.workflow_fields import workflow_fields
from fields.workflow_run_fields import workflow_run_node_execution_fields from fields.workflow_run_fields import workflow_run_node_execution_fields
from libs import helper from libs import helper
from libs.helper import TimestampField, uuid_value from libs.helper import TimestampField, uuid_value
from libs.login import current_user, login_required from libs.login import current_user, login_required
from models import App from models.model import App, AppMode
from models.model import AppMode
from services.app_dsl_service import AppDslService from services.app_dsl_service import AppDslService
from services.app_generate_service import AppGenerateService from services.app_generate_service import AppGenerateService
from services.errors.app import WorkflowHashNotEqualError from services.errors.app import WorkflowHashNotEqualError
@ -100,13 +101,9 @@ class DraftWorkflowApi(Resource):
try: try:
environment_variables_list = args.get("environment_variables") or [] environment_variables_list = args.get("environment_variables") or []
environment_variables = [ environment_variables = [factory.build_variable_from_mapping(obj) for obj in environment_variables_list]
variable_factory.build_variable_from_mapping(obj) for obj in environment_variables_list
]
conversation_variables_list = args.get("conversation_variables") or [] conversation_variables_list = args.get("conversation_variables") or []
conversation_variables = [ conversation_variables = [factory.build_variable_from_mapping(obj) for obj in conversation_variables_list]
variable_factory.build_variable_from_mapping(obj) for obj in conversation_variables_list
]
workflow = workflow_service.sync_draft_workflow( workflow = workflow_service.sync_draft_workflow(
app_model=app_model, app_model=app_model,
graph=args["graph"], graph=args["graph"],
@ -169,8 +166,6 @@ class AdvancedChatDraftWorkflowRunApi(Resource):
parser.add_argument("query", type=str, required=True, location="json", default="") parser.add_argument("query", type=str, required=True, location="json", default="")
parser.add_argument("files", type=list, location="json") parser.add_argument("files", type=list, location="json")
parser.add_argument("conversation_id", type=uuid_value, location="json") parser.add_argument("conversation_id", type=uuid_value, location="json")
parser.add_argument("parent_message_id", type=uuid_value, required=False, location="json")
args = parser.parse_args() args = parser.parse_args()
try: try:
@ -276,15 +271,17 @@ class DraftWorkflowRunApi(Resource):
parser.add_argument("files", type=list, required=False, location="json") parser.add_argument("files", type=list, required=False, location="json")
args = parser.parse_args() args = parser.parse_args()
response = AppGenerateService.generate( try:
app_model=app_model, response = AppGenerateService.generate(
user=current_user, app_model=app_model, user=current_user, args=args, invoke_from=InvokeFrom.DEBUGGER, streaming=True
args=args, )
invoke_from=InvokeFrom.DEBUGGER,
streaming=True,
)
return helper.compact_generate_response(response) return helper.compact_generate_response(response)
except (ValueError, AppInvokeQuotaExceededError) as e:
raise e
except Exception as e:
logging.exception("internal server error.")
raise InternalServerError()
class WorkflowTaskStopApi(Resource): class WorkflowTaskStopApi(Resource):

View File

@ -3,11 +3,11 @@ from flask_restful.inputs import int_range
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from fields.workflow_app_log_fields import workflow_app_log_pagination_fields from fields.workflow_app_log_fields import workflow_app_log_pagination_fields
from libs.login import login_required from libs.login import login_required
from models import App from models.model import App, AppMode
from models.model import AppMode
from services.workflow_app_service import WorkflowAppService from services.workflow_app_service import WorkflowAppService

View File

@ -3,7 +3,8 @@ from flask_restful.inputs import int_range
from controllers.console import api from controllers.console import api
from controllers.console.app.wraps import get_app_model from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.setup import setup_required
from controllers.console.wraps import account_initialization_required
from fields.workflow_run_fields import ( from fields.workflow_run_fields import (
advanced_chat_workflow_run_pagination_fields, advanced_chat_workflow_run_pagination_fields,
workflow_run_detail_fields, workflow_run_detail_fields,
@ -12,8 +13,7 @@ from fields.workflow_run_fields import (
) )
from libs.helper import uuid_value from libs.helper import uuid_value
from libs.login import login_required from libs.login import login_required
from models import App from models.model import App, AppMode
from models.model import AppMode
from services.workflow_run_service import WorkflowRunService from services.workflow_run_service import WorkflowRunService

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