Co-authored-by: André de Matteo <andre.matteo@accenture.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>tags/1.5.0
| CODE_GENERATION_MAX_TOKENS=1024 | CODE_GENERATION_MAX_TOKENS=1024 | ||||
| PLUGIN_BASED_TOKEN_COUNTING_ENABLED=false | PLUGIN_BASED_TOKEN_COUNTING_ENABLED=false | ||||
| # Mail configuration, support: resend, smtp | |||||
| # Mail configuration, support: resend, smtp, sendgrid | |||||
| MAIL_TYPE= | MAIL_TYPE= | ||||
| # If using SendGrid, use the 'from' field for authentication if necessary. | |||||
| MAIL_DEFAULT_SEND_FROM=no-reply <no-reply@dify.ai> | MAIL_DEFAULT_SEND_FROM=no-reply <no-reply@dify.ai> | ||||
| # resend configuration | |||||
| RESEND_API_KEY= | RESEND_API_KEY= | ||||
| RESEND_API_URL=https://api.resend.com | RESEND_API_URL=https://api.resend.com | ||||
| # smtp configuration | # smtp configuration | ||||
| SMTP_PASSWORD=abc | SMTP_PASSWORD=abc | ||||
| SMTP_USE_TLS=true | SMTP_USE_TLS=true | ||||
| SMTP_OPPORTUNISTIC_TLS=false | SMTP_OPPORTUNISTIC_TLS=false | ||||
| # Sendgid configuration | |||||
| SENDGRID_API_KEY= | |||||
| # Sentry configuration | # Sentry configuration | ||||
| SENTRY_DSN= | SENTRY_DSN= | ||||
| """ | """ | ||||
| MAIL_TYPE: Optional[str] = Field( | MAIL_TYPE: Optional[str] = Field( | ||||
| description="Email service provider type ('smtp' or 'resend'), default to None.", | |||||
| description="Email service provider type ('smtp' or 'resend' or 'sendGrid), default to None.", | |||||
| default=None, | default=None, | ||||
| ) | ) | ||||
| default=50, | default=50, | ||||
| ) | ) | ||||
| SENDGRID_API_KEY: Optional[str] = Field( | |||||
| description="API key for SendGrid service", | |||||
| default=None, | |||||
| ) | |||||
| class RagEtlConfig(BaseSettings): | class RagEtlConfig(BaseSettings): | ||||
| """ | """ |
| use_tls=dify_config.SMTP_USE_TLS, | use_tls=dify_config.SMTP_USE_TLS, | ||||
| opportunistic_tls=dify_config.SMTP_OPPORTUNISTIC_TLS, | opportunistic_tls=dify_config.SMTP_OPPORTUNISTIC_TLS, | ||||
| ) | ) | ||||
| case "sendgrid": | |||||
| from libs.sendgrid import SendGridClient | |||||
| if not dify_config.SENDGRID_API_KEY: | |||||
| raise ValueError("SENDGRID_API_KEY is required for SendGrid mail type") | |||||
| self._client = SendGridClient( | |||||
| sendgrid_api_key=dify_config.SENDGRID_API_KEY, _from=dify_config.MAIL_DEFAULT_SEND_FROM or "" | |||||
| ) | |||||
| case _: | case _: | ||||
| raise ValueError("Unsupported mail type {}".format(mail_type)) | raise ValueError("Unsupported mail type {}".format(mail_type)) | ||||
| import logging | |||||
| import sendgrid # type: ignore | |||||
| from python_http_client.exceptions import ForbiddenError, UnauthorizedError | |||||
| from sendgrid.helpers.mail import Content, Email, Mail, To # type: ignore | |||||
| class SendGridClient: | |||||
| def __init__(self, sendgrid_api_key: str, _from: str): | |||||
| self.sendgrid_api_key = sendgrid_api_key | |||||
| self._from = _from | |||||
| def send(self, mail: dict): | |||||
| logging.debug("Sending email with SendGrid") | |||||
| try: | |||||
| _to = mail["to"] | |||||
| if not _to: | |||||
| raise ValueError("SendGridClient: Cannot send email: recipient address is missing.") | |||||
| sg = sendgrid.SendGridAPIClient(api_key=self.sendgrid_api_key) | |||||
| from_email = Email(self._from) | |||||
| to_email = To(_to) | |||||
| subject = mail["subject"] | |||||
| content = Content("text/html", mail["html"]) | |||||
| mail = Mail(from_email, to_email, subject, content) | |||||
| mail_json = mail.get() # type: ignore | |||||
| response = sg.client.mail.send.post(request_body=mail_json) | |||||
| logging.debug(response.status_code) | |||||
| logging.debug(response.body) | |||||
| logging.debug(response.headers) | |||||
| except TimeoutError as e: | |||||
| logging.exception("SendGridClient Timeout occurred while sending email") | |||||
| raise | |||||
| except (UnauthorizedError, ForbiddenError) as e: | |||||
| logging.exception("SendGridClient Authentication failed. Verify that your credentials and the 'from") | |||||
| raise | |||||
| except Exception as e: | |||||
| logging.exception(f"SendGridClient Unexpected error occurred while sending email to {_to}") | |||||
| raise |
| [mypy-flask_restful.inputs] | [mypy-flask_restful.inputs] | ||||
| ignore_missing_imports=True | ignore_missing_imports=True | ||||
| "weave~=0.51.0", | "weave~=0.51.0", | ||||
| "yarl~=1.18.3", | "yarl~=1.18.3", | ||||
| "webvtt-py~=0.5.1", | "webvtt-py~=0.5.1", | ||||
| "sendgrid~=6.12.3", | |||||
| ] | ] | ||||
| # Before adding new dependency, consider place it in | # Before adding new dependency, consider place it in | ||||
| # alphabet order (a-z) and suitable group. | # alphabet order (a-z) and suitable group. |
| { name = "readabilipy" }, | { name = "readabilipy" }, | ||||
| { name = "redis", extra = ["hiredis"] }, | { name = "redis", extra = ["hiredis"] }, | ||||
| { name = "resend" }, | { name = "resend" }, | ||||
| { name = "sendgrid" }, | |||||
| { name = "sentry-sdk", extra = ["flask"] }, | { name = "sentry-sdk", extra = ["flask"] }, | ||||
| { name = "sqlalchemy" }, | { name = "sqlalchemy" }, | ||||
| { name = "starlette" }, | { name = "starlette" }, | ||||
| { name = "readabilipy", specifier = "~=0.3.0" }, | { name = "readabilipy", specifier = "~=0.3.0" }, | ||||
| { name = "redis", extras = ["hiredis"], specifier = "~=6.1.0" }, | { name = "redis", extras = ["hiredis"], specifier = "~=6.1.0" }, | ||||
| { name = "resend", specifier = "~=2.9.0" }, | { name = "resend", specifier = "~=2.9.0" }, | ||||
| { name = "sendgrid", specifier = "~=6.12.3" }, | |||||
| { name = "sentry-sdk", extras = ["flask"], specifier = "~=2.28.0" }, | { name = "sentry-sdk", extras = ["flask"], specifier = "~=2.28.0" }, | ||||
| { name = "sqlalchemy", specifier = "~=2.0.29" }, | { name = "sqlalchemy", specifier = "~=2.0.29" }, | ||||
| { name = "starlette", specifier = "==0.41.0" }, | { name = "starlette", specifier = "==0.41.0" }, | ||||
| { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922 }, | { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922 }, | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "ecdsa" | |||||
| version = "0.19.1" | |||||
| source = { registry = "https://pypi.org/simple" } | |||||
| dependencies = [ | |||||
| { name = "six" }, | |||||
| ] | |||||
| sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" } | |||||
| wheels = [ | |||||
| { url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" }, | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "elastic-transport" | name = "elastic-transport" | ||||
| version = "8.17.1" | version = "8.17.1" | ||||
| { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, | { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "python-http-client" | |||||
| version = "3.3.7" | |||||
| source = { registry = "https://pypi.org/simple" } | |||||
| sdist = { url = "https://files.pythonhosted.org/packages/56/fa/284e52a8c6dcbe25671f02d217bf2f85660db940088faf18ae7a05e97313/python_http_client-3.3.7.tar.gz", hash = "sha256:bf841ee45262747e00dec7ee9971dfb8c7d83083f5713596488d67739170cea0", size = 9377, upload-time = "2022-03-09T20:23:56.386Z" } | |||||
| wheels = [ | |||||
| { url = "https://files.pythonhosted.org/packages/29/31/9b360138f4e4035ee9dac4fe1132b6437bd05751aaf1db2a2d83dc45db5f/python_http_client-3.3.7-py3-none-any.whl", hash = "sha256:ad371d2bbedc6ea15c26179c6222a78bc9308d272435ddf1d5c84f068f249a36", size = 8352, upload-time = "2022-03-09T20:23:54.862Z" }, | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "python-iso639" | name = "python-iso639" | ||||
| version = "2025.2.18" | version = "2025.2.18" | ||||
| { url = "https://files.pythonhosted.org/packages/6c/42/cd8dc81f8060de1f14960885ad5b2d2651f41de8b93d09f3f919d6567a5a/scipy_stubs-1.15.3.0-py3-none-any.whl", hash = "sha256:a251254cf4fd6e7fb87c55c1feee92d32ddbc1f542ecdf6a0159cdb81c2fb62d", size = 459062 }, | { url = "https://files.pythonhosted.org/packages/6c/42/cd8dc81f8060de1f14960885ad5b2d2651f41de8b93d09f3f919d6567a5a/scipy_stubs-1.15.3.0-py3-none-any.whl", hash = "sha256:a251254cf4fd6e7fb87c55c1feee92d32ddbc1f542ecdf6a0159cdb81c2fb62d", size = 459062 }, | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "sendgrid" | |||||
| version = "6.12.4" | |||||
| source = { registry = "https://pypi.org/simple" } | |||||
| dependencies = [ | |||||
| { name = "ecdsa" }, | |||||
| { name = "python-http-client" }, | |||||
| { name = "werkzeug" }, | |||||
| ] | |||||
| sdist = { url = "https://files.pythonhosted.org/packages/11/31/62e00433878dccf33edf07f8efa417b9030a2464eb3b04bbd797a11b4447/sendgrid-6.12.4.tar.gz", hash = "sha256:9e88b849daf0fa4bdf256c3b5da9f5a3272402c0c2fd6b1928c9de440db0a03d", size = 50271, upload-time = "2025-06-12T10:29:37.213Z" } | |||||
| wheels = [ | |||||
| { url = "https://files.pythonhosted.org/packages/c2/9c/45d068fd831a65e6ed1e2ab3233de58784842afdc62fdcdd0a01bbb6b39d/sendgrid-6.12.4-py3-none-any.whl", hash = "sha256:9a211b96241e63bd5b9ed9afcc8608f4bcac426e4a319b3920ab877c8426e92c", size = 102122, upload-time = "2025-06-12T10:29:35.457Z" }, | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "sentry-sdk" | name = "sentry-sdk" | ||||
| version = "2.28.0" | version = "2.28.0" |
| # Mail related configuration | # Mail related configuration | ||||
| # ------------------------------ | # ------------------------------ | ||||
| # Mail type, support: resend, smtp | |||||
| # Mail type, support: resend, smtp, sendgrid | |||||
| MAIL_TYPE=resend | MAIL_TYPE=resend | ||||
| # Default send from email address, if not specified | # Default send from email address, if not specified | ||||
| # If using SendGrid, use the 'from' field for authentication if necessary. | |||||
| MAIL_DEFAULT_SEND_FROM= | MAIL_DEFAULT_SEND_FROM= | ||||
| # API-Key for the Resend email provider, used when MAIL_TYPE is `resend`. | # API-Key for the Resend email provider, used when MAIL_TYPE is `resend`. | ||||
| SMTP_USE_TLS=true | SMTP_USE_TLS=true | ||||
| SMTP_OPPORTUNISTIC_TLS=false | SMTP_OPPORTUNISTIC_TLS=false | ||||
| # Sendgid configuration | |||||
| SENDGRID_API_KEY= | |||||
| # ------------------------------ | # ------------------------------ | ||||
| # Others Configuration | # Others Configuration | ||||
| # ------------------------------ | # ------------------------------ |
| SMTP_PASSWORD: ${SMTP_PASSWORD:-} | SMTP_PASSWORD: ${SMTP_PASSWORD:-} | ||||
| SMTP_USE_TLS: ${SMTP_USE_TLS:-true} | SMTP_USE_TLS: ${SMTP_USE_TLS:-true} | ||||
| SMTP_OPPORTUNISTIC_TLS: ${SMTP_OPPORTUNISTIC_TLS:-false} | SMTP_OPPORTUNISTIC_TLS: ${SMTP_OPPORTUNISTIC_TLS:-false} | ||||
| SENDGRID_API_KEY: ${SENDGRID_API_KEY:-} | |||||
| INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-4000} | INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: ${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH:-4000} | ||||
| INVITE_EXPIRY_HOURS: ${INVITE_EXPIRY_HOURS:-72} | INVITE_EXPIRY_HOURS: ${INVITE_EXPIRY_HOURS:-72} | ||||
| RESET_PASSWORD_TOKEN_EXPIRY_MINUTES: ${RESET_PASSWORD_TOKEN_EXPIRY_MINUTES:-5} | RESET_PASSWORD_TOKEN_EXPIRY_MINUTES: ${RESET_PASSWORD_TOKEN_EXPIRY_MINUTES:-5} |