From eb3b5f751a573e27f76166ebe4ad3048804469e8 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:08:11 +0000 Subject: [PATCH 1/3] [autofix.ci] apply automated fixes --- api/AGENTS.md | 76 ++++++++++--------- api/agent_skills/coding_style.md | 5 ++ api/agent_skills/trigger.md | 6 +- api/core/app/apps/workflow/app_generator.py | 20 ++--- api/core/app/apps/workflow/app_runner.py | 2 +- api/core/app/apps/workflow_app_runner.py | 2 +- api/core/entities/provider_entities.py | 14 ++-- api/core/helper/provider_encryption.py | 2 +- api/core/plugin/entities/plugin.py | 2 +- api/core/plugin/entities/request.py | 2 +- api/core/trigger/entities/api_entities.py | 12 +-- api/core/trigger/entities/entities.py | 26 +++---- .../workflow/nodes/trigger_plugin/entities.py | 2 +- .../trigger_plugin/trigger_event_node.py | 4 +- .../nodes/trigger_schedule/entities.py | 20 ++--- .../trigger_schedule/trigger_schedule_node.py | 4 +- .../nodes/trigger_webhook/entities.py | 2 +- .../workflow/nodes/trigger_webhook/node.py | 4 +- ...nc_workflow_schedule_when_app_published.py | 2 +- api/libs/schedule_utils.py | 2 +- api/models/trigger.py | 22 +++--- ...alchemy_workflow_trigger_log_repository.py | 2 +- .../workflow_trigger_log_repository.py | 2 +- api/services/app_generate_service.py | 2 +- api/services/async_workflow_service.py | 4 +- api/services/trigger/schedule_service.py | 2 +- .../trigger/trigger_provider_service.py | 8 +- api/services/trigger/webhook_service.py | 2 +- api/services/workflow/entities.py | 28 +++---- .../components/workflow-children.tsx | 8 +- .../_base/components/form-input-item.tsx | 2 +- .../nodes/trigger-plugin/use-config.ts | 2 +- 32 files changed, 151 insertions(+), 142 deletions(-) diff --git a/api/AGENTS.md b/api/AGENTS.md index 6fa97cdea4..17398ec4b8 100644 --- a/api/AGENTS.md +++ b/api/AGENTS.md @@ -2,57 +2,61 @@ Start with the section that best matches your need. Each entry lists the problems it solves plus key files/concepts so you know what to expect before opening it. ---- +______________________________________________________________________ ## Platform Foundations -- **[Infrastructure Overview](agent_skills/infra.md)** - When to read this: - - You need to understand where a feature belongs in the architecture. - - You’re wiring storage, Redis, vector stores, or OTEL. - - You’re about to add CLI commands or async jobs. - What it covers: configuration stack (`configs/app_config.py`, remote settings), storage entry points (`extensions/ext_storage.py`, `core/file/file_manager.py`), Redis conventions (`extensions/ext_redis.py`), plugin runtime topology, vector-store factory (`core/rag/datasource/vdb/*`), observability hooks, SSRF proxy usage, and core CLI commands. +- **[Infrastructure Overview](agent_skills/infra.md)**\ + When to read this: -- **[Coding Style](agent_skills/coding_style.md)** - When to read this: - - You’re writing or reviewing backend code and need the authoritative checklist. - - You’re unsure about Pydantic validators, SQLAlchemy session usage, or logging patterns. - - You want the exact lint/type/test commands used in PRs. - Includes: Ruff & BasedPyright commands, no-annotation policy, session examples (`with Session(db.engine, ...)`), `@field_validator` usage, logging expectations, and the rule set for file size, helpers, and package management. + - You need to understand where a feature belongs in the architecture. + - You’re wiring storage, Redis, vector stores, or OTEL. + - You’re about to add CLI commands or async jobs.\ + What it covers: configuration stack (`configs/app_config.py`, remote settings), storage entry points (`extensions/ext_storage.py`, `core/file/file_manager.py`), Redis conventions (`extensions/ext_redis.py`), plugin runtime topology, vector-store factory (`core/rag/datasource/vdb/*`), observability hooks, SSRF proxy usage, and core CLI commands. ---- +- **[Coding Style](agent_skills/coding_style.md)**\ + When to read this: + + - You’re writing or reviewing backend code and need the authoritative checklist. + - You’re unsure about Pydantic validators, SQLAlchemy session usage, or logging patterns. + - You want the exact lint/type/test commands used in PRs.\ + Includes: Ruff & BasedPyright commands, no-annotation policy, session examples (`with Session(db.engine, ...)`), `@field_validator` usage, logging expectations, and the rule set for file size, helpers, and package management. + +______________________________________________________________________ ## Plugin & Extension Development -- **[Plugin Systems](agent_skills/plugin.md)** - When to read this: - - You’re building or debugging a marketplace plugin. - - You need to know how manifests, providers, daemons, and migrations fit together. - What it covers: plugin manifests (`core/plugin/entities/plugin.py`), installation/upgrade flows (`services/plugin/plugin_service.py`, CLI commands), runtime adapters (`core/plugin/impl/*` for tool/model/datasource/trigger/endpoint/agent), daemon coordination (`core/plugin/entities/plugin_daemon.py`), and how provider registries surface capabilities to the rest of the platform. +- **[Plugin Systems](agent_skills/plugin.md)**\ + When to read this: -- **[Plugin OAuth](agent_skills/plugin_oauth.md)** - When to read this: - - You must integrate OAuth for a plugin or datasource. - - You’re handling credential encryption or refresh flows. - Topics: credential storage, encryption helpers (`core/helper/provider_encryption.py`), OAuth client bootstrap (`services/plugin/oauth_service.py`, `services/plugin/plugin_parameter_service.py`), and how console/API layers expose the flows. + - You’re building or debugging a marketplace plugin. + - You need to know how manifests, providers, daemons, and migrations fit together.\ + What it covers: plugin manifests (`core/plugin/entities/plugin.py`), installation/upgrade flows (`services/plugin/plugin_service.py`, CLI commands), runtime adapters (`core/plugin/impl/*` for tool/model/datasource/trigger/endpoint/agent), daemon coordination (`core/plugin/entities/plugin_daemon.py`), and how provider registries surface capabilities to the rest of the platform. ---- +- **[Plugin OAuth](agent_skills/plugin_oauth.md)**\ + When to read this: + + - You must integrate OAuth for a plugin or datasource. + - You’re handling credential encryption or refresh flows.\ + Topics: credential storage, encryption helpers (`core/helper/provider_encryption.py`), OAuth client bootstrap (`services/plugin/oauth_service.py`, `services/plugin/plugin_parameter_service.py`), and how console/API layers expose the flows. + +______________________________________________________________________ ## Workflow Entry & Execution -- **[Trigger Concepts](agent_skills/trigger.md)** - When to read this: - - You’re debugging why a workflow didn’t start. - - You’re adding a new trigger type or hook. - - You need to trace async execution, draft debugging, or webhook/schedule pipelines. - Details: Start-node taxonomy, webhook & schedule internals (`core/workflow/nodes/trigger_*`, `services/trigger/*`), async orchestration (`services/async_workflow_service.py`, Celery queues), debug event bus, and storage/logging interactions. +- **[Trigger Concepts](agent_skills/trigger.md)**\ + When to read this: + - You’re debugging why a workflow didn’t start. + - You’re adding a new trigger type or hook. + - You need to trace async execution, draft debugging, or webhook/schedule pipelines.\ + Details: Start-node taxonomy, webhook & schedule internals (`core/workflow/nodes/trigger_*`, `services/trigger/*`), async orchestration (`services/async_workflow_service.py`, Celery queues), debug event bus, and storage/logging interactions. ---- +______________________________________________________________________ ## Additional Notes for Agents -- All skill docs assume you follow the coding style guide—run Ruff/BasedPyright/tests listed there before submitting changes. -- When you cannot find an answer in these briefs, search the codebase using the paths referenced (e.g., `core/plugin/impl/tool.py`, `services/dataset_service.py`). -- If you run into cross-cutting concerns (tenancy, configuration, storage), check the infrastructure guide first; it links to most supporting modules. -- Keep multi-tenancy and configuration central: everything flows through `configs.dify_config` and `tenant_id`. +- All skill docs assume you follow the coding style guide—run Ruff/BasedPyright/tests listed there before submitting changes. +- When you cannot find an answer in these briefs, search the codebase using the paths referenced (e.g., `core/plugin/impl/tool.py`, `services/dataset_service.py`). +- If you run into cross-cutting concerns (tenancy, configuration, storage), check the infrastructure guide first; it links to most supporting modules. +- Keep multi-tenancy and configuration central: everything flows through `configs.dify_config` and `tenant_id`. - When touching plugins or triggers, consult both the system overview and the specialised doc to ensure you adjust lifecycle, storage, and observability consistently. diff --git a/api/agent_skills/coding_style.md b/api/agent_skills/coding_style.md index c2f161fa22..a2b66f0bd5 100644 --- a/api/agent_skills/coding_style.md +++ b/api/agent_skills/coding_style.md @@ -30,6 +30,7 @@ ## SQLAlchemy Patterns - Models inherit from `models.base.Base`; never create ad-hoc metadata or engines. + - Open sessions with context managers: ```python @@ -44,7 +45,9 @@ ``` - Use SQLAlchemy expressions; avoid raw SQL unless necessary. + - Introduce repository abstractions only for very large tables (e.g., workflow executions) to support alternative storage strategies. + - Always scope queries by `tenant_id` and protect write paths with safeguards (`FOR UPDATE`, row counts, etc.). ## Storage & External IO @@ -56,7 +59,9 @@ ## Pydantic Usage - Define DTOs with Pydantic v2 models and forbid extras by default. + - Use `@field_validator` / `@model_validator` for domain rules. + - Example: ```python diff --git a/api/agent_skills/trigger.md b/api/agent_skills/trigger.md index 1ff3f1f9e1..f4b076332c 100644 --- a/api/agent_skills/trigger.md +++ b/api/agent_skills/trigger.md @@ -14,8 +14,8 @@ Trigger is a collection of nodes that we called `Start` nodes, also, the concept Before `Trigger` concept is introduced, it's what we called `Start` node, but now, to avoid confusion, it was renamed to `UserInput` node, has a strong relation with `ServiceAPI` in `controllers/service_api/app` 1. `UserInput` node introduces a list of arguments that need to be provided by the user, finally it will be converted into variables in the workflow variable pool. -2. `ServiceAPI` accept those arguments, and pass through them into `UserInput` node. -3. For its detailed implementation, please refer to `core/workflow/nodes/start` +1. `ServiceAPI` accept those arguments, and pass through them into `UserInput` node. +1. For its detailed implementation, please refer to `core/workflow/nodes/start` ### Trigger Webhook @@ -34,7 +34,7 @@ To Achieve this, a `WorkflowSchedulePlan` model was introduced in `models/trigge `Trigger Plugin` node allows user define there own distributed trigger plugin, whenever a request was received, Dify forwards it to the plugin and wait for parsed variables from it. 1. Requests were saved in storage by `services/trigger/trigger_request_service.py`, referenced by `services/trigger/trigger_service.py`.`TriggerService`.`process_endpoint` -2. Plugins accept those requests and parse variables from it, see `core/plugin/impl/trigger.py` for details. +1. Plugins accept those requests and parse variables from it, see `core/plugin/impl/trigger.py` for details. A `subscription` concept was out here by Dify, it means an endpoint address from Dify was bound to thirdparty webhook service like `Github` `Slack` `Linear` `GoogleDrive` `Gmail` etc. Once a subscription was created, Dify continually receives requests from the platforms and handle them one by one. diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py index 46c03c061d..d564182edd 100644 --- a/api/core/app/apps/workflow/app_generator.py +++ b/api/core/app/apps/workflow/app_generator.py @@ -54,8 +54,8 @@ class WorkflowAppGenerator(BaseAppGenerator): invoke_from: InvokeFrom, streaming: Literal[True], call_depth: int, - triggered_from: Optional[WorkflowRunTriggeredFrom] = None, - root_node_id: Optional[str] = None, + triggered_from: WorkflowRunTriggeredFrom | None = None, + root_node_id: str | None = None, graph_engine_layers: Sequence[GraphEngineLayer] = (), ) -> Generator[Mapping[str, Any] | str, None, None]: ... @@ -70,8 +70,8 @@ class WorkflowAppGenerator(BaseAppGenerator): invoke_from: InvokeFrom, streaming: Literal[False], call_depth: int, - triggered_from: Optional[WorkflowRunTriggeredFrom] = None, - root_node_id: Optional[str] = None, + triggered_from: WorkflowRunTriggeredFrom | None = None, + root_node_id: str | None = None, graph_engine_layers: Sequence[GraphEngineLayer] = (), ) -> Mapping[str, Any]: ... @@ -86,8 +86,8 @@ class WorkflowAppGenerator(BaseAppGenerator): invoke_from: InvokeFrom, streaming: bool, call_depth: int, - triggered_from: Optional[WorkflowRunTriggeredFrom] = None, - root_node_id: Optional[str] = None, + triggered_from: WorkflowRunTriggeredFrom | None = None, + root_node_id: str | None = None, graph_engine_layers: Sequence[GraphEngineLayer] = (), ) -> Union[Mapping[str, Any], Generator[Mapping[str, Any] | str, None, None]]: ... @@ -101,8 +101,8 @@ class WorkflowAppGenerator(BaseAppGenerator): invoke_from: InvokeFrom, streaming: bool = True, call_depth: int = 0, - triggered_from: Optional[WorkflowRunTriggeredFrom] = None, - root_node_id: Optional[str] = None, + triggered_from: WorkflowRunTriggeredFrom | None = None, + root_node_id: str | None = None, graph_engine_layers: Sequence[GraphEngineLayer] = (), ) -> Union[Mapping[str, Any], Generator[Mapping[str, Any] | str, None, None]]: files: Sequence[Mapping[str, Any]] = args.get("files") or [] @@ -223,7 +223,7 @@ class WorkflowAppGenerator(BaseAppGenerator): workflow_node_execution_repository: WorkflowNodeExecutionRepository, streaming: bool = True, variable_loader: VariableLoader = DUMMY_VARIABLE_LOADER, - root_node_id: Optional[str] = None, + root_node_id: str | None = None, graph_engine_layers: Sequence[GraphEngineLayer] = (), ) -> Union[Mapping[str, Any], Generator[str | Mapping[str, Any], None, None]]: """ @@ -457,7 +457,7 @@ class WorkflowAppGenerator(BaseAppGenerator): variable_loader: VariableLoader, workflow_execution_repository: WorkflowExecutionRepository, workflow_node_execution_repository: WorkflowNodeExecutionRepository, - root_node_id: Optional[str] = None, + root_node_id: str | None = None, graph_engine_layers: Sequence[GraphEngineLayer] = (), ) -> None: """ diff --git a/api/core/app/apps/workflow/app_runner.py b/api/core/app/apps/workflow/app_runner.py index 707d88cb60..328daa9b91 100644 --- a/api/core/app/apps/workflow/app_runner.py +++ b/api/core/app/apps/workflow/app_runner.py @@ -38,7 +38,7 @@ class WorkflowAppRunner(WorkflowBasedAppRunner): variable_loader: VariableLoader, workflow: Workflow, system_user_id: str, - root_node_id: Optional[str] = None, + root_node_id: str | None = None, workflow_execution_repository: WorkflowExecutionRepository, workflow_node_execution_repository: WorkflowNodeExecutionRepository, graph_engine_layers: Sequence[GraphEngineLayer] = (), diff --git a/api/core/app/apps/workflow_app_runner.py b/api/core/app/apps/workflow_app_runner.py index 38cb5e70ff..d67451d2c8 100644 --- a/api/core/app/apps/workflow_app_runner.py +++ b/api/core/app/apps/workflow_app_runner.py @@ -84,7 +84,7 @@ class WorkflowBasedAppRunner: workflow_id: str = "", tenant_id: str = "", user_id: str = "", - root_node_id: Optional[str] = None, + root_node_id: str | None = None, ) -> Graph: """ Init graph diff --git a/api/core/entities/provider_entities.py b/api/core/entities/provider_entities.py index deac8847e6..20e273232e 100644 --- a/api/core/entities/provider_entities.py +++ b/api/core/entities/provider_entities.py @@ -84,9 +84,9 @@ class SystemConfiguration(BaseModel): """ enabled: bool - current_quota_type: Optional[ProviderQuotaType] = None + current_quota_type: ProviderQuotaType | None = None quota_configurations: list[QuotaConfiguration] = [] - credentials: Optional[dict] = None + credentials: dict | None = None class CustomProviderConfiguration(BaseModel): @@ -95,8 +95,8 @@ class CustomProviderConfiguration(BaseModel): """ credentials: dict - current_credential_id: Optional[str] = None - current_credential_name: Optional[str] = None + current_credential_id: str | None = None + current_credential_name: str | None = None available_credentials: list[CredentialConfiguration] = [] @@ -108,8 +108,8 @@ class CustomModelConfiguration(BaseModel): model: str model_type: ModelType credentials: dict | None - current_credential_id: Optional[str] = None - current_credential_name: Optional[str] = None + current_credential_id: str | None = None + current_credential_name: str | None = None available_model_credentials: list[CredentialConfiguration] = [] unadded_to_model_list: bool | None = False @@ -131,7 +131,7 @@ class CustomConfiguration(BaseModel): Model class for provider custom configuration. """ - provider: Optional[CustomProviderConfiguration] = None + provider: CustomProviderConfiguration | None = None models: list[CustomModelConfiguration] = [] can_added_models: list[UnaddedModelConfiguration] = [] diff --git a/api/core/helper/provider_encryption.py b/api/core/helper/provider_encryption.py index 4f3ed75d86..58817d2b1c 100644 --- a/api/core/helper/provider_encryption.py +++ b/api/core/helper/provider_encryption.py @@ -12,7 +12,7 @@ class ProviderConfigCache(Protocol): Interface for provider configuration cache operations """ - def get(self) -> Optional[dict[str, Any]]: + def get(self) -> dict[str, Any] | None: """Get cached provider configuration""" ... diff --git a/api/core/plugin/entities/plugin.py b/api/core/plugin/entities/plugin.py index 1b521d0ff7..30af4d8a5e 100644 --- a/api/core/plugin/entities/plugin.py +++ b/api/core/plugin/entities/plugin.py @@ -73,7 +73,7 @@ class PluginDeclaration(BaseModel): models: list[str] | None = Field(default_factory=list[str]) endpoints: list[str] | None = Field(default_factory=list[str]) datasources: list[str] | None = Field(default_factory=list[str]) - triggers: Optional[list[str]] = Field(default_factory=list[str]) + triggers: list[str] | None = Field(default_factory=list[str]) class Meta(BaseModel): minimum_dify_version: str | None = Field(default=None) diff --git a/api/core/plugin/entities/request.py b/api/core/plugin/entities/request.py index 995d52ac6e..1ce449cfa6 100644 --- a/api/core/plugin/entities/request.py +++ b/api/core/plugin/entities/request.py @@ -246,7 +246,7 @@ class RequestFetchAppInfo(BaseModel): class TriggerInvokeEventResponse(BaseModel): variables: Mapping[str, Any] = Field(default_factory=dict) - cancelled: Optional[bool] = False + cancelled: bool | None = False model_config = ConfigDict(protected_namespaces=(), arbitrary_types_allowed=True) diff --git a/api/core/trigger/entities/api_entities.py b/api/core/trigger/entities/api_entities.py index 7e77e89932..6f950f2820 100644 --- a/api/core/trigger/entities/api_entities.py +++ b/api/core/trigger/entities/api_entities.py @@ -31,7 +31,7 @@ class EventApiEntity(BaseModel): identity: EventIdentity = Field(description="The identity of the trigger") description: I18nObject = Field(description="The description of the trigger") parameters: list[EventParameter] = Field(description="The parameters of the trigger") - output_schema: Optional[Mapping[str, Any]] = Field(description="The output schema of the trigger") + output_schema: Mapping[str, Any] | None = Field(description="The output schema of the trigger") class TriggerProviderApiEntity(BaseModel): @@ -39,19 +39,19 @@ class TriggerProviderApiEntity(BaseModel): name: str = Field(..., description="The name of the trigger provider") label: I18nObject = Field(..., description="The label of the trigger provider") description: I18nObject = Field(..., description="The description of the trigger provider") - icon: Optional[str] = Field(default=None, description="The icon of the trigger provider") - icon_dark: Optional[str] = Field(default=None, description="The dark icon of the trigger provider") + icon: str | None = Field(default=None, description="The icon of the trigger provider") + icon_dark: str | None = Field(default=None, description="The dark icon of the trigger provider") tags: list[str] = Field(default_factory=list, description="The tags of the trigger provider") - plugin_id: Optional[str] = Field(default="", description="The plugin id of the tool") - plugin_unique_identifier: Optional[str] = Field(default="", description="The unique identifier of the tool") + plugin_id: str | None = Field(default="", description="The plugin id of the tool") + plugin_unique_identifier: str | None = Field(default="", description="The unique identifier of the tool") supported_creation_methods: list[TriggerCreationMethod] = Field( default_factory=list, description="Supported creation methods for the trigger provider. like 'OAUTH', 'APIKEY', 'MANUAL'.", ) - subscription_constructor: Optional[SubscriptionConstructor] = Field( + subscription_constructor: SubscriptionConstructor | None = Field( default=None, description="The subscription constructor of the trigger provider" ) diff --git a/api/core/trigger/entities/entities.py b/api/core/trigger/entities/entities.py index 5f70bdeae2..c4814d767f 100644 --- a/api/core/trigger/entities/entities.py +++ b/api/core/trigger/entities/entities.py @@ -40,12 +40,12 @@ class EventParameter(BaseModel): name: str = Field(..., description="The name of the parameter") label: I18nObject = Field(..., description="The label presented to the user") type: EventParameterType = Field(..., description="The type of the parameter") - auto_generate: Optional[PluginParameterAutoGenerate] = Field( + auto_generate: PluginParameterAutoGenerate | None = Field( default=None, description="The auto generate of the parameter" ) - template: Optional[PluginParameterTemplate] = Field(default=None, description="The template of the parameter") - scope: Optional[str] = None - required: Optional[bool] = False + template: PluginParameterTemplate | None = Field(default=None, description="The template of the parameter") + scope: str | None = None + required: bool | None = False multiple: bool | None = Field( default=False, description="Whether the parameter is multiple select, only valid for select or dynamic-select type", @@ -53,9 +53,9 @@ class EventParameter(BaseModel): default: Union[int, float, str, list[Any], None] = None min: Union[float, int, None] = None max: Union[float, int, None] = None - precision: Optional[int] = None - options: Optional[list[PluginParameterOption]] = None - description: Optional[I18nObject] = None + precision: int | None = None + options: list[PluginParameterOption] | None = None + description: I18nObject | None = None class TriggerProviderIdentity(BaseModel): @@ -67,8 +67,8 @@ class TriggerProviderIdentity(BaseModel): name: str = Field(..., description="The name of the trigger provider") label: I18nObject = Field(..., description="The label of the trigger provider") description: I18nObject = Field(..., description="The description of the trigger provider") - icon: Optional[str] = Field(default=None, description="The icon of the trigger provider") - icon_dark: Optional[str] = Field(default=None, description="The dark icon of the trigger provider") + icon: str | None = Field(default=None, description="The icon of the trigger provider") + icon_dark: str | None = Field(default=None, description="The dark icon of the trigger provider") tags: list[str] = Field(default_factory=list, description="The tags of the trigger provider") @@ -80,7 +80,7 @@ class EventIdentity(BaseModel): author: str = Field(..., description="The author of the event") name: str = Field(..., description="The name of the event") label: I18nObject = Field(..., description="The label of the event") - provider: Optional[str] = Field(default=None, description="The provider of the event") + provider: str | None = Field(default=None, description="The provider of the event") class EventEntity(BaseModel): @@ -93,7 +93,7 @@ class EventEntity(BaseModel): default_factory=list[EventParameter], description="The parameters of the event" ) description: I18nObject = Field(..., description="The description of the event") - output_schema: Optional[Mapping[str, Any]] = Field( + output_schema: Mapping[str, Any] | None = Field( default=None, description="The output schema that this event produces" ) @@ -124,7 +124,7 @@ class SubscriptionConstructor(BaseModel): description="The credentials schema of the subscription constructor", ) - oauth_schema: Optional[OAuthSchema] = Field( + oauth_schema: OAuthSchema | None = Field( default=None, description="The OAuth schema of the subscription constructor if OAuth is supported", ) @@ -183,7 +183,7 @@ class UnsubscribeResult(BaseModel): success: bool = Field(..., description="Whether the unsubscription was successful") - message: Optional[str] = Field( + message: str | None = Field( None, description="Human-readable message about the operation result. " "Success message for successful operations, " diff --git a/api/core/workflow/nodes/trigger_plugin/entities.py b/api/core/workflow/nodes/trigger_plugin/entities.py index e6853bb854..d9c4a7274b 100644 --- a/api/core/workflow/nodes/trigger_plugin/entities.py +++ b/api/core/workflow/nodes/trigger_plugin/entities.py @@ -39,7 +39,7 @@ class TriggerEventNodeData(BaseNodeData): return type title: str - desc: Optional[str] = None + desc: str | None = None plugin_id: str = Field(..., description="Plugin ID") provider_id: str = Field(..., description="Provider ID") event_name: str = Field(..., description="Event name") diff --git a/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py b/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py index 0a70aa8727..d1990b9632 100644 --- a/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py +++ b/api/core/workflow/nodes/trigger_plugin/trigger_event_node.py @@ -20,7 +20,7 @@ class TriggerEventNode(Node): def init_node_data(self, data: Mapping[str, Any]) -> None: self._node_data = TriggerEventNodeData.model_validate(data) - def _get_error_strategy(self) -> Optional[ErrorStrategy]: + def _get_error_strategy(self) -> ErrorStrategy | None: return self._node_data.error_strategy def _get_retry_config(self) -> RetryConfig: @@ -29,7 +29,7 @@ class TriggerEventNode(Node): def _get_title(self) -> str: return self._node_data.title - def _get_description(self) -> Optional[str]: + def _get_description(self) -> str | None: return self._node_data.desc def _get_default_value_dict(self) -> dict[str, Any]: diff --git a/api/core/workflow/nodes/trigger_schedule/entities.py b/api/core/workflow/nodes/trigger_schedule/entities.py index 7ff78c4054..e09e1867c6 100644 --- a/api/core/workflow/nodes/trigger_schedule/entities.py +++ b/api/core/workflow/nodes/trigger_schedule/entities.py @@ -11,11 +11,11 @@ class TriggerScheduleNodeData(BaseNodeData): """ mode: str = Field(default="visual", description="Schedule mode: visual or cron") - frequency: Optional[str] = Field( + frequency: str | None = Field( default=None, description="Frequency for visual mode: hourly, daily, weekly, monthly" ) - cron_expression: Optional[str] = Field(default=None, description="Cron expression for cron mode") - visual_config: Optional[dict] = Field(default=None, description="Visual configuration details") + cron_expression: str | None = Field(default=None, description="Cron expression for cron mode") + visual_config: dict | None = Field(default=None, description="Visual configuration details") timezone: str = Field(default="UTC", description="Timezone for schedule execution") @@ -26,26 +26,26 @@ class ScheduleConfig(BaseModel): class SchedulePlanUpdate(BaseModel): - node_id: Optional[str] = None - cron_expression: Optional[str] = None - timezone: Optional[str] = None + node_id: str | None = None + cron_expression: str | None = None + timezone: str | None = None class VisualConfig(BaseModel): """Visual configuration for schedule trigger""" # For hourly frequency - on_minute: Optional[int] = Field(default=0, ge=0, le=59, description="Minute of the hour (0-59)") + on_minute: int | None = Field(default=0, ge=0, le=59, description="Minute of the hour (0-59)") # For daily, weekly, monthly frequencies - time: Optional[str] = Field(default="12:00 AM", description="Time in 12-hour format (e.g., '2:30 PM')") + time: str | None = Field(default="12:00 AM", description="Time in 12-hour format (e.g., '2:30 PM')") # For weekly frequency - weekdays: Optional[list[Literal["sun", "mon", "tue", "wed", "thu", "fri", "sat"]]] = Field( + weekdays: list[Literal["sun", "mon", "tue", "wed", "thu", "fri", "sat"]] | None = Field( default=None, description="List of weekdays to run on" ) # For monthly frequency - monthly_days: Optional[list[Union[int, Literal["last"]]]] = Field( + monthly_days: list[Union[int, Literal["last"]]] | None = Field( default=None, description="Days of month to run on (1-31 or 'last')" ) diff --git a/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py b/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py index 55d66ac907..824e4e98ad 100644 --- a/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py +++ b/api/core/workflow/nodes/trigger_schedule/trigger_schedule_node.py @@ -19,7 +19,7 @@ class TriggerScheduleNode(Node): def init_node_data(self, data: Mapping[str, Any]) -> None: self._node_data = TriggerScheduleNodeData(**data) - def _get_error_strategy(self) -> Optional[ErrorStrategy]: + def _get_error_strategy(self) -> ErrorStrategy | None: return self._node_data.error_strategy def _get_retry_config(self) -> RetryConfig: @@ -28,7 +28,7 @@ class TriggerScheduleNode(Node): def _get_title(self) -> str: return self._node_data.title - def _get_description(self) -> Optional[str]: + def _get_description(self) -> str | None: return self._node_data.desc def _get_default_value_dict(self) -> dict[str, Any]: diff --git a/api/core/workflow/nodes/trigger_webhook/entities.py b/api/core/workflow/nodes/trigger_webhook/entities.py index edb7338473..df14f6b79d 100644 --- a/api/core/workflow/nodes/trigger_webhook/entities.py +++ b/api/core/workflow/nodes/trigger_webhook/entities.py @@ -75,5 +75,5 @@ class WebhookData(BaseNodeData): response_body: str = "" # Template for response body # Webhook specific fields (not from client data, set internally) - webhook_id: Optional[str] = None # Set when webhook trigger is created + webhook_id: str | None = None # Set when webhook trigger is created timeout: int = 30 # Timeout in seconds to wait for webhook response diff --git a/api/core/workflow/nodes/trigger_webhook/node.py b/api/core/workflow/nodes/trigger_webhook/node.py index 58ccf5a3cc..7ec1825551 100644 --- a/api/core/workflow/nodes/trigger_webhook/node.py +++ b/api/core/workflow/nodes/trigger_webhook/node.py @@ -20,7 +20,7 @@ class TriggerWebhookNode(Node): def init_node_data(self, data: Mapping[str, Any]) -> None: self._node_data = WebhookData.model_validate(data) - def _get_error_strategy(self) -> Optional[ErrorStrategy]: + def _get_error_strategy(self) -> ErrorStrategy | None: return self._node_data.error_strategy def _get_retry_config(self) -> RetryConfig: @@ -29,7 +29,7 @@ class TriggerWebhookNode(Node): def _get_title(self) -> str: return self._node_data.title - def _get_description(self) -> Optional[str]: + def _get_description(self) -> str | None: return self._node_data.desc def _get_default_value_dict(self) -> dict[str, Any]: diff --git a/api/events/event_handlers/sync_workflow_schedule_when_app_published.py b/api/events/event_handlers/sync_workflow_schedule_when_app_published.py index ddbb8479d1..912abe1274 100644 --- a/api/events/event_handlers/sync_workflow_schedule_when_app_published.py +++ b/api/events/event_handlers/sync_workflow_schedule_when_app_published.py @@ -33,7 +33,7 @@ def handle(sender, **kwargs): sync_schedule_from_workflow(tenant_id=app.tenant_id, app_id=app.id, workflow=published_workflow) -def sync_schedule_from_workflow(tenant_id: str, app_id: str, workflow: Workflow) -> Optional[WorkflowSchedulePlan]: +def sync_schedule_from_workflow(tenant_id: str, app_id: str, workflow: Workflow) -> WorkflowSchedulePlan | None: """ Sync schedule plan from workflow graph configuration. diff --git a/api/libs/schedule_utils.py b/api/libs/schedule_utils.py index 3d70ef2ef2..4105a8e4c4 100644 --- a/api/libs/schedule_utils.py +++ b/api/libs/schedule_utils.py @@ -8,7 +8,7 @@ from croniter import croniter def calculate_next_run_at( cron_expression: str, timezone: str, - base_time: Optional[datetime] = None, + base_time: datetime | None = None, ) -> datetime: """ Calculate the next run time for a cron expression in a specific timezone. diff --git a/api/models/trigger.py b/api/models/trigger.py index 22bdcbca33..9bc57daf1e 100644 --- a/api/models/trigger.py +++ b/api/models/trigger.py @@ -194,32 +194,32 @@ class WorkflowTriggerLog(Base): tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False) app_id: Mapped[str] = mapped_column(StringUUID, nullable=False) workflow_id: Mapped[str] = mapped_column(StringUUID, nullable=False) - workflow_run_id: Mapped[Optional[str]] = mapped_column(StringUUID, nullable=True) - root_node_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) + workflow_run_id: Mapped[str | None] = mapped_column(StringUUID, nullable=True) + root_node_id: Mapped[str | None] = mapped_column(String(255), nullable=True) trigger_metadata: Mapped[str] = mapped_column(sa.Text, nullable=False) trigger_type: Mapped[str] = mapped_column(EnumText(AppTriggerType, length=50), nullable=False) trigger_data: Mapped[str] = mapped_column(sa.Text, nullable=False) # Full TriggerData as JSON inputs: Mapped[str] = mapped_column(sa.Text, nullable=False) # Just inputs for easy viewing - outputs: Mapped[Optional[str]] = mapped_column(sa.Text, nullable=True) + outputs: Mapped[str | None] = mapped_column(sa.Text, nullable=True) status: Mapped[str] = mapped_column( EnumText(WorkflowTriggerStatus, length=50), nullable=False, default=WorkflowTriggerStatus.PENDING ) - error: Mapped[Optional[str]] = mapped_column(sa.Text, nullable=True) + error: Mapped[str | None] = mapped_column(sa.Text, nullable=True) queue_name: Mapped[str] = mapped_column(String(100), nullable=False) - celery_task_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) + celery_task_id: Mapped[str | None] = mapped_column(String(255), nullable=True) retry_count: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0) - elapsed_time: Mapped[Optional[float]] = mapped_column(sa.Float, nullable=True) - total_tokens: Mapped[Optional[int]] = mapped_column(sa.Integer, nullable=True) + elapsed_time: Mapped[float | None] = mapped_column(sa.Float, nullable=True) + total_tokens: Mapped[int | None] = mapped_column(sa.Integer, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, server_default=func.current_timestamp()) created_by_role: Mapped[str] = mapped_column(String(255), nullable=False) created_by: Mapped[str] = mapped_column(String(255), nullable=False) - triggered_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) - finished_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) + triggered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + finished_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) @property def created_by_account(self): @@ -383,7 +383,7 @@ class AppTrigger(Base): id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuidv7()")) tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False) app_id: Mapped[str] = mapped_column(StringUUID, nullable=False) - node_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=False) + node_id: Mapped[str | None] = mapped_column(String(64), nullable=False) trigger_type: Mapped[str] = mapped_column(EnumText(AppTriggerType, length=50), nullable=False) title: Mapped[str] = mapped_column(String(255), nullable=False) provider_name: Mapped[str] = mapped_column(String(255), server_default="", nullable=True) @@ -435,7 +435,7 @@ class WorkflowSchedulePlan(Base): timezone: Mapped[str] = mapped_column(String(64), nullable=False) # Schedule control - next_run_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) + next_run_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, server_default=func.current_timestamp()) updated_at: Mapped[datetime] = mapped_column( DateTime, nullable=False, server_default=func.current_timestamp(), onupdate=func.current_timestamp() diff --git a/api/repositories/sqlalchemy_workflow_trigger_log_repository.py b/api/repositories/sqlalchemy_workflow_trigger_log_repository.py index 8637f46b06..c2f8322ac3 100644 --- a/api/repositories/sqlalchemy_workflow_trigger_log_repository.py +++ b/api/repositories/sqlalchemy_workflow_trigger_log_repository.py @@ -36,7 +36,7 @@ class SQLAlchemyWorkflowTriggerLogRepository(WorkflowTriggerLogRepository): self.session.flush() return trigger_log - def get_by_id(self, trigger_log_id: str, tenant_id: Optional[str] = None) -> Optional[WorkflowTriggerLog]: + def get_by_id(self, trigger_log_id: str, tenant_id: str | None = None) -> WorkflowTriggerLog | None: """Get a trigger log by its ID.""" query = select(WorkflowTriggerLog).where(WorkflowTriggerLog.id == trigger_log_id) diff --git a/api/repositories/workflow_trigger_log_repository.py b/api/repositories/workflow_trigger_log_repository.py index 495f8cb941..d96ecade2b 100644 --- a/api/repositories/workflow_trigger_log_repository.py +++ b/api/repositories/workflow_trigger_log_repository.py @@ -63,7 +63,7 @@ class WorkflowTriggerLogRepository(Protocol): """ ... - def get_by_id(self, trigger_log_id: str, tenant_id: Optional[str] = None) -> Optional[WorkflowTriggerLog]: + def get_by_id(self, trigger_log_id: str, tenant_id: str | None = None) -> WorkflowTriggerLog | None: """ Get a trigger log by its ID. diff --git a/api/services/app_generate_service.py b/api/services/app_generate_service.py index 137bb01f27..88449787b3 100644 --- a/api/services/app_generate_service.py +++ b/api/services/app_generate_service.py @@ -30,7 +30,7 @@ class AppGenerateService: args: Mapping[str, Any], invoke_from: InvokeFrom, streaming: bool = True, - root_node_id: Optional[str] = None, + root_node_id: str | None = None, ): """ App Content Generate diff --git a/api/services/async_workflow_service.py b/api/services/async_workflow_service.py index e325028ccf..1fbb1fcfda 100644 --- a/api/services/async_workflow_service.py +++ b/api/services/async_workflow_service.py @@ -224,7 +224,7 @@ class AsyncWorkflowService: return cls.trigger_workflow_async(session, user, trigger_data) @classmethod - def get_trigger_log(cls, workflow_trigger_log_id: str, tenant_id: Optional[str] = None) -> Optional[dict[str, Any]]: + def get_trigger_log(cls, workflow_trigger_log_id: str, tenant_id: str | None = None) -> dict[str, Any] | None: """ Get trigger log by ID @@ -293,7 +293,7 @@ class AsyncWorkflowService: return [log.to_dict() for log in logs] @staticmethod - def _get_workflow(workflow_service: WorkflowService, app_model: App, workflow_id: Optional[str] = None) -> Workflow: + def _get_workflow(workflow_service: WorkflowService, app_model: App, workflow_id: str | None = None) -> Workflow: """ Get workflow for the app diff --git a/api/services/trigger/schedule_service.py b/api/services/trigger/schedule_service.py index 97f2def0a1..f4f0a5daf5 100644 --- a/api/services/trigger/schedule_service.py +++ b/api/services/trigger/schedule_service.py @@ -204,7 +204,7 @@ class ScheduleService: return ScheduleConfig(node_id=node_id, cron_expression=cron_expression, timezone=timezone) @staticmethod - def extract_schedule_config(workflow: Workflow) -> Optional[ScheduleConfig]: + def extract_schedule_config(workflow: Workflow) -> ScheduleConfig | None: """ Extracts schedule configuration from workflow graph. diff --git a/api/services/trigger/trigger_provider_service.py b/api/services/trigger/trigger_provider_service.py index f701f44eab..89e8455e84 100644 --- a/api/services/trigger/trigger_provider_service.py +++ b/api/services/trigger/trigger_provider_service.py @@ -117,7 +117,7 @@ class TriggerProviderService: parameters: Mapping[str, Any], properties: Mapping[str, Any], credentials: Mapping[str, str], - subscription_id: Optional[str] = None, + subscription_id: str | None = None, credential_expires_at: int = -1, expires_at: int = -1, ) -> Mapping[str, Any]: @@ -435,7 +435,7 @@ class TriggerProviderService: return {"result": "success", "expires_at": int(refreshed.expires_at)} @classmethod - def get_oauth_client(cls, tenant_id: str, provider_id: TriggerProviderID) -> Optional[Mapping[str, Any]]: + def get_oauth_client(cls, tenant_id: str, provider_id: TriggerProviderID) -> Mapping[str, Any] | None: """ Get OAuth client configuration for a provider. First tries tenant-level OAuth, then falls back to system OAuth. @@ -509,8 +509,8 @@ class TriggerProviderService: cls, tenant_id: str, provider_id: TriggerProviderID, - client_params: Optional[Mapping[str, Any]] = None, - enabled: Optional[bool] = None, + client_params: Mapping[str, Any] | None = None, + enabled: bool | None = None, ) -> Mapping[str, Any]: """ Save or update custom OAuth client parameters for a trigger provider. diff --git a/api/services/trigger/webhook_service.py b/api/services/trigger/webhook_service.py index eaf0e051bb..946764c35c 100644 --- a/api/services/trigger/webhook_service.py +++ b/api/services/trigger/webhook_service.py @@ -67,7 +67,7 @@ class WebhookService: with Session(db.engine) as session: # Get webhook trigger webhook_trigger = ( - session.query(WorkflowWebhookTrigger).filter(WorkflowWebhookTrigger.webhook_id == webhook_id).first() + session.query(WorkflowWebhookTrigger).where(WorkflowWebhookTrigger.webhook_id == webhook_id).first() ) if not webhook_trigger: raise ValueError(f"Webhook not found: {webhook_id}") diff --git a/api/services/workflow/entities.py b/api/services/workflow/entities.py index fff559557e..f80732e092 100644 --- a/api/services/workflow/entities.py +++ b/api/services/workflow/entities.py @@ -30,7 +30,7 @@ class TriggerData(BaseModel): app_id: str tenant_id: str - workflow_id: Optional[str] = None + workflow_id: str | None = None root_node_id: str inputs: Mapping[str, Any] files: Sequence[Mapping[str, Any]] = Field(default_factory=list) @@ -101,10 +101,10 @@ class AsyncTriggerExecutionResult(BaseModel): execution_id: str status: AsyncTriggerStatus - result: Optional[Mapping[str, Any]] = None - error: Optional[str] = None - elapsed_time: Optional[float] = None - total_tokens: Optional[int] = None + result: Mapping[str, Any] | None = None + error: str | None = None + elapsed_time: float | None = None + total_tokens: int | None = None model_config = ConfigDict(use_enum_values=True) @@ -131,15 +131,15 @@ class TriggerLogResponse(BaseModel): status: str queue_name: str retry_count: int - celery_task_id: Optional[str] = None - workflow_run_id: Optional[str] = None - error: Optional[str] = None - outputs: Optional[str] = None - elapsed_time: Optional[float] = None - total_tokens: Optional[int] = None - created_at: Optional[str] = None - triggered_at: Optional[str] = None - finished_at: Optional[str] = None + celery_task_id: str | None = None + workflow_run_id: str | None = None + error: str | None = None + outputs: str | None = None + elapsed_time: float | None = None + total_tokens: int | None = None + created_at: str | None = None + triggered_at: str | None = None + finished_at: str | None = None model_config = ConfigDict(use_enum_values=True) diff --git a/web/app/components/workflow-app/components/workflow-children.tsx b/web/app/components/workflow-app/components/workflow-children.tsx index 35b6219a9b..1c8ed0cdf9 100644 --- a/web/app/components/workflow-app/components/workflow-children.tsx +++ b/web/app/components/workflow-app/components/workflow-children.tsx @@ -56,9 +56,9 @@ const getTriggerPluginNodeData = ( event_description: triggerConfig.event_description, title: triggerConfig.event_label || triggerConfig.title || fallbackTitle, desc: triggerConfig.event_description || fallbackDesc, - output_schema: { ...(triggerConfig.output_schema || {}) }, + output_schema: { ...triggerConfig.output_schema }, parameters_schema: triggerConfig.paramSchemas ? [...triggerConfig.paramSchemas] : [], - config: { ...(triggerConfig.params || {}) }, + config: { ...triggerConfig.params }, subscription_id: triggerConfig.subscription_id, plugin_unique_identifier: triggerConfig.plugin_unique_identifier, is_team_authorization: triggerConfig.is_team_authorization, @@ -124,8 +124,8 @@ const WorkflowChildren = () => { ...baseNodeData, ...triggerNodeData, config: { - ...(baseNodeData as { config?: Record }).config || {}, - ...(triggerNodeData.config || {}), + ...(baseNodeData as { config?: Record }).config, + ...triggerNodeData.config, }, } })() diff --git a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx index f4c6e4d18b..14a0f19317 100644 --- a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx @@ -315,7 +315,7 @@ const FormInputItem: FC = ({ onChange({ ...value, [variable]: { - ...(varInput || {}), + ...varInput, type: VarKindType.constant, value: selected, }, diff --git a/web/app/components/workflow/nodes/trigger-plugin/use-config.ts b/web/app/components/workflow/nodes/trigger-plugin/use-config.ts index 7e4d6ab716..cf66913e58 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/use-config.ts +++ b/web/app/components/workflow/nodes/trigger-plugin/use-config.ts @@ -186,7 +186,7 @@ const useConfig = (id: string, payload: PluginTriggerNodeType) => { (variable: InputVar, varDetail: InputVar) => { const newInputs = produce(inputs, (draft) => { const nextEventParameters = normalizeEventParameters({ - ...(draft.event_parameters || {}), + ...draft.event_parameters, [variable.variable]: { type: VarKindType.variable, value: varDetail.variable, From 5861ca773ee2ae73e50f122e38c0f442152748c3 Mon Sep 17 00:00:00 2001 From: Yeuoly Date: Thu, 30 Oct 2025 17:09:30 +0800 Subject: [PATCH 2/3] fix: mapping to dict --- api/core/workflow/nodes/trigger_plugin/entities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/workflow/nodes/trigger_plugin/entities.py b/api/core/workflow/nodes/trigger_plugin/entities.py index d9c4a7274b..a21b2e534b 100644 --- a/api/core/workflow/nodes/trigger_plugin/entities.py +++ b/api/core/workflow/nodes/trigger_plugin/entities.py @@ -62,7 +62,7 @@ class TriggerEventNodeData(BaseNodeData): Mapping[str, Any]: A dictionary containing the generated parameters. """ - result: Mapping[str, Any] = {} + result: dict[str, Any] = {} for parameter_name in self.event_parameters: parameter: EventParameter | None = parameter_schemas.get(parameter_name) if not parameter: From 291e9a3aee9d230b11ecbd49e2e49fac8f88400c Mon Sep 17 00:00:00 2001 From: Yeuoly Date: Thu, 30 Oct 2025 17:10:21 +0800 Subject: [PATCH 3/3] fix: ruff --- api/core/workflow/nodes/trigger_plugin/entities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/workflow/nodes/trigger_plugin/entities.py b/api/core/workflow/nodes/trigger_plugin/entities.py index a21b2e534b..6c53acee4f 100644 --- a/api/core/workflow/nodes/trigger_plugin/entities.py +++ b/api/core/workflow/nodes/trigger_plugin/entities.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from typing import Any, Literal, Optional, Union +from typing import Any, Literal, Union from pydantic import BaseModel, Field, ValidationInfo, field_validator