= ({
{t(`billing.plans.${type}.for`)}
- {enableEducationPlan && !isEducationAccount && (
+ {enableEducationPlan && (!isEducationAccount || isAboutToExpire) && (
)}
- {formatTime(log.created_at, t('appLog.dateTimeFormat') as string)} |
+ {formatTime(log.created_at, t('appLog.dateTimeFormat') as string)} |
{statusTdRender(log.workflow_run.status)} |
Date: Thu, 21 Aug 2025 14:43:08 +0800
Subject: [PATCH 038/169] =?UTF-8?q?fix(api):Fix=20the=20issue=20of=20empty?=
=?UTF-8?q?=20and=20not=20empty=20operations=20failing=20in=20k=E2=80=A6?=
=?UTF-8?q?=20(#24276)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: liumin
---
api/core/rag/retrieval/dataset_retrieval.py | 2 +-
.../nodes/knowledge_retrieval/knowledge_retrieval_node.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py
index a25bc65646..cd4af72832 100644
--- a/api/core/rag/retrieval/dataset_retrieval.py
+++ b/api/core/rag/retrieval/dataset_retrieval.py
@@ -1012,7 +1012,7 @@ class DatasetRetrieval:
def _process_metadata_filter_func(
self, sequence: int, condition: str, metadata_name: str, value: Optional[Any], filters: list
):
- if value is None:
+ if value is None and condition not in ("empty", "not empty"):
return
key = f"{metadata_name}_{sequence}"
diff --git a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py
index 7303b68501..e8afe6aa4a 100644
--- a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py
+++ b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py
@@ -572,7 +572,7 @@ class KnowledgeRetrievalNode(BaseNode):
def _process_metadata_filter_func(
self, sequence: int, condition: str, metadata_name: str, value: Optional[Any], filters: list
):
- if value is None:
+ if value is None and condition not in ("empty", "not empty"):
return
key = f"{metadata_name}_{sequence}"
From a183b2affbee2438cc11dc2906d3857419f9d73c Mon Sep 17 00:00:00 2001
From: 8bitpd <51897400+lpdink@users.noreply.github.com>
Date: Thu, 21 Aug 2025 15:00:26 +0800
Subject: [PATCH 039/169] fix: rollback when AnalyticDB create zhparser failed
(#24260)
Co-authored-by: xiaozeyu
---
.../rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py
index 2df17181a4..bb61b71bb1 100644
--- a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py
+++ b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py
@@ -105,9 +105,11 @@ class AnalyticdbVectorBySql:
conn.close()
self.pool = self._create_connection_pool()
with self._get_cursor() as cur:
+ conn = cur.connection
try:
cur.execute("CREATE EXTENSION IF NOT EXISTS zhparser;")
except Exception as e:
+ conn.rollback()
raise RuntimeError(
"Failed to create zhparser extension. Please ensure it is available in your AnalyticDB."
) from e
@@ -115,6 +117,7 @@ class AnalyticdbVectorBySql:
cur.execute("CREATE TEXT SEARCH CONFIGURATION zh_cn (PARSER = zhparser)")
cur.execute("ALTER TEXT SEARCH CONFIGURATION zh_cn ADD MAPPING FOR n,v,a,i,e,l,x WITH simple")
except Exception as e:
+ conn.rollback()
if "already exists" not in str(e):
raise e
cur.execute(
From 0e1dfb4161b77ab28af63cc18f2b3467b962491b Mon Sep 17 00:00:00 2001
From: me0106
Date: Thu, 21 Aug 2025 15:28:07 +0800
Subject: [PATCH 040/169] fix: value_type check failed when updating variables
(#24274)
Co-authored-by: me
---
api/core/variables/types.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/api/core/variables/types.py b/api/core/variables/types.py
index d28fb11401..6629056042 100644
--- a/api/core/variables/types.py
+++ b/api/core/variables/types.py
@@ -126,7 +126,7 @@ class SegmentType(StrEnum):
"""
if self.is_array_type():
return self._validate_array(value, array_validation)
- elif self == SegmentType.NUMBER:
+ elif self in [SegmentType.INTEGER, SegmentType.FLOAT, SegmentType.NUMBER]:
return isinstance(value, (int, float))
elif self == SegmentType.STRING:
return isinstance(value, str)
@@ -166,7 +166,6 @@ _ARRAY_TYPES = frozenset(
]
)
-
_NUMERICAL_TYPES = frozenset(
[
SegmentType.NUMBER,
From ad8e82ee1ded5e2965a57109ac606ad61b54d37e Mon Sep 17 00:00:00 2001
From: Yongtao Huang
Date: Thu, 21 Aug 2025 16:05:53 +0800
Subject: [PATCH 041/169] [Test] add unit tests for ProviderConfigEncrypter
encrypt/mask/decrypt (#24280)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
---
.../core/tools/utils/test_encryption.py | 181 ++++++++++++++++++
1 file changed, 181 insertions(+)
create mode 100644 api/tests/unit_tests/core/tools/utils/test_encryption.py
diff --git a/api/tests/unit_tests/core/tools/utils/test_encryption.py b/api/tests/unit_tests/core/tools/utils/test_encryption.py
new file mode 100644
index 0000000000..6425ab0b8d
--- /dev/null
+++ b/api/tests/unit_tests/core/tools/utils/test_encryption.py
@@ -0,0 +1,181 @@
+import copy
+from unittest.mock import patch
+
+import pytest
+
+from core.entities.provider_entities import BasicProviderConfig
+from core.tools.utils.encryption import ProviderConfigEncrypter
+
+
+# ---------------------------
+# A no-op cache
+# ---------------------------
+class NoopCache:
+ """Simple cache stub: always returns None, does nothing for set/delete."""
+
+ def get(self):
+ return None
+
+ def set(self, config):
+ pass
+
+ def delete(self):
+ pass
+
+
+@pytest.fixture
+def secret_field() -> BasicProviderConfig:
+ """A SECRET_INPUT field named 'password'."""
+ return BasicProviderConfig(
+ name="password",
+ type=BasicProviderConfig.Type.SECRET_INPUT,
+ )
+
+
+@pytest.fixture
+def normal_field() -> BasicProviderConfig:
+ """A TEXT_INPUT field named 'username'."""
+ return BasicProviderConfig(
+ name="username",
+ type=BasicProviderConfig.Type.TEXT_INPUT,
+ )
+
+
+@pytest.fixture
+def encrypter_obj(secret_field, normal_field):
+ """
+ Build ProviderConfigEncrypter with:
+ - tenant_id = tenant123
+ - one secret field (password) and one normal field (username)
+ - NoopCache as cache
+ """
+ return ProviderConfigEncrypter(
+ tenant_id="tenant123",
+ config=[secret_field, normal_field],
+ provider_config_cache=NoopCache(),
+ )
+
+
+# ============================================================
+# ProviderConfigEncrypter.encrypt()
+# ============================================================
+
+
+def test_encrypt_only_secret_is_encrypted_and_non_secret_unchanged(encrypter_obj):
+ """
+ Secret field should be encrypted, non-secret field unchanged.
+ Verify encrypt_token called only for secret field.
+ Also check deep copy (input not modified).
+ """
+ data_in = {"username": "alice", "password": "plain_pwd"}
+ data_copy = copy.deepcopy(data_in)
+
+ with patch("core.tools.utils.encryption.encrypter.encrypt_token", return_value="CIPHERTEXT") as mock_encrypt:
+ out = encrypter_obj.encrypt(data_in)
+
+ assert out["username"] == "alice"
+ assert out["password"] == "CIPHERTEXT"
+ mock_encrypt.assert_called_once_with("tenant123", "plain_pwd")
+ assert data_in == data_copy # deep copy semantics
+
+
+def test_encrypt_missing_secret_key_is_ok(encrypter_obj):
+ """If secret field missing in input, no error and no encryption called."""
+ with patch("core.tools.utils.encryption.encrypter.encrypt_token") as mock_encrypt:
+ out = encrypter_obj.encrypt({"username": "alice"})
+ assert out["username"] == "alice"
+ mock_encrypt.assert_not_called()
+
+
+# ============================================================
+# ProviderConfigEncrypter.mask_tool_credentials()
+# ============================================================
+
+
+@pytest.mark.parametrize(
+ ("raw", "prefix", "suffix"),
+ [
+ ("longsecret", "lo", "et"),
+ ("abcdefg", "ab", "fg"),
+ ("1234567", "12", "67"),
+ ],
+)
+def test_mask_tool_credentials_long_secret(encrypter_obj, raw, prefix, suffix):
+ """
+ For length > 6: keep first 2 and last 2, mask middle with '*'.
+ """
+ data_in = {"username": "alice", "password": raw}
+ data_copy = copy.deepcopy(data_in)
+
+ out = encrypter_obj.mask_tool_credentials(data_in)
+ masked = out["password"]
+
+ assert masked.startswith(prefix)
+ assert masked.endswith(suffix)
+ assert "*" in masked
+ assert len(masked) == len(raw)
+ assert data_in == data_copy # deep copy semantics
+
+
+@pytest.mark.parametrize("raw", ["", "1", "12", "123", "123456"])
+def test_mask_tool_credentials_short_secret(encrypter_obj, raw):
+ """
+ For length <= 6: fully mask with '*' of same length.
+ """
+ out = encrypter_obj.mask_tool_credentials({"password": raw})
+ assert out["password"] == ("*" * len(raw))
+
+
+def test_mask_tool_credentials_missing_key_noop(encrypter_obj):
+ """If secret key missing, leave other fields unchanged."""
+ data_in = {"username": "alice"}
+ data_copy = copy.deepcopy(data_in)
+
+ out = encrypter_obj.mask_tool_credentials(data_in)
+ assert out["username"] == "alice"
+ assert data_in == data_copy
+
+
+# ============================================================
+# ProviderConfigEncrypter.decrypt()
+# ============================================================
+
+
+def test_decrypt_normal_flow(encrypter_obj):
+ """
+ Normal decrypt flow:
+ - decrypt_token called for secret field
+ - secret replaced with decrypted value
+ - non-secret unchanged
+ """
+ data_in = {"username": "alice", "password": "ENC"}
+ data_copy = copy.deepcopy(data_in)
+
+ with patch("core.tools.utils.encryption.encrypter.decrypt_token", return_value="PLAIN") as mock_decrypt:
+ out = encrypter_obj.decrypt(data_in)
+
+ assert out["username"] == "alice"
+ assert out["password"] == "PLAIN"
+ mock_decrypt.assert_called_once_with("tenant123", "ENC")
+ assert data_in == data_copy # deep copy semantics
+
+
+@pytest.mark.parametrize("empty_val", ["", None])
+def test_decrypt_skip_empty_values(encrypter_obj, empty_val):
+ """Skip decrypt if value is empty or None, keep original."""
+ with patch("core.tools.utils.encryption.encrypter.decrypt_token") as mock_decrypt:
+ out = encrypter_obj.decrypt({"password": empty_val})
+
+ mock_decrypt.assert_not_called()
+ assert out["password"] == empty_val
+
+
+def test_decrypt_swallow_exception_and_keep_original(encrypter_obj):
+ """
+ If decrypt_token raises, exception should be swallowed,
+ and original value preserved.
+ """
+ with patch("core.tools.utils.encryption.encrypter.decrypt_token", side_effect=Exception("boom")):
+ out = encrypter_obj.decrypt({"password": "ENC_ERR"})
+
+ assert out["password"] == "ENC_ERR"
From 1abf1240b2de386749a87d2bb4c7b5a9d2193ab1 Mon Sep 17 00:00:00 2001
From: Guangdong Liu
Date: Thu, 21 Aug 2025 18:18:49 +0800
Subject: [PATCH 042/169] refactor: replace try-except blocks with
contextlib.suppress for cleaner exception handling (#24284)
---
api/controllers/console/wraps.py | 6 +++---
api/core/helper/trace_id_helper.py | 5 ++---
api/core/provider_manager.py | 9 +++-----
.../vdb/clickzetta/clickzetta_vector.py | 21 ++++++-------------
api/core/rag/extractor/pdf_extractor.py | 5 ++---
.../unstructured_eml_extractor.py | 5 ++---
api/core/tools/entities/tool_entities.py | 5 ++---
api/core/tools/tool_engine.py | 9 +++-----
api/core/tools/utils/configuration.py | 7 +++----
api/core/tools/utils/encryption.py | 5 ++---
.../parameter_extractor_node.py | 10 ++++-----
.../event_handlers/create_document_index.py | 18 ++++++++--------
api/extensions/ext_otel.py | 6 ++----
api/services/conversation_service.py | 5 ++---
.../vdb/clickzetta/test_clickzetta.py | 5 ++---
.../unit_tests/core/mcp/client/test_sse.py | 19 +++++------------
16 files changed, 52 insertions(+), 88 deletions(-)
diff --git a/api/controllers/console/wraps.py b/api/controllers/console/wraps.py
index d862dac373..d3fd1d52e5 100644
--- a/api/controllers/console/wraps.py
+++ b/api/controllers/console/wraps.py
@@ -1,3 +1,4 @@
+import contextlib
import json
import os
import time
@@ -178,7 +179,7 @@ def cloud_edition_billing_rate_limit_check(resource: str):
def cloud_utm_record(view):
@wraps(view)
def decorated(*args, **kwargs):
- try:
+ with contextlib.suppress(Exception):
features = FeatureService.get_features(current_user.current_tenant_id)
if features.billing.enabled:
@@ -187,8 +188,7 @@ def cloud_utm_record(view):
if utm_info:
utm_info_dict: dict = json.loads(utm_info)
OperationService.record_utm(current_user.current_tenant_id, utm_info_dict)
- except Exception as e:
- pass
+
return view(*args, **kwargs)
return decorated
diff --git a/api/core/helper/trace_id_helper.py b/api/core/helper/trace_id_helper.py
index df42837796..5cd0ea5c66 100644
--- a/api/core/helper/trace_id_helper.py
+++ b/api/core/helper/trace_id_helper.py
@@ -1,3 +1,4 @@
+import contextlib
import re
from collections.abc import Mapping
from typing import Any, Optional
@@ -97,10 +98,8 @@ def parse_traceparent_header(traceparent: str) -> Optional[str]:
Reference:
W3C Trace Context Specification: https://www.w3.org/TR/trace-context/
"""
- try:
+ with contextlib.suppress(Exception):
parts = traceparent.split("-")
if len(parts) == 4 and len(parts[1]) == 32:
return parts[1]
- except Exception:
- pass
return None
diff --git a/api/core/provider_manager.py b/api/core/provider_manager.py
index 9250497d29..39fec951bb 100644
--- a/api/core/provider_manager.py
+++ b/api/core/provider_manager.py
@@ -1,3 +1,4 @@
+import contextlib
import json
from collections import defaultdict
from json import JSONDecodeError
@@ -624,14 +625,12 @@ class ProviderManager:
for variable in provider_credential_secret_variables:
if variable in provider_credentials:
- try:
+ with contextlib.suppress(ValueError):
provider_credentials[variable] = encrypter.decrypt_token_with_decoding(
provider_credentials.get(variable) or "", # type: ignore
self.decoding_rsa_key,
self.decoding_cipher_rsa,
)
- except ValueError:
- pass
# cache provider credentials
provider_credentials_cache.set(credentials=provider_credentials)
@@ -672,14 +671,12 @@ class ProviderManager:
for variable in model_credential_secret_variables:
if variable in provider_model_credentials:
- try:
+ with contextlib.suppress(ValueError):
provider_model_credentials[variable] = encrypter.decrypt_token_with_decoding(
provider_model_credentials.get(variable),
self.decoding_rsa_key,
self.decoding_cipher_rsa,
)
- except ValueError:
- pass
# cache provider model credentials
provider_model_credentials_cache.set(credentials=provider_model_credentials)
diff --git a/api/core/rag/datasource/vdb/clickzetta/clickzetta_vector.py b/api/core/rag/datasource/vdb/clickzetta/clickzetta_vector.py
index 1059b855a2..6e8077ffd9 100644
--- a/api/core/rag/datasource/vdb/clickzetta/clickzetta_vector.py
+++ b/api/core/rag/datasource/vdb/clickzetta/clickzetta_vector.py
@@ -1,3 +1,4 @@
+import contextlib
import json
import logging
import queue
@@ -214,10 +215,8 @@ class ClickzettaConnectionPool:
return connection
else:
# Connection expired or invalid, close it
- try:
+ with contextlib.suppress(Exception):
connection.close()
- except Exception:
- pass
# No valid connection found, create new one
return self._create_connection(config)
@@ -228,10 +227,8 @@ class ClickzettaConnectionPool:
if config_key not in self._pool_locks:
# Pool was cleaned up, just close the connection
- try:
+ with contextlib.suppress(Exception):
connection.close()
- except Exception:
- pass
return
with self._pool_locks[config_key]:
@@ -243,10 +240,8 @@ class ClickzettaConnectionPool:
logger.debug("Returned ClickZetta connection to pool")
else:
# Pool full or connection invalid, close it
- try:
+ with contextlib.suppress(Exception):
connection.close()
- except Exception:
- pass
def _cleanup_expired_connections(self) -> None:
"""Clean up expired connections from all pools."""
@@ -265,10 +260,8 @@ class ClickzettaConnectionPool:
if current_time - last_used < self._connection_timeout:
valid_connections.append((connection, last_used))
else:
- try:
+ with contextlib.suppress(Exception):
connection.close()
- except Exception:
- pass
self._pools[config_key] = valid_connections
@@ -299,10 +292,8 @@ class ClickzettaConnectionPool:
with self._pool_locks[config_key]:
pool = self._pools[config_key]
for connection, _ in pool:
- try:
+ with contextlib.suppress(Exception):
connection.close()
- except Exception:
- pass
pool.clear()
diff --git a/api/core/rag/extractor/pdf_extractor.py b/api/core/rag/extractor/pdf_extractor.py
index 04033dec3f..7dfe2e357c 100644
--- a/api/core/rag/extractor/pdf_extractor.py
+++ b/api/core/rag/extractor/pdf_extractor.py
@@ -1,5 +1,6 @@
"""Abstract interface for document loader implementations."""
+import contextlib
from collections.abc import Iterator
from typing import Optional, cast
@@ -25,12 +26,10 @@ class PdfExtractor(BaseExtractor):
def extract(self) -> list[Document]:
plaintext_file_exists = False
if self._file_cache_key:
- try:
+ with contextlib.suppress(FileNotFoundError):
text = cast(bytes, storage.load(self._file_cache_key)).decode("utf-8")
plaintext_file_exists = True
return [Document(page_content=text)]
- except FileNotFoundError:
- pass
documents = list(self.load())
text_list = []
for document in documents:
diff --git a/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py b/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py
index f1fa5dde5c..856a9bce18 100644
--- a/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py
+++ b/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py
@@ -1,4 +1,5 @@
import base64
+import contextlib
import logging
from typing import Optional
@@ -33,7 +34,7 @@ class UnstructuredEmailExtractor(BaseExtractor):
elements = partition_email(filename=self._file_path)
# noinspection PyBroadException
- try:
+ with contextlib.suppress(Exception):
for element in elements:
element_text = element.text.strip()
@@ -43,8 +44,6 @@ class UnstructuredEmailExtractor(BaseExtractor):
element_decode = base64.b64decode(element_text)
soup = BeautifulSoup(element_decode.decode("utf-8"), "html.parser")
element.text = soup.get_text()
- except Exception:
- pass
from unstructured.chunking.title import chunk_by_title
diff --git a/api/core/tools/entities/tool_entities.py b/api/core/tools/entities/tool_entities.py
index 5ffba07b44..df599a09a3 100644
--- a/api/core/tools/entities/tool_entities.py
+++ b/api/core/tools/entities/tool_entities.py
@@ -1,4 +1,5 @@
import base64
+import contextlib
import enum
from collections.abc import Mapping
from enum import Enum
@@ -227,10 +228,8 @@ class ToolInvokeMessage(BaseModel):
@classmethod
def decode_blob_message(cls, v):
if isinstance(v, dict) and "blob" in v:
- try:
+ with contextlib.suppress(Exception):
v["blob"] = base64.b64decode(v["blob"])
- except Exception:
- pass
return v
@field_serializer("message")
diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py
index 83444c02d8..10db4d9503 100644
--- a/api/core/tools/tool_engine.py
+++ b/api/core/tools/tool_engine.py
@@ -1,3 +1,4 @@
+import contextlib
import json
from collections.abc import Generator, Iterable
from copy import deepcopy
@@ -69,10 +70,8 @@ class ToolEngine:
if parameters and len(parameters) == 1:
tool_parameters = {parameters[0].name: tool_parameters}
else:
- try:
+ with contextlib.suppress(Exception):
tool_parameters = json.loads(tool_parameters)
- except Exception:
- pass
if not isinstance(tool_parameters, dict):
raise ValueError(f"tool_parameters should be a dict, but got a string: {tool_parameters}")
@@ -270,14 +269,12 @@ class ToolEngine:
if response.meta.get("mime_type"):
mimetype = response.meta.get("mime_type")
else:
- try:
+ with contextlib.suppress(Exception):
url = URL(cast(ToolInvokeMessage.TextMessage, response.message).text)
extension = url.suffix
guess_type_result, _ = guess_type(f"a{extension}")
if guess_type_result:
mimetype = guess_type_result
- except Exception:
- pass
if not mimetype:
mimetype = "image/jpeg"
diff --git a/api/core/tools/utils/configuration.py b/api/core/tools/utils/configuration.py
index aceba6e69f..3a9391dbb1 100644
--- a/api/core/tools/utils/configuration.py
+++ b/api/core/tools/utils/configuration.py
@@ -1,3 +1,4 @@
+import contextlib
from copy import deepcopy
from typing import Any
@@ -137,11 +138,9 @@ class ToolParameterConfigurationManager:
and parameter.type == ToolParameter.ToolParameterType.SECRET_INPUT
):
if parameter.name in parameters:
- try:
- has_secret_input = True
+ has_secret_input = True
+ with contextlib.suppress(Exception):
parameters[parameter.name] = encrypter.decrypt_token(self.tenant_id, parameters[parameter.name])
- except Exception:
- pass
if has_secret_input:
cache.set(parameters)
diff --git a/api/core/tools/utils/encryption.py b/api/core/tools/utils/encryption.py
index 5fdfd3b9d1..d771293e11 100644
--- a/api/core/tools/utils/encryption.py
+++ b/api/core/tools/utils/encryption.py
@@ -1,3 +1,4 @@
+import contextlib
from copy import deepcopy
from typing import Any, Optional, Protocol
@@ -111,14 +112,12 @@ class ProviderConfigEncrypter:
for field_name, field in fields.items():
if field.type == BasicProviderConfig.Type.SECRET_INPUT:
if field_name in data:
- try:
+ with contextlib.suppress(Exception):
# if the value is None or empty string, skip decrypt
if not data[field_name]:
continue
data[field_name] = encrypter.decrypt_token(self.tenant_id, data[field_name])
- except Exception:
- pass
self.provider_config_cache.set(data)
return data
diff --git a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py
index 45c5e0a62c..49c4c142e1 100644
--- a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py
+++ b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py
@@ -1,3 +1,4 @@
+import contextlib
import json
import logging
import uuid
@@ -666,10 +667,8 @@ class ParameterExtractorNode(BaseNode):
if result[idx] == "{" or result[idx] == "[":
json_str = extract_json(result[idx:])
if json_str:
- try:
+ with contextlib.suppress(Exception):
return cast(dict, json.loads(json_str))
- except Exception:
- pass
logger.info("extra error: %s", result)
return None
@@ -686,10 +685,9 @@ class ParameterExtractorNode(BaseNode):
if result[idx] == "{" or result[idx] == "[":
json_str = extract_json(result[idx:])
if json_str:
- try:
+ with contextlib.suppress(Exception):
return cast(dict, json.loads(json_str))
- except Exception:
- pass
+
logger.info("extra error: %s", result)
return None
diff --git a/api/events/event_handlers/create_document_index.py b/api/events/event_handlers/create_document_index.py
index c607161e2a..1b0321f42e 100644
--- a/api/events/event_handlers/create_document_index.py
+++ b/api/events/event_handlers/create_document_index.py
@@ -1,3 +1,4 @@
+import contextlib
import logging
import time
@@ -38,12 +39,11 @@ def handle(sender, **kwargs):
db.session.add(document)
db.session.commit()
- try:
- indexing_runner = IndexingRunner()
- indexing_runner.run(documents)
- end_at = time.perf_counter()
- logging.info(click.style(f"Processed dataset: {dataset_id} latency: {end_at - start_at}", fg="green"))
- except DocumentIsPausedError as ex:
- logging.info(click.style(str(ex), fg="yellow"))
- except Exception:
- pass
+ with contextlib.suppress(Exception):
+ try:
+ indexing_runner = IndexingRunner()
+ indexing_runner.run(documents)
+ end_at = time.perf_counter()
+ logging.info(click.style(f"Processed dataset: {dataset_id} latency: {end_at - start_at}", fg="green"))
+ except DocumentIsPausedError as ex:
+ logging.info(click.style(str(ex), fg="yellow"))
diff --git a/api/extensions/ext_otel.py b/api/extensions/ext_otel.py
index a8f025a750..3fd9633e79 100644
--- a/api/extensions/ext_otel.py
+++ b/api/extensions/ext_otel.py
@@ -1,4 +1,5 @@
import atexit
+import contextlib
import logging
import os
import platform
@@ -106,7 +107,7 @@ def init_app(app: DifyApp):
"""Custom logging handler that creates spans for logging.exception() calls"""
def emit(self, record: logging.LogRecord):
- try:
+ with contextlib.suppress(Exception):
if record.exc_info:
tracer = get_tracer_provider().get_tracer("dify.exception.logging")
with tracer.start_as_current_span(
@@ -126,9 +127,6 @@ def init_app(app: DifyApp):
if record.exc_info[0]:
span.set_attribute("exception.type", record.exc_info[0].__name__)
- except Exception:
- pass
-
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter as GRPCMetricExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter as GRPCSpanExporter
diff --git a/api/services/conversation_service.py b/api/services/conversation_service.py
index 4f3dd3c762..712ef4c601 100644
--- a/api/services/conversation_service.py
+++ b/api/services/conversation_service.py
@@ -1,3 +1,4 @@
+import contextlib
from collections.abc import Callable, Sequence
from typing import Any, Optional, Union
@@ -142,13 +143,11 @@ class ConversationService:
raise MessageNotExistsError()
# generate conversation name
- try:
+ with contextlib.suppress(Exception):
name = LLMGenerator.generate_conversation_name(
app_model.tenant_id, message.query, conversation.id, app_model.id
)
conversation.name = name
- except Exception:
- pass
db.session.commit()
diff --git a/api/tests/integration_tests/vdb/clickzetta/test_clickzetta.py b/api/tests/integration_tests/vdb/clickzetta/test_clickzetta.py
index 8b57132772..21de8be6e3 100644
--- a/api/tests/integration_tests/vdb/clickzetta/test_clickzetta.py
+++ b/api/tests/integration_tests/vdb/clickzetta/test_clickzetta.py
@@ -1,3 +1,4 @@
+import contextlib
import os
import pytest
@@ -44,10 +45,8 @@ class TestClickzettaVector(AbstractVectorTest):
yield vector
# Cleanup: delete the test collection
- try:
+ with contextlib.suppress(Exception):
vector.delete()
- except Exception:
- pass
def test_clickzetta_vector_basic_operations(self, vector_store):
"""Test basic CRUD operations on Clickzetta vector store."""
diff --git a/api/tests/unit_tests/core/mcp/client/test_sse.py b/api/tests/unit_tests/core/mcp/client/test_sse.py
index 880a0d4940..aadd366762 100644
--- a/api/tests/unit_tests/core/mcp/client/test_sse.py
+++ b/api/tests/unit_tests/core/mcp/client/test_sse.py
@@ -1,3 +1,4 @@
+import contextlib
import json
import queue
import threading
@@ -124,13 +125,10 @@ def test_sse_client_connection_validation():
mock_event_source.iter_sse.return_value = [endpoint_event]
# Test connection
- try:
+ with contextlib.suppress(Exception):
with sse_client(test_url) as (read_queue, write_queue):
assert read_queue is not None
assert write_queue is not None
- except Exception as e:
- # Connection might fail due to mocking, but we're testing the validation logic
- pass
def test_sse_client_error_handling():
@@ -178,7 +176,7 @@ def test_sse_client_timeout_configuration():
mock_event_source.iter_sse.return_value = []
mock_sse_connect.return_value.__enter__.return_value = mock_event_source
- try:
+ with contextlib.suppress(Exception):
with sse_client(
test_url, headers=custom_headers, timeout=custom_timeout, sse_read_timeout=custom_sse_timeout
) as (read_queue, write_queue):
@@ -190,9 +188,6 @@ def test_sse_client_timeout_configuration():
assert call_args is not None
timeout_arg = call_args[1]["timeout"]
assert timeout_arg.read == custom_sse_timeout
- except Exception:
- # Connection might fail due to mocking, but we tested the configuration
- pass
def test_sse_transport_endpoint_validation():
@@ -251,12 +246,10 @@ def test_sse_client_queue_cleanup():
# Mock connection that raises an exception
mock_sse_connect.side_effect = Exception("Connection failed")
- try:
+ with contextlib.suppress(Exception):
with sse_client(test_url) as (rq, wq):
read_queue = rq
write_queue = wq
- except Exception:
- pass # Expected to fail
# Queues should be cleaned up even on exception
# Note: In real implementation, cleanup should put None to signal shutdown
@@ -283,11 +276,9 @@ def test_sse_client_headers_propagation():
mock_event_source.iter_sse.return_value = []
mock_sse_connect.return_value.__enter__.return_value = mock_event_source
- try:
+ with contextlib.suppress(Exception):
with sse_client(test_url, headers=custom_headers):
pass
- except Exception:
- pass # Expected due to mocking
# Verify headers were passed to client factory
mock_client_factory.assert_called_with(headers=custom_headers)
From 0c595c47459f14eb4751be35a21c467ceae856a4 Mon Sep 17 00:00:00 2001
From: Charles Lee <114982593+rookie-orange@users.noreply.github.com>
Date: Thu, 21 Aug 2025 21:38:40 +0800
Subject: [PATCH 043/169] style: replace `h-[1px]` with `h-px` to unify the
writing format of Tailwind CSS (#24146)
---
web/app/components/app-sidebar/index.tsx | 2 +-
.../annotation/edit-annotation-modal/edit-item/index.tsx | 2 +-
.../app/configuration/base/group-name/index.tsx | 2 +-
.../app/configuration/config-var/select-var-type.tsx | 2 +-
.../configuration/config/assistant-type-picker/index.tsx | 2 +-
web/app/components/app/log/list.tsx | 2 +-
web/app/components/base/agent-log-modal/iteration.tsx | 2 +-
web/app/components/base/chat/chat/citation/index.tsx | 2 +-
web/app/components/base/chat/chat/citation/popup.tsx | 2 +-
web/app/components/base/dropdown/index.tsx | 2 +-
.../annotation-reply/score-slider/index.tsx | 2 +-
.../base/file-uploader/file-from-link-or-local/index.tsx | 4 ++--
.../base/image-uploader/chat-image-uploader.tsx | 4 ++--
web/app/components/base/tag-management/selector.tsx | 4 ++--
.../datasets/create/embedding-process/index.tsx | 4 +---
web/app/components/datasets/create/step-three/index.tsx | 2 +-
.../documents/detail/completed/child-segment-list.tsx | 2 +-
.../datasets/documents/detail/completed/index.tsx | 6 +-----
.../account-setting/api-based-extension-page/selector.tsx | 2 +-
.../model-provider-page/model-parameter-modal/index.tsx | 2 +-
.../plugins/marketplace/intersection-line/index.tsx | 2 +-
.../components/plugins/plugin-auth/authorized/index.tsx | 2 +-
.../plugins/plugin-detail-panel/model-selector/index.tsx | 2 +-
.../components/tools/workflow-tool/configure-button.tsx | 2 +-
web/app/components/workflow/help-line/index.tsx | 2 +-
.../components/panel-operator/panel-operator-popup.tsx | 8 ++++----
.../workflow/nodes/if-else/components/condition-wrap.tsx | 2 +-
web/app/components/workflow/nodes/if-else/panel.tsx | 2 +-
.../workflow/note-node/note-editor/toolbar/operator.tsx | 4 ++--
.../debug-and-preview/conversation-variable-modal.tsx | 2 +-
.../panel/version-history-panel/context-menu/index.tsx | 2 +-
.../workflow/panel/version-history-panel/filter/index.tsx | 2 +-
web/app/components/workflow/selection-contextmenu.tsx | 2 +-
web/app/education-apply/education-apply-page.tsx | 2 +-
34 files changed, 41 insertions(+), 47 deletions(-)
diff --git a/web/app/components/app-sidebar/index.tsx b/web/app/components/app-sidebar/index.tsx
index cf32339b8a..c3ff45d6a6 100644
--- a/web/app/components/app-sidebar/index.tsx
+++ b/web/app/components/app-sidebar/index.tsx
@@ -107,7 +107,7 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
)}
|