diff --git a/api/.ruff.toml b/api/.ruff.toml index 22b99999d24..d15d960714c 100644 --- a/api/.ruff.toml +++ b/api/.ruff.toml @@ -60,7 +60,6 @@ select = [ ignore = [ "E402", # module-import-not-at-top-of-file "F841", # unused-variable - "UP007", # non-pep604-annotation "B901", # allow return in yield "B903", # class-as-data-structure "B904", # raise-without-from-inside-except diff --git a/api/controllers/mcp/mcp.py b/api/controllers/mcp/mcp.py index 98ac0acce18..6295bf00ebc 100644 --- a/api/controllers/mcp/mcp.py +++ b/api/controllers/mcp/mcp.py @@ -1,4 +1,4 @@ -from typing import Any, Union +from typing import Any from flask import Response, request from flask_restx import Resource @@ -69,7 +69,7 @@ class MCPAppApi(Resource): """ # response-contract:ignore MCP route returns Flask Response from JSON-RPC handler args = MCPRequestPayload.model_validate(mcp_ns.payload or {}) - request_id: Union[int, str] | None = args.id + request_id: int | str | None = args.id mcp_request = self._parse_mcp_request(args.model_dump(exclude_none=True)) # Resolve the negotiated protocol version from the MCP-Protocol-Version header. @@ -98,9 +98,7 @@ class MCPAppApi(Resource): session.commit() return response - def _protocol_version_error_response( - self, request_id: Union[int, str] | None, header_value: str | None - ) -> Response: + def _protocol_version_error_response(self, request_id: int | str | None, header_value: str | None) -> Response: """Return a JSON-RPC error for an unsupported MCP-Protocol-Version header. Per JSON-RPC 2.0, an error whose request id is unknown uses a null id, so we echo the @@ -137,7 +135,7 @@ class MCPAppApi(Resource): def _process_mcp_message( self, mcp_request: mcp_types.ClientRequest | mcp_types.ClientNotification, - request_id: Union[int, str] | None, + request_id: int | str | None, app: App, mcp_server: AppMCPServer, user_input_form: list[VariableEntity], @@ -163,7 +161,7 @@ class MCPAppApi(Resource): def _handle_request( self, mcp_request: mcp_types.ClientRequest, - request_id: Union[int, str] | None, + request_id: int | str | None, app: App, mcp_server: AppMCPServer, user_input_form: list[VariableEntity], @@ -272,7 +270,7 @@ class MCPAppApi(Resource): mcp_request: mcp_types.ClientRequest, user_input_form: list[VariableEntity], session: Session, - request_id: Union[int, str], + request_id: int | str, protocol_version: str, ) -> mcp_types.JSONRPCResponse | mcp_types.JSONRPCError | None: """Handle MCP request and return response""" diff --git a/api/core/agent/base_agent_runner.py b/api/core/agent/base_agent_runner.py index 0bbc3880c58..37fb7d3539e 100644 --- a/api/core/agent/base_agent_runner.py +++ b/api/core/agent/base_agent_runner.py @@ -2,7 +2,7 @@ import json import logging import uuid from decimal import Decimal -from typing import Union, cast +from typing import cast from sqlalchemy import func, select from sqlalchemy.orm import Session @@ -269,10 +269,10 @@ class BaseAgentRunner(AppRunner): self, agent_thought_id: str, tool_name: str | None, - tool_input: Union[str, dict, None], + tool_input: str | dict | None, thought: str | None, - observation: Union[str, dict, None], - tool_invoke_meta: Union[str, dict, None], + observation: str | dict | None, + tool_invoke_meta: str | dict | None, answer: str | None, messages_ids: list[str], llm_usage: LLMUsage | None = None, diff --git a/api/core/agent/entities.py b/api/core/agent/entities.py index 220feced1d1..026545f2f44 100644 --- a/api/core/agent/entities.py +++ b/api/core/agent/entities.py @@ -1,5 +1,5 @@ from enum import StrEnum -from typing import Any, Union +from typing import Any from pydantic import BaseModel, Field @@ -39,7 +39,7 @@ class AgentScratchpadUnit(BaseModel): """ action_name: str - action_input: Union[dict, str] + action_input: dict | str def to_dict(self): """ diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index a3bd64c1b2a..1b204608632 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -3,7 +3,7 @@ import logging import re from collections.abc import Generator from copy import deepcopy -from typing import Any, Union +from typing import Any from sqlalchemy import select from sqlalchemy.orm import Session @@ -173,7 +173,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): "created_by": CreditUsageCreatedBy.APP, } - chunks: Union[Generator[LLMResultChunk], LLMResult] = model_instance.invoke_llm( + chunks: Generator[LLMResultChunk] | LLMResult = model_instance.invoke_llm( prompt_messages=prompt_messages, model_parameters=app_generate_entity.model_conf.parameters, tools=prompt_messages_tools, diff --git a/api/core/agent/output_parser/cot_output_parser.py b/api/core/agent/output_parser/cot_output_parser.py index fb854ced2eb..75da9133b5f 100644 --- a/api/core/agent/output_parser/cot_output_parser.py +++ b/api/core/agent/output_parser/cot_output_parser.py @@ -1,7 +1,7 @@ import json import re from collections.abc import Generator -from typing import Any, Union +from typing import Any from core.agent.entities import AgentScratchpadUnit from graphon.model_runtime.entities.llm_entities import LLMResultChunk @@ -11,8 +11,8 @@ class CotAgentOutputParser: @classmethod def handle_react_stream_output( cls, llm_response: Generator[LLMResultChunk], usage_dict: dict[str, Any] - ) -> Generator[Union[str, AgentScratchpadUnit.Action]]: - def parse_action(action) -> Union[str, AgentScratchpadUnit.Action]: + ) -> Generator[str | AgentScratchpadUnit.Action]: + def parse_action(action) -> str | AgentScratchpadUnit.Action: action_name = None action_input = None if isinstance(action, str): @@ -39,7 +39,7 @@ class CotAgentOutputParser: else: return json.dumps(action) - def extra_json_from_code_block(code_block) -> list[Union[list, dict]]: + def extra_json_from_code_block(code_block) -> list[list | dict]: blocks = re.findall(r"```[json]*\s*([\[{].*[]}])\s*```", code_block, re.DOTALL | re.IGNORECASE) if not blocks: return [] diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index bad6020054d..8497714f685 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -6,7 +6,7 @@ from contextlib import contextmanager from dataclasses import dataclass from datetime import datetime from threading import Thread -from typing import Any, Union +from typing import Any from sqlalchemy import select, update from sqlalchemy.orm import Session @@ -152,7 +152,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport): queue_manager: AppQueueManager, conversation: ConversationSnapshot, message: MessageSnapshot, - user: Union[Account, EndUser], + user: Account | EndUser, stream: bool, dialogue_count: int, draft_var_saver_factory: DraftVariableSaverFactory, @@ -219,11 +219,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport): def process( self, - ) -> Union[ - ChatbotAppBlockingResponse, - AdvancedChatPausedBlockingResponse, - Generator[ChatbotAppStreamResponse], - ]: + ) -> ChatbotAppBlockingResponse | AdvancedChatPausedBlockingResponse | Generator[ChatbotAppStreamResponse]: """ Process generate task pipeline. :return: @@ -243,7 +239,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport): def _to_blocking_response( self, generator: Generator[StreamResponse] - ) -> Union[ChatbotAppBlockingResponse, AdvancedChatPausedBlockingResponse]: + ) -> ChatbotAppBlockingResponse | AdvancedChatPausedBlockingResponse: """ Process blocking response. :return: @@ -515,7 +511,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport): def _handle_node_failed_events( self, - event: Union[QueueNodeFailedEvent, QueueNodeExceptionEvent], + event: QueueNodeFailedEvent | QueueNodeExceptionEvent, **kwargs, ) -> Generator[StreamResponse]: """Handle various node failure events.""" @@ -535,7 +531,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport): event: QueueTextChunkEvent, *, tts_publisher: AppGeneratorTTSPublisher | None = None, - queue_message: Union[WorkflowQueueMessage, MessageQueueMessage] | None = None, + queue_message: WorkflowQueueMessage | MessageQueueMessage | None = None, **kwargs, ) -> Generator[StreamResponse]: """Handle text chunk events.""" @@ -952,7 +948,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport): *, tts_publisher: AppGeneratorTTSPublisher | None = None, trace_manager: TraceQueueManager | None = None, - queue_message: Union[WorkflowQueueMessage, MessageQueueMessage] | None = None, + queue_message: WorkflowQueueMessage | MessageQueueMessage | None = None, ) -> Generator[StreamResponse]: """Dispatch events using elegant pattern matching.""" handlers = self._get_event_handlers() diff --git a/api/core/app/apps/base_app_generate_response_converter.py b/api/core/app/apps/base_app_generate_response_converter.py index 07bbdadaea3..fd92583e671 100644 --- a/api/core/app/apps/base_app_generate_response_converter.py +++ b/api/core/app/apps/base_app_generate_response_converter.py @@ -1,7 +1,7 @@ import logging from abc import ABC, abstractmethod from collections.abc import Generator, Mapping -from typing import Any, Union, cast +from typing import Any, cast from dify_agent.protocol import RunFailureType from pydantic import JsonValue @@ -23,7 +23,7 @@ class AppGenerateResponseConverter[TBlockingResponse: AppBlockingResponse](ABC): @classmethod def convert( - cls, response: Union[AppBlockingResponse, Generator[AppStreamResponse, Any]], invoke_from: InvokeFrom + cls, response: AppBlockingResponse | Generator[AppStreamResponse, Any], invoke_from: InvokeFrom ) -> Mapping[str, Any] | Generator[str | Mapping[str, Any], Any]: if invoke_from in {InvokeFrom.DEBUGGER, InvokeFrom.SERVICE_API, InvokeFrom.OPENAPI}: if isinstance(response, AppBlockingResponse): diff --git a/api/core/app/apps/base_app_generator.py b/api/core/app/apps/base_app_generator.py index 0305952724f..0964b9a33ba 100644 --- a/api/core/app/apps/base_app_generator.py +++ b/api/core/app/apps/base_app_generator.py @@ -2,7 +2,7 @@ import logging import threading from collections.abc import Generator, Mapping, Sequence from contextlib import AbstractContextManager, nullcontext -from typing import TYPE_CHECKING, Any, Union, final +from typing import TYPE_CHECKING, Any, final from sqlalchemy.orm import Session from sqlalchemy.orm.attributes import set_committed_value @@ -310,7 +310,7 @@ class BaseAppGenerator: return value @classmethod - def convert_to_event_stream(cls, generator: Union[Mapping, Generator[Mapping | str]]): + def convert_to_event_stream(cls, generator: Mapping | Generator[Mapping | str]): """ Convert messages into event stream """ diff --git a/api/core/app/apps/base_app_runner.py b/api/core/app/apps/base_app_runner.py index 30b7605756b..774cf255968 100644 --- a/api/core/app/apps/base_app_runner.py +++ b/api/core/app/apps/base_app_runner.py @@ -3,7 +3,7 @@ import logging import time from collections.abc import Generator, Mapping, Sequence from mimetypes import guess_extension -from typing import TYPE_CHECKING, Any, Union +from typing import TYPE_CHECKING, Any from sqlalchemy.orm import Session @@ -115,7 +115,7 @@ class AppRunner: """ # get prompt without memory and context if prompt_template_entity.prompt_type == PromptTemplateEntity.PromptType.SIMPLE: - prompt_transform: Union[SimplePromptTransform, AdvancedPromptTransform] + prompt_transform: SimplePromptTransform | AdvancedPromptTransform prompt_transform = SimplePromptTransform() prompt_messages, stop = prompt_transform.get_prompt( app_mode=AppMode.value_of(app_record.mode), @@ -133,7 +133,7 @@ class AppRunner: memory_config = MemoryConfig(window=MemoryConfig.WindowConfig(enabled=False)) model_mode = ModelMode(model_config.mode) - prompt_template: Union[CompletionModelPromptTemplate, list[ChatModelMessage]] + prompt_template: CompletionModelPromptTemplate | list[ChatModelMessage] if model_mode == ModelMode.COMPLETION: advanced_completion_prompt_template = prompt_template_entity.advanced_completion_prompt_template if not advanced_completion_prompt_template: @@ -212,7 +212,7 @@ class AppRunner: def _handle_invoke_result( self, - invoke_result: Union[LLMResult, Generator[Any]], + invoke_result: LLMResult | Generator[Any], queue_manager: AppQueueManager, stream: bool, agent: bool = False, diff --git a/api/core/app/apps/common/workflow_response_converter.py b/api/core/app/apps/common/workflow_response_converter.py index e95a68c0b44..77ff812b2c9 100644 --- a/api/core/app/apps/common/workflow_response_converter.py +++ b/api/core/app/apps/common/workflow_response_converter.py @@ -4,7 +4,7 @@ import time from collections.abc import Mapping, Sequence from dataclasses import dataclass from datetime import datetime -from typing import Any, NewType, TypedDict, Union +from typing import Any, NewType, TypedDict from sqlalchemy import select from sqlalchemy.orm import Session @@ -127,8 +127,8 @@ class WorkflowResponseConverter: def __init__( self, *, - application_generate_entity: Union[AdvancedChatAppGenerateEntity, WorkflowAppGenerateEntity], - user: Union[Account, EndUser], + application_generate_entity: AdvancedChatAppGenerateEntity | WorkflowAppGenerateEntity, + user: Account | EndUser, system_variables: Sequence[Variable], ): self._application_generate_entity = application_generate_entity @@ -857,7 +857,7 @@ class WorkflowResponseConverter: @classmethod def _fetch_files_from_variable_value( - cls, value: Union[dict, list, Segment, File, None] + cls, value: dict | list | Segment | File | None ) -> Sequence[Mapping[str, Any]]: """ Fetch files from variable value diff --git a/api/core/app/apps/message_based_app_generator.py b/api/core/app/apps/message_based_app_generator.py index ea6d27f46af..42d2247d86e 100644 --- a/api/core/app/apps/message_based_app_generator.py +++ b/api/core/app/apps/message_based_app_generator.py @@ -1,7 +1,7 @@ import json import logging from collections.abc import Callable, Generator, Mapping -from typing import Union, cast +from typing import cast from sqlalchemy import select from sqlalchemy.orm import Session @@ -46,21 +46,17 @@ logger = logging.getLogger(__name__) class MessageBasedAppGenerator(BaseAppGenerator): def _handle_response( self, - application_generate_entity: Union[ - ChatAppGenerateEntity, - CompletionAppGenerateEntity, - AgentChatAppGenerateEntity, - ], + application_generate_entity: ChatAppGenerateEntity | CompletionAppGenerateEntity | AgentChatAppGenerateEntity, queue_manager: AppQueueManager, conversation: Conversation, message: Message, - user: Union[Account, EndUser], + user: Account | EndUser, stream: bool = False, - ) -> Union[ - ChatbotAppBlockingResponse, - CompletionAppBlockingResponse, - Generator[Union[ChatbotAppStreamResponse, CompletionAppStreamResponse]], - ]: + ) -> ( + ChatbotAppBlockingResponse + | CompletionAppBlockingResponse + | Generator[ChatbotAppStreamResponse | CompletionAppStreamResponse] + ): """ Handle response. :param application_generate_entity: application generate entity @@ -117,12 +113,10 @@ class MessageBasedAppGenerator(BaseAppGenerator): def _init_generate_records( self, - application_generate_entity: Union[ - ChatAppGenerateEntity, - CompletionAppGenerateEntity, - AgentChatAppGenerateEntity, - AdvancedChatAppGenerateEntity, - ], + application_generate_entity: ChatAppGenerateEntity + | CompletionAppGenerateEntity + | AgentChatAppGenerateEntity + | AdvancedChatAppGenerateEntity, conversation: Conversation | None = None, *, session: Session, diff --git a/api/core/app/apps/workflow/generate_task_pipeline.py b/api/core/app/apps/workflow/generate_task_pipeline.py index 90360f76bb6..e5f9e68b2aa 100644 --- a/api/core/app/apps/workflow/generate_task_pipeline.py +++ b/api/core/app/apps/workflow/generate_task_pipeline.py @@ -2,7 +2,6 @@ import logging import time from collections.abc import Callable, Generator from contextlib import contextmanager -from typing import Union from sqlalchemy.orm import Session, sessionmaker @@ -83,7 +82,7 @@ class WorkflowAppGenerateTaskPipeline(GraphRuntimeStateSupport): application_generate_entity: WorkflowAppGenerateEntity, workflow: Workflow, queue_manager: AppQueueManager, - user: Union[Account, EndUser], + user: Account | EndUser, stream: bool, draft_var_saver_factory: DraftVariableSaverFactory, ): @@ -124,7 +123,7 @@ class WorkflowAppGenerateTaskPipeline(GraphRuntimeStateSupport): def process( self, - ) -> Union[WorkflowAppBlockingResponse, WorkflowAppPausedBlockingResponse, Generator[WorkflowAppStreamResponse]]: + ) -> WorkflowAppBlockingResponse | WorkflowAppPausedBlockingResponse | Generator[WorkflowAppStreamResponse]: """ Process generate task pipeline. :return: @@ -137,7 +136,7 @@ class WorkflowAppGenerateTaskPipeline(GraphRuntimeStateSupport): def _to_blocking_response( self, generator: Generator[StreamResponse] - ) -> Union[WorkflowAppBlockingResponse, WorkflowAppPausedBlockingResponse]: + ) -> WorkflowAppBlockingResponse | WorkflowAppPausedBlockingResponse: """ To blocking response. :return: @@ -383,7 +382,7 @@ class WorkflowAppGenerateTaskPipeline(GraphRuntimeStateSupport): def _handle_node_failed_events( self, - event: Union[QueueNodeFailedEvent, QueueNodeExceptionEvent], + event: QueueNodeFailedEvent | QueueNodeExceptionEvent, **kwargs, ) -> Generator[StreamResponse]: """Handle various node failure events.""" @@ -523,7 +522,7 @@ class WorkflowAppGenerateTaskPipeline(GraphRuntimeStateSupport): def _handle_workflow_failed_and_stop_events( self, - event: Union[QueueWorkflowFailedEvent, QueueStopEvent], + event: QueueWorkflowFailedEvent | QueueStopEvent, *, trace_manager: TraceQueueManager | None = None, **kwargs, @@ -556,7 +555,7 @@ class WorkflowAppGenerateTaskPipeline(GraphRuntimeStateSupport): event: QueueTextChunkEvent, *, tts_publisher: AppGeneratorTTSPublisher | None = None, - queue_message: Union[WorkflowQueueMessage, MessageQueueMessage] | None = None, + queue_message: WorkflowQueueMessage | MessageQueueMessage | None = None, **kwargs, ) -> Generator[StreamResponse]: """Handle text chunk events.""" @@ -643,7 +642,7 @@ class WorkflowAppGenerateTaskPipeline(GraphRuntimeStateSupport): *, tts_publisher: AppGeneratorTTSPublisher | None = None, trace_manager: TraceQueueManager | None = None, - queue_message: Union[WorkflowQueueMessage, MessageQueueMessage] | None = None, + queue_message: WorkflowQueueMessage | MessageQueueMessage | None = None, ) -> Generator[StreamResponse]: """Dispatch events using elegant pattern matching.""" handlers = self._get_event_handlers() diff --git a/api/core/app/features/rate_limiting/rate_limit.py b/api/core/app/features/rate_limiting/rate_limit.py index 8babb4af026..d495be83a95 100644 --- a/api/core/app/features/rate_limiting/rate_limit.py +++ b/api/core/app/features/rate_limiting/rate_limit.py @@ -4,7 +4,7 @@ import time import uuid from collections.abc import Generator, Mapping from datetime import timedelta -from typing import Any, Union +from typing import Any from core.errors.error import AppInvokeQuotaExceededError from extensions.ext_redis import redis_client @@ -99,7 +99,7 @@ class RateLimit: def gen_request_key() -> str: return str(uuid.uuid4()) - def generate(self, generator: Union[Generator[str], Mapping[str, Any]], request_id: str): + def generate(self, generator: Generator[str] | Mapping[str, Any], request_id: str): if isinstance(generator, Mapping): return generator else: diff --git a/api/core/app/task_pipeline/message_cycle_manager.py b/api/core/app/task_pipeline/message_cycle_manager.py index f1100d69ef2..d68dfb20c5b 100644 --- a/api/core/app/task_pipeline/message_cycle_manager.py +++ b/api/core/app/task_pipeline/message_cycle_manager.py @@ -1,7 +1,6 @@ import hashlib import logging from threading import Thread, Timer -from typing import Union from flask import Flask, current_app from sqlalchemy import select @@ -45,13 +44,11 @@ class MessageCycleManager: def __init__( self, *, - application_generate_entity: Union[ - ChatAppGenerateEntity, - CompletionAppGenerateEntity, - AgentChatAppGenerateEntity, - AdvancedChatAppGenerateEntity, - ], - task_state: Union[EasyUITaskState, WorkflowTaskState], + application_generate_entity: ChatAppGenerateEntity + | CompletionAppGenerateEntity + | AgentChatAppGenerateEntity + | AdvancedChatAppGenerateEntity, + task_state: EasyUITaskState | WorkflowTaskState, ): self._application_generate_entity = application_generate_entity self._task_state = task_state diff --git a/api/core/app/workflow/layers/persistence.py b/api/core/app/workflow/layers/persistence.py index 8b04661deb3..6edc745701d 100644 --- a/api/core/app/workflow/layers/persistence.py +++ b/api/core/app/workflow/layers/persistence.py @@ -12,7 +12,7 @@ state. from collections.abc import Mapping from dataclasses import dataclass from datetime import datetime -from typing import Any, Union, override +from typing import Any, override from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, WorkflowAppGenerateEntity from core.app.workflow.retry_history import RETRY_HISTORY_PROCESS_DATA_KEY, WorkflowNodeRetryAttempt @@ -86,7 +86,7 @@ class WorkflowPersistenceLayer(GraphEngineLayer): def __init__( self, *, - application_generate_entity: Union[AdvancedChatAppGenerateEntity, WorkflowAppGenerateEntity], + application_generate_entity: AdvancedChatAppGenerateEntity | WorkflowAppGenerateEntity, workflow_info: PersistenceWorkflowInfo, workflow_execution_repository: WorkflowExecutionRepository, workflow_node_execution_repository: WorkflowNodeExecutionRepository, diff --git a/api/core/callback_handler/agent_tool_callback_handler.py b/api/core/callback_handler/agent_tool_callback_handler.py index 6591b08a7e8..79b3b869803 100644 --- a/api/core/callback_handler/agent_tool_callback_handler.py +++ b/api/core/callback_handler/agent_tool_callback_handler.py @@ -1,5 +1,5 @@ from collections.abc import Iterable, Mapping -from typing import Any, TextIO, Union +from typing import Any, TextIO from pydantic import BaseModel @@ -82,7 +82,7 @@ class DifyAgentCallbackHandler(BaseModel): ) ) - def on_tool_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any): + def on_tool_error(self, error: Exception | KeyboardInterrupt, **kwargs: Any): """Do nothing.""" if dify_config.DEBUG: print_text("\n[on_tool_error] Error: " + str(error) + "\n", color="red") diff --git a/api/core/datasource/datasource_file_manager.py b/api/core/datasource/datasource_file_manager.py index 317f362d662..e9b4baf0ae1 100644 --- a/api/core/datasource/datasource_file_manager.py +++ b/api/core/datasource/datasource_file_manager.py @@ -6,7 +6,6 @@ import os import time from datetime import datetime from mimetypes import guess_extension, guess_type -from typing import Union from uuid import uuid4 import httpx @@ -135,7 +134,7 @@ class DatasourceFileManager: return tool_file @staticmethod - def get_file_binary(id: str) -> Union[tuple[bytes, str], None]: + def get_file_binary(id: str) -> tuple[bytes, str] | None: """ get file binary @@ -154,7 +153,7 @@ class DatasourceFileManager: return blob, upload_file.mime_type @staticmethod - def get_file_binary_by_message_file_id(id: str) -> Union[tuple[bytes, str], None]: + def get_file_binary_by_message_file_id(id: str) -> tuple[bytes, str] | None: """ get file binary diff --git a/api/core/entities/provider_entities.py b/api/core/entities/provider_entities.py index 4ac7614425c..25428bd2fad 100644 --- a/api/core/entities/provider_entities.py +++ b/api/core/entities/provider_entities.py @@ -1,7 +1,7 @@ from __future__ import annotations from enum import StrEnum, auto -from typing import Any, Union +from typing import Any from pydantic import BaseModel, ConfigDict, Field @@ -208,7 +208,7 @@ class ProviderConfig(BasicProviderConfig): scope: AppSelectorScope | ModelSelectorScope | ToolSelectorScope | None = None required: bool = False - default: Union[int, str, float, bool] | None = None + default: int | str | float | bool | None = None options: list[Option] | None = None multiple: bool = False label: I18nObject | None = None diff --git a/api/core/model_manager.py b/api/core/model_manager.py index 384a537a781..d43c15a382f 100644 --- a/api/core/model_manager.py +++ b/api/core/model_manager.py @@ -1,7 +1,7 @@ import logging from collections.abc import Callable, Generator, Iterable, Mapping, Sequence from copy import deepcopy -from typing import IO, Any, Literal, Optional, ParamSpec, TypeVar, Union, cast, overload, override +from typing import IO, Any, Literal, Optional, ParamSpec, TypeVar, cast, overload, override from uuid import UUID from configs import dify_config @@ -176,7 +176,7 @@ class ModelInstance: stream: bool = True, callbacks: list[Callback] | None = None, request_metadata: Mapping[str, object] | None = None, - ) -> Union[LLMResult, Generator]: ... + ) -> LLMResult | Generator: ... def invoke_llm( self, @@ -187,7 +187,7 @@ class ModelInstance: stream: bool = True, callbacks: list[Callback] | None = None, request_metadata: Mapping[str, object] | None = None, - ) -> Union[LLMResult, Generator]: + ) -> LLMResult | Generator: """ Invoke large language model @@ -204,7 +204,7 @@ class ModelInstance: raise Exception("Model type instance is not LargeLanguageModel") request_metadata = self._resolve_request_metadata(request_metadata) return cast( - Union[LLMResult, Generator], + LLMResult | Generator, self._round_robin_invoke( self.model_type_instance.invoke, model=self.model_name, @@ -437,7 +437,7 @@ class ModelInstance: if not self.load_balancing_manager: return function(*args, **kwargs) - last_exception: Union[InvokeRateLimitError, InvokeAuthorizationError, InvokeConnectionError, None] = None + last_exception: InvokeRateLimitError | InvokeAuthorizationError | InvokeConnectionError | None = None while True: lb_config = self.load_balancing_manager.fetch_next() if not lb_config: @@ -620,7 +620,7 @@ class QuotaManagedModelInstance(ModelInstance): stream: bool = True, callbacks: list[Callback] | None = None, request_metadata: Mapping[str, object] | None = None, - ) -> Union[LLMResult, Generator]: ... + ) -> LLMResult | Generator: ... @override def invoke_llm( @@ -632,7 +632,7 @@ class QuotaManagedModelInstance(ModelInstance): stream: bool = True, callbacks: list[Callback] | None = None, request_metadata: Mapping[str, object] | None = None, - ) -> Union[LLMResult, Generator]: + ) -> LLMResult | Generator: normalized_prompt_messages = list(prompt_messages) normalized_stop = list(stop) if stop else None if stream: diff --git a/api/core/ops/entities/trace_entity.py b/api/core/ops/entities/trace_entity.py index bfbc5a8cc2e..e9685a208ee 100644 --- a/api/core/ops/entities/trace_entity.py +++ b/api/core/ops/entities/trace_entity.py @@ -1,7 +1,7 @@ from collections.abc import Mapping from datetime import datetime from enum import StrEnum -from typing import Any, Union +from typing import Any from pydantic import BaseModel, ConfigDict, field_serializer, field_validator @@ -12,8 +12,8 @@ class BaseTraceInfo(BaseModel): operation_id: str | None = None message_id: str | None = None message_data: Any | None = None - inputs: Union[str, dict[str, Any], list[Any]] | None = None - outputs: Union[str, dict[str, Any], list[Any]] | None = None + inputs: str | dict[str, Any] | list[Any] | None = None + outputs: str | dict[str, Any] | list[Any] | None = None start_time: datetime | None = None end_time: datetime | None = None metadata: dict[str, Any] @@ -92,7 +92,7 @@ class WorkflowTraceInfo(BaseTraceInfo): workflow_id: str tenant_id: str workflow_run_id: str - workflow_run_elapsed_time: Union[int, float] + workflow_run_elapsed_time: int | float workflow_run_status: str workflow_run_inputs: Mapping[str, Any] workflow_run_outputs: Mapping[str, Any] @@ -113,7 +113,7 @@ class MessageTraceInfo(BaseTraceInfo): answer_tokens: int total_tokens: int error: str | None = None - file_list: Union[str, dict[str, Any], list[Any]] | None = None + file_list: str | dict[str, Any] | list[Any] | None = None message_file_data: Any | None = None conversation_mode: str gen_ai_server_time_to_first_token: float | None = None @@ -158,9 +158,9 @@ class ToolTraceInfo(BaseTraceInfo): message_file_data: Any = None error: str | None = None tool_config: dict[str, Any] - time_cost: Union[int, float] + time_cost: int | float tool_parameters: dict[str, Any] - file_url: Union[str, None, list[str]] = None + file_url: str | None | list[str] = None class GenerateNameTraceInfo(BaseTraceInfo): diff --git a/api/core/ops/utils.py b/api/core/ops/utils.py index 87e0f52a76d..0526c6ab5be 100644 --- a/api/core/ops/utils.py +++ b/api/core/ops/utils.py @@ -1,6 +1,6 @@ from contextlib import contextmanager from datetime import datetime -from typing import Any, Union +from typing import Any from urllib.parse import urlparse from pydantic import TypeAdapter @@ -54,7 +54,7 @@ def replace_text_with_content(data): return data -def generate_dotted_order(run_id: str, start_time: Union[str, datetime], parent_dotted_order: str | None = None) -> str: +def generate_dotted_order(run_id: str, start_time: str | datetime, parent_dotted_order: str | None = None) -> str: """ generate dotted_order for langsmith """ diff --git a/api/core/plugin/entities/parameters.py b/api/core/plugin/entities/parameters.py index 8d66376f7ec..1e08737cbe0 100644 --- a/api/core/plugin/entities/parameters.py +++ b/api/core/plugin/entities/parameters.py @@ -1,7 +1,7 @@ import json from datetime import date from enum import StrEnum, auto -from typing import Any, Union +from typing import Any from pydantic import BaseModel, Field, field_validator @@ -80,9 +80,9 @@ class PluginParameter(BaseModel): auto_generate: PluginParameterAutoGenerate | None = None template: PluginParameterTemplate | None = None required: bool = False - default: Union[float, int, str, bool, list, dict] | None = None - min: Union[float, int] | None = None - max: Union[float, int] | None = None + default: float | int | str | bool | list | dict | None = None + min: float | int | None = None + max: float | int | None = None precision: int | None = None options: list[PluginParameterOption] = Field(default_factory=list) diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py index d81f26f88e9..7498842fb40 100644 --- a/api/core/rag/retrieval/dataset_retrieval.py +++ b/api/core/rag/retrieval/dataset_retrieval.py @@ -6,7 +6,7 @@ import threading import time from collections import Counter, defaultdict from collections.abc import Generator, Mapping -from typing import Any, Union, cast +from typing import Any, cast from flask import Flask, current_app from opentelemetry.trace import get_current_span @@ -1822,7 +1822,7 @@ class DatasetRetrieval: model_mode = ModelMode(mode) input_text = query - prompt_template: Union[CompletionModelPromptTemplate, list[ChatModelMessage]] + prompt_template: CompletionModelPromptTemplate | list[ChatModelMessage] if model_mode == ModelMode.CHAT: prompt_template = [] system_prompt_messages = ChatModelMessage(role=PromptMessageRole.SYSTEM, text=METADATA_FILTER_SYSTEM_PROMPT) diff --git a/api/core/rag/retrieval/output_parser/react_output.py b/api/core/rag/retrieval/output_parser/react_output.py index 29abae42803..08627c3c1a9 100644 --- a/api/core/rag/retrieval/output_parser/react_output.py +++ b/api/core/rag/retrieval/output_parser/react_output.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, NamedTuple, Union +from typing import Any, NamedTuple @dataclass @@ -10,7 +10,7 @@ class ReactAction: tool: str """The name of the Tool to execute.""" - tool_input: Union[str, dict[str, Any]] + tool_input: str | dict[str, Any] """The input to pass in to the Tool.""" log: str """Additional information to log about the action.""" diff --git a/api/core/rag/retrieval/output_parser/structured_chat.py b/api/core/rag/retrieval/output_parser/structured_chat.py index 5d9001890e5..3011be58d2a 100644 --- a/api/core/rag/retrieval/output_parser/structured_chat.py +++ b/api/core/rag/retrieval/output_parser/structured_chat.py @@ -1,12 +1,11 @@ import json import re -from typing import Union from core.rag.retrieval.output_parser.react_output import ReactAction, ReactFinish class StructuredChatOutputParser: - def parse(self, text: str) -> Union[ReactAction, ReactFinish]: + def parse(self, text: str) -> ReactAction | ReactFinish: try: action_match = re.search(r"```(\w*)\n?({.*?)```", text, re.DOTALL) if action_match is not None: diff --git a/api/core/rag/retrieval/router/multi_dataset_function_call_router.py b/api/core/rag/retrieval/router/multi_dataset_function_call_router.py index 3dc6a6e1b2c..b88ffce59b9 100644 --- a/api/core/rag/retrieval/router/multi_dataset_function_call_router.py +++ b/api/core/rag/retrieval/router/multi_dataset_function_call_router.py @@ -1,5 +1,3 @@ -from typing import Union - from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.credit_usage import CreditUsageCreatedBy from core.model_context import with_credit_usage_created_by @@ -16,7 +14,7 @@ class FunctionCallMultiDatasetRouter: dataset_tools: list[PromptMessageTool], model_config: ModelConfigWithCredentialsEntity, model_instance: ModelInstance, - ) -> tuple[Union[str, None], LLMUsage]: + ) -> tuple[str | None, LLMUsage]: """Given input, decided what to do. Returns: Action specifying what tool to use. diff --git a/api/core/rag/retrieval/router/multi_dataset_react_route.py b/api/core/rag/retrieval/router/multi_dataset_react_route.py index 7bd97428d60..cdb19f61944 100644 --- a/api/core/rag/retrieval/router/multi_dataset_react_route.py +++ b/api/core/rag/retrieval/router/multi_dataset_react_route.py @@ -1,5 +1,5 @@ from collections.abc import Generator, Sequence -from typing import Any, Union +from typing import Any from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.credit_usage import CreditUsageCreatedBy @@ -60,7 +60,7 @@ class ReactMultiDatasetRouter: model_instance: ModelInstance, user_id: str, tenant_id: str, - ) -> tuple[Union[str, None], LLMUsage]: + ) -> tuple[str | None, LLMUsage]: """Given input, decided what to do. Returns: Action specifying what tool to use. @@ -93,8 +93,8 @@ class ReactMultiDatasetRouter: prefix: str = PREFIX, suffix: str = SUFFIX, format_instructions: str = FORMAT_INSTRUCTIONS, - ) -> tuple[Union[str, None], LLMUsage]: - prompt: Union[list[ChatModelMessage], CompletionModelPromptTemplate] + ) -> tuple[str | None, LLMUsage]: + prompt: list[ChatModelMessage] | CompletionModelPromptTemplate if model_config.mode == "chat": prompt = self.create_chat_prompt( query=query, diff --git a/api/core/tools/custom_tool/tool.py b/api/core/tools/custom_tool/tool.py index 6555feda1c1..951fd01bc72 100644 --- a/api/core/tools/custom_tool/tool.py +++ b/api/core/tools/custom_tool/tool.py @@ -2,7 +2,7 @@ import json from collections.abc import Generator from dataclasses import dataclass from os import getenv -from typing import Any, Union, override +from typing import Any, override from urllib.parse import urlencode import httpx @@ -26,7 +26,7 @@ API_TOOL_DEFAULT_TIMEOUT = ( class ParsedResponse: """Represents a parsed HTTP response with type information""" - content: Union[str, dict] + content: str | dict is_json: bool def to_string(self) -> str: diff --git a/api/core/tools/entities/tool_entities.py b/api/core/tools/entities/tool_entities.py index cc8123f0adb..1cab639cd50 100644 --- a/api/core/tools/entities/tool_entities.py +++ b/api/core/tools/entities/tool_entities.py @@ -4,7 +4,7 @@ import base64 import contextlib from collections.abc import Mapping from enum import StrEnum, auto -from typing import Any, Union +from typing import Any from pydantic import ( BaseModel, @@ -549,7 +549,7 @@ class ToolSelector(BaseModel): type: ToolParameter.ToolParameterType = Field(..., description="The type of the parameter") required: bool = Field(..., description="Whether the parameter is required") description: str = Field(..., description="The description of the parameter") - default: Union[int, float, str] | None = None + default: int | float | str | None = None options: list[PluginParameterOption] | None = None provider_id: str = Field(..., description="The id of the provider") diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index 043dcb54003..448054998bd 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -5,7 +5,7 @@ from collections.abc import Generator, Iterable from copy import deepcopy from datetime import UTC, datetime from mimetypes import guess_type -from typing import Any, Union, cast +from typing import Any, cast from sqlalchemy.orm import Session, sessionmaker from yarl import URL @@ -49,7 +49,7 @@ class ToolEngine: def agent_invoke( session: Session, tool: Tool, - tool_parameters: Union[str, dict[str, Any]], + tool_parameters: str | dict[str, Any], user_id: str, tenant_id: str, message: Message, diff --git a/api/core/trigger/entities/entities.py b/api/core/trigger/entities/entities.py index 06a29bed111..6fa06912dbe 100644 --- a/api/core/trigger/entities/entities.py +++ b/api/core/trigger/entities/entities.py @@ -1,7 +1,7 @@ from collections.abc import Mapping from datetime import datetime from enum import StrEnum -from typing import Any, Union +from typing import Any from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator @@ -51,9 +51,9 @@ class EventParameter(BaseModel): default=False, description="Whether the parameter is multiple select, only valid for select or dynamic-select type", ) - default: Union[int, float, str, list[Any], None] = None - min: Union[float, int, None] = None - max: Union[float, int, None] = None + default: int | float | str | list[Any] | None = None + min: float | int | None = None + max: float | int | None = None precision: int | None = None options: list[PluginParameterOption] | None = None description: I18nObject | None = None diff --git a/api/core/trigger/utils/encryption.py b/api/core/trigger/utils/encryption.py index 8409be0813e..62ddcebb39d 100644 --- a/api/core/trigger/utils/encryption.py +++ b/api/core/trigger/utils/encryption.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from typing import Union, override +from typing import override from core.entities.provider_entities import ProviderConfig, ProviderConfigType from core.helper.provider_cache import ProviderCredentialsCache @@ -54,7 +54,7 @@ class TriggerProviderPropertiesCache(ProviderCredentialsCache): def create_trigger_provider_encrypter_for_subscription( tenant_id: str, controller: PluginTriggerProviderController, - subscription: Union[TriggerSubscription, TriggerProviderSubscriptionApiEntity], + subscription: TriggerSubscription | TriggerProviderSubscriptionApiEntity, ) -> tuple[ProviderConfigEncrypter, ProviderConfigCache]: cache = TriggerProviderCredentialsCache( tenant_id=tenant_id, @@ -85,7 +85,7 @@ def delete_cache_for_subscription(tenant_id: str, provider_id: str, subscription def create_trigger_provider_encrypter_for_properties( tenant_id: str, controller: PluginTriggerProviderController, - subscription: Union[TriggerSubscription, TriggerProviderSubscriptionApiEntity], + subscription: TriggerSubscription | TriggerProviderSubscriptionApiEntity, ) -> tuple[ProviderConfigEncrypter, ProviderConfigCache]: cache = TriggerProviderPropertiesCache( tenant_id=tenant_id, diff --git a/api/core/workflow/nodes/agent/entities.py b/api/core/workflow/nodes/agent/entities.py index 16e026939df..7557bb9e17a 100644 --- a/api/core/workflow/nodes/agent/entities.py +++ b/api/core/workflow/nodes/agent/entities.py @@ -1,5 +1,5 @@ from enum import IntEnum, StrEnum, auto -from typing import Any, Literal, Union +from typing import Any, Literal from pydantic import BaseModel, Field @@ -21,7 +21,7 @@ class AgentNodeData(BaseNodeData): tool_node_version: str | None = None class AgentInput(BaseModel): - value: Union[list[str], list[ToolSelector], Any] + value: list[str] | list[ToolSelector] | Any type: Literal["mixed", "variable", "constant"] agent_parameters: dict[str, AgentInput] = Field(default_factory=dict) diff --git a/api/core/workflow/nodes/datasource/entities.py b/api/core/workflow/nodes/datasource/entities.py index 28966f2392c..8eb908743b5 100644 --- a/api/core/workflow/nodes/datasource/entities.py +++ b/api/core/workflow/nodes/datasource/entities.py @@ -1,4 +1,4 @@ -from typing import Any, Literal, Union +from typing import Any, Literal from pydantic import BaseModel, field_validator from pydantic_core.core_schema import ValidationInfo @@ -21,7 +21,7 @@ class DatasourceNodeData(BaseNodeData, DatasourceEntity): class DatasourceInput(BaseModel): # TODO: check this type - value: Union[Any, list[str]] + value: Any | list[str] type: Literal["mixed", "variable", "constant"] | None = None @field_validator("type", mode="before") diff --git a/api/core/workflow/nodes/knowledge_index/entities.py b/api/core/workflow/nodes/knowledge_index/entities.py index 12f47f3d57c..f57dbe68aae 100644 --- a/api/core/workflow/nodes/knowledge_index/entities.py +++ b/api/core/workflow/nodes/knowledge_index/entities.py @@ -1,4 +1,4 @@ -from typing import Any, Union +from typing import Any from pydantic import BaseModel, field_validator @@ -70,7 +70,7 @@ class GeneralStructureChunk(BaseModel): """ general_chunks: list[str] - data_source_info: Union[FileInfo, OnlineDocumentInfo, WebsiteInfo] + data_source_info: FileInfo | OnlineDocumentInfo | WebsiteInfo class ParentChildChunk(BaseModel): @@ -88,7 +88,7 @@ class ParentChildStructureChunk(BaseModel): """ parent_child_chunks: list[ParentChildChunk] - data_source_info: Union[FileInfo, OnlineDocumentInfo, WebsiteInfo] + data_source_info: FileInfo | OnlineDocumentInfo | WebsiteInfo class KnowledgeIndexNodeData(BaseNodeData): diff --git a/api/core/workflow/nodes/trigger_plugin/entities.py b/api/core/workflow/nodes/trigger_plugin/entities.py index 23ed2cd4088..b67ec3520f8 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, Union +from typing import Any, Literal from pydantic import BaseModel, Field, ValidationInfo, field_validator @@ -17,7 +17,7 @@ class TriggerEventNodeData(BaseNodeData): type: NodeType = TRIGGER_PLUGIN_NODE_TYPE class TriggerEventInput(BaseModel): - value: Union[Any, list[str]] + value: Any | list[str] type: Literal["mixed", "variable", "constant"] @field_validator("type", mode="before") diff --git a/api/core/workflow/nodes/trigger_schedule/entities.py b/api/core/workflow/nodes/trigger_schedule/entities.py index 683c8d420f2..79ed1179849 100644 --- a/api/core/workflow/nodes/trigger_schedule/entities.py +++ b/api/core/workflow/nodes/trigger_schedule/entities.py @@ -1,4 +1,4 @@ -from typing import Any, Literal, Union +from typing import Any, Literal from pydantic import BaseModel, Field @@ -47,6 +47,6 @@ class VisualConfig(BaseModel): ) # For monthly frequency - monthly_days: list[Union[int, Literal["last"]]] | None = Field( + monthly_days: list[int | Literal["last"]] | None = Field( default=None, description="Days of month to run on (1-31 or 'last')" ) diff --git a/api/extensions/ext_otel.py b/api/extensions/ext_otel.py index 3cc65040588..5ee6fed2601 100644 --- a/api/extensions/ext_otel.py +++ b/api/extensions/ext_otel.py @@ -3,7 +3,6 @@ import logging import os import platform import socket -from typing import Union from configs import dify_config from dify_app import DifyApp @@ -73,8 +72,8 @@ def init_app(app: DifyApp): provider = TracerProvider(resource=resource, sampler=sampler) set_tracer_provider(provider) - exporter: Union[GRPCSpanExporter, HTTPSpanExporter, ConsoleSpanExporter] - metric_exporter: Union[GRPCMetricExporter, HTTPMetricExporter, ConsoleMetricExporter] + exporter: GRPCSpanExporter | HTTPSpanExporter | ConsoleSpanExporter + metric_exporter: GRPCMetricExporter | HTTPMetricExporter | ConsoleMetricExporter protocol = (dify_config.OTEL_EXPORTER_OTLP_PROTOCOL or "").lower() if dify_config.OTEL_EXPORTER_TYPE == "otlp": if protocol == "grpc": diff --git a/api/extensions/ext_redis.py b/api/extensions/ext_redis.py index 8ff5db1643d..cc83efaa99e 100644 --- a/api/extensions/ext_redis.py +++ b/api/extensions/ext_redis.py @@ -5,7 +5,7 @@ import ssl import sys from collections.abc import Callable from datetime import timedelta -from typing import Any, Protocol, Union, cast, runtime_checkable +from typing import Any, Protocol, cast, runtime_checkable import redis from redis import RedisError @@ -67,12 +67,12 @@ class RedisClientWrapper: if the client is not initialized. """ - _client: Union[redis.Redis, RedisCluster, None] + _client: redis.Redis | RedisCluster | None def __init__(self) -> None: self._client = None - def initialize(self, client: Union[redis.Redis, RedisCluster]) -> None: + def initialize(self, client: redis.Redis | RedisCluster) -> None: if self._client is None: self._client = client @@ -299,7 +299,7 @@ class RedisBaseParamsDict(TypedDict): socket_keepalive_options: dict[int, int] -def _get_ssl_configuration() -> tuple[type[Union[Connection, SSLConnection]], dict[str, Any]]: +def _get_ssl_configuration() -> tuple[type[Connection | SSLConnection], dict[str, Any]]: """Get SSL configuration for Redis connection.""" if not dify_config.REDIS_USE_SSL: return Connection, {} @@ -416,7 +416,7 @@ def _parse_redis_nodes(value: str) -> list[tuple[str, int]]: return nodes -def _create_sentinel_client(redis_params: RedisBaseParamsDict) -> Union[redis.Redis, RedisCluster]: +def _create_sentinel_client(redis_params: RedisBaseParamsDict) -> redis.Redis | RedisCluster: """Create Redis client using Sentinel configuration.""" if not dify_config.REDIS_SENTINELS: raise ValueError("REDIS_SENTINELS must be set when REDIS_USE_SENTINEL is True") @@ -449,7 +449,7 @@ def _create_sentinel_client(redis_params: RedisBaseParamsDict) -> Union[redis.Re return master -def _create_cluster_client() -> Union[redis.Redis, RedisCluster]: +def _create_cluster_client() -> redis.Redis | RedisCluster: """Create Redis cluster client.""" if not dify_config.REDIS_CLUSTERS: raise ValueError("REDIS_CLUSTERS must be set when REDIS_USE_CLUSTERS is True") @@ -469,7 +469,7 @@ def _create_cluster_client() -> Union[redis.Redis, RedisCluster]: return cluster -def _create_standalone_client(redis_params: RedisBaseParamsDict) -> Union[redis.Redis, RedisCluster]: +def _create_standalone_client(redis_params: RedisBaseParamsDict) -> redis.Redis | RedisCluster: """Create standalone Redis client.""" connection_class, ssl_kwargs = _get_ssl_configuration() diff --git a/api/extensions/ext_storage.py b/api/extensions/ext_storage.py index bc8b83b268b..a59e7118d51 100644 --- a/api/extensions/ext_storage.py +++ b/api/extensions/ext_storage.py @@ -1,6 +1,6 @@ import logging from collections.abc import Callable, Generator -from typing import Literal, Union, overload +from typing import Literal, overload from flask import Flask @@ -96,9 +96,9 @@ class Storage: # Keep a bool fallback overload for callers that forward a runtime bool flag. @overload - def load(self, filename: str, /, *, stream: bool = False) -> Union[bytes, Generator]: ... + def load(self, filename: str, /, *, stream: bool = False) -> bytes | Generator: ... - def load(self, filename: str, /, *, stream: bool = False) -> Union[bytes, Generator]: + def load(self, filename: str, /, *, stream: bool = False) -> bytes | Generator: if stream: return self.load_stream(filename) else: diff --git a/api/libs/rsa.py b/api/libs/rsa.py index c401129b3a4..8e5fad7833a 100644 --- a/api/libs/rsa.py +++ b/api/libs/rsa.py @@ -16,7 +16,6 @@ This is enforced by the "no-direct-rsa-imports" contract in .importlinter (run v """ import hashlib -from typing import Union from Crypto.Cipher import AES from Crypto.PublicKey import RSA @@ -44,7 +43,7 @@ def generate_key_pair(tenant_id: str) -> str: prefix_hybrid = b"HYBRID:" -def encrypt(text: str, public_key: Union[str, bytes]) -> bytes: +def encrypt(text: str, public_key: str | bytes) -> bytes: if isinstance(public_key, str): public_key = public_key.encode() diff --git a/api/providers/trace/trace-arize-phoenix/src/dify_trace_arize_phoenix/arize_phoenix_trace.py b/api/providers/trace/trace-arize-phoenix/src/dify_trace_arize_phoenix/arize_phoenix_trace.py index 4199ec131a0..b4722eb720b 100644 --- a/api/providers/trace/trace-arize-phoenix/src/dify_trace_arize_phoenix/arize_phoenix_trace.py +++ b/api/providers/trace/trace-arize-phoenix/src/dify_trace_arize_phoenix/arize_phoenix_trace.py @@ -6,7 +6,7 @@ import traceback from collections.abc import Mapping, Sequence from dataclasses import dataclass, field from datetime import datetime, timedelta -from typing import Any, Protocol, Union, cast, override +from typing import Any, Protocol, cast, override from urllib.parse import urlparse from openinference.semconv.trace import ( @@ -188,7 +188,7 @@ def setup_tracer(arize_phoenix_config: ArizeConfig | PhoenixConfig) -> tuple[tra """Configure OpenTelemetry tracer with OTLP exporter for Arize/Phoenix.""" try: # Choose the appropriate exporter based on config type - exporter: Union[GrpcOTLPSpanExporter, HttpOTLPSpanExporter] + exporter: GrpcOTLPSpanExporter | HttpOTLPSpanExporter # Inspect the provided endpoint to determine its structure parsed = urlparse(arize_phoenix_config.endpoint) diff --git a/api/providers/trace/trace-langfuse/src/dify_trace_langfuse/entities/langfuse_trace_entity.py b/api/providers/trace/trace-langfuse/src/dify_trace_langfuse/entities/langfuse_trace_entity.py index 742938f09f4..5b1a01e4bab 100644 --- a/api/providers/trace/trace-langfuse/src/dify_trace_langfuse/entities/langfuse_trace_entity.py +++ b/api/providers/trace/trace-langfuse/src/dify_trace_langfuse/entities/langfuse_trace_entity.py @@ -1,7 +1,7 @@ from collections.abc import Mapping from datetime import datetime from enum import StrEnum -from typing import Any, Union +from typing import Any from pydantic import BaseModel, ConfigDict, Field, field_validator from pydantic_core.core_schema import ValidationInfo @@ -62,10 +62,10 @@ class LangfuseTrace(BaseModel): default=None, description="Identifier of the trace. Useful for sorting/filtering in the UI.", ) - input: Union[str, dict[str, Any], list, None] | None = Field( + input: str | dict[str, Any] | list | None | None = Field( default=None, description="The input of the trace. Can be any JSON object." ) - output: Union[str, dict[str, Any], list, None] | None = Field( + output: str | dict[str, Any] | list | None | None = Field( default=None, description="The output of the trace. Can be any JSON object." ) metadata: dict[str, Any] | None = Field( @@ -157,10 +157,10 @@ class LangfuseSpan(BaseModel): description="The status message of the span. Additional field for context of the event. E.g. the error " "message of an error event.", ) - input: Union[str, Mapping[str, Any], list, None] | None = Field( + input: str | Mapping[str, Any] | list | None | None = Field( default=None, description="The input of the span. Can be any JSON object." ) - output: Union[str, Mapping[str, Any], list, None] | None = Field( + output: str | Mapping[str, Any] | list | None | None = Field( default=None, description="The output of the span. Can be any JSON object." ) version: str | None = Field( diff --git a/api/providers/trace/trace-langsmith/src/dify_trace_langsmith/entities/langsmith_trace_entity.py b/api/providers/trace/trace-langsmith/src/dify_trace_langsmith/entities/langsmith_trace_entity.py index 07159d8a7e3..cdbedaf7399 100644 --- a/api/providers/trace/trace-langsmith/src/dify_trace_langsmith/entities/langsmith_trace_entity.py +++ b/api/providers/trace/trace-langsmith/src/dify_trace_langsmith/entities/langsmith_trace_entity.py @@ -1,7 +1,7 @@ from collections.abc import Mapping from datetime import datetime from enum import StrEnum -from typing import Any, Union +from typing import Any from pydantic import BaseModel, Field, field_validator from pydantic_core.core_schema import ValidationInfo @@ -31,8 +31,8 @@ class LangSmithMultiModel(BaseModel): class LangSmithRunModel(LangSmithTokenUsage, LangSmithMultiModel): name: str | None = Field(..., description="Name of the run") - inputs: Union[str, Mapping[str, Any], list, None] | None = Field(None, description="Inputs of the run") - outputs: Union[str, Mapping[str, Any], list, None] | None = Field(None, description="Outputs of the run") + inputs: str | Mapping[str, Any] | list | None | None = Field(None, description="Inputs of the run") + outputs: str | Mapping[str, Any] | list | None | None = Field(None, description="Outputs of the run") run_type: LangSmithRunType = Field(..., description="Type of the run") start_time: datetime | str | None = Field(None, description="Start time of the run") end_time: datetime | str | None = Field(None, description="End time of the run") diff --git a/api/providers/trace/trace-weave/src/dify_trace_weave/entities/weave_trace_entity.py b/api/providers/trace/trace-weave/src/dify_trace_weave/entities/weave_trace_entity.py index 98180c80a2c..f8a3e3723ce 100644 --- a/api/providers/trace/trace-weave/src/dify_trace_weave/entities/weave_trace_entity.py +++ b/api/providers/trace/trace-weave/src/dify_trace_weave/entities/weave_trace_entity.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from typing import Any, Union +from typing import Any from pydantic import BaseModel, Field, field_validator from pydantic_core.core_schema import ValidationInfo @@ -20,9 +20,9 @@ class WeaveMultiModel(BaseModel): class WeaveTraceModel(WeaveTokenUsage, WeaveMultiModel): id: str = Field(..., description="ID of the trace") op: str = Field(..., description="Name of the operation") - inputs: Union[str, Mapping[str, Any], list, None] | None = Field(None, description="Inputs of the trace") - outputs: Union[str, Mapping[str, Any], list, None] | None = Field(None, description="Outputs of the trace") - attributes: Union[str, dict[str, Any], list, None] | None = Field( + inputs: str | Mapping[str, Any] | list | None | None = Field(None, description="Inputs of the trace") + outputs: str | Mapping[str, Any] | list | None | None = Field(None, description="Outputs of the trace") + attributes: str | dict[str, Any] | list | None | None = Field( None, description="Metadata and attributes associated with trace" ) exception: str | None = Field(None, description="Exception message of the trace") diff --git a/api/providers/vdb/vdb-tencent/tests/integration_tests/conftest.py b/api/providers/vdb/vdb-tencent/tests/integration_tests/conftest.py index 8f87d6a073b..39b20e8913e 100644 --- a/api/providers/vdb/vdb-tencent/tests/integration_tests/conftest.py +++ b/api/providers/vdb/vdb-tencent/tests/integration_tests/conftest.py @@ -1,5 +1,5 @@ import os -from typing import Any, Union +from typing import Any import pytest from _pytest.monkeypatch import MonkeyPatch @@ -100,7 +100,7 @@ class MockTcvectordbClass: self, database_name: str, collection_name: str, - documents: list[Union[Document, dict]], + documents: list[Document | dict], timeout: float | None = None, build_index: bool = True, **kwargs, @@ -125,9 +125,9 @@ class MockTcvectordbClass: self, database_name: str, collection_name: str, - ann: Union[list[AnnSearch], AnnSearch] | None = None, - match: Union[list[KeywordSearch], KeywordSearch] | None = None, - filter: Union[Filter, str] | None = None, + ann: list[AnnSearch] | AnnSearch | None = None, + match: list[KeywordSearch] | KeywordSearch | None = None, + filter: Filter | str | None = None, rerank: Rerank | None = None, retrieve_vector: bool | None = None, output_fields: list[str] | None = None, diff --git a/api/providers/vdb/vdb-vikingdb/tests/integration_tests/conftest.py b/api/providers/vdb/vdb-vikingdb/tests/integration_tests/conftest.py index 289c515b858..34ddb94cc13 100644 --- a/api/providers/vdb/vdb-vikingdb/tests/integration_tests/conftest.py +++ b/api/providers/vdb/vdb-vikingdb/tests/integration_tests/conftest.py @@ -1,5 +1,4 @@ import os -from typing import Union from unittest.mock import MagicMock import pytest @@ -120,10 +119,10 @@ class MockVikingDBClass: assert collection_name != "" assert index_name != "" - def upsert_data(self, data: Union[Data, list[Data]]): + def upsert_data(self, data: Data | list[Data]): assert data is not None - def fetch_data(self, id: Union[str, list[str], int, list[int]]): + def fetch_data(self, id: str | list[str] | int | list[int]): return Data( fields={ vdb_Field.GROUP_KEY: "test_group", @@ -135,7 +134,7 @@ class MockVikingDBClass: id=id, ) - def delete_data(self, id: Union[str, list[str], int, list[int]]): + def delete_data(self, id: str | list[str] | int | list[int]): assert id is not None def search_by_vector( diff --git a/api/services/entities/external_knowledge_entities/external_knowledge_entities.py b/api/services/entities/external_knowledge_entities/external_knowledge_entities.py index be4bd467731..ffb22b59ed0 100644 --- a/api/services/entities/external_knowledge_entities/external_knowledge_entities.py +++ b/api/services/entities/external_knowledge_entities/external_knowledge_entities.py @@ -1,12 +1,12 @@ -from typing import Any, Literal, Union +from typing import Any, Literal from pydantic import BaseModel, Field class AuthorizationConfig(BaseModel): type: Literal[None, "basic", "bearer", "custom"] - api_key: Union[None, str] = None - header: Union[None, str] = None + api_key: None | str = None + header: None | str = None class Authorization(BaseModel): diff --git a/api/services/snippet_generate_service.py b/api/services/snippet_generate_service.py index eda98e892e5..86177f0112f 100644 --- a/api/services/snippet_generate_service.py +++ b/api/services/snippet_generate_service.py @@ -21,7 +21,7 @@ Supported execution modes: import json import logging from collections.abc import Generator, Mapping, Sequence -from typing import Any, Union, cast +from typing import Any, cast from sqlalchemy.orm import Session, make_transient, sessionmaker @@ -131,7 +131,7 @@ class SnippetGenerateService: def generate( cls, snippet: CustomizedSnippet, - user: Union[Account, EndUser], + user: Account | EndUser, args: Mapping[str, Any], invoke_from: InvokeFrom, streaming: bool = True, @@ -183,7 +183,7 @@ class SnippetGenerateService: def run_published( cls, snippet: CustomizedSnippet, - user: Union[Account, EndUser], + user: Account | EndUser, args: Mapping[str, Any], invoke_from: InvokeFrom, session_maker: sessionmaker[Session] | None = None, @@ -372,7 +372,7 @@ class SnippetGenerateService: def generate_single_iteration( cls, snippet: CustomizedSnippet, - user: Union[Account, EndUser], + user: Account | EndUser, node_id: str, args: Mapping[str, Any], streaming: bool = True, @@ -419,7 +419,7 @@ class SnippetGenerateService: def generate_single_loop( cls, snippet: CustomizedSnippet, - user: Union[Account, EndUser], + user: Account | EndUser, node_id: str, args: Any, streaming: bool = True, diff --git a/api/tasks/process_tenant_plugin_autoupgrade_check_task.py b/api/tasks/process_tenant_plugin_autoupgrade_check_task.py index d50dc6b69ef..08846a2d48e 100644 --- a/api/tasks/process_tenant_plugin_autoupgrade_check_task.py +++ b/api/tasks/process_tenant_plugin_autoupgrade_check_task.py @@ -1,7 +1,6 @@ import json import logging import operator -import typing import click from celery import shared_task @@ -27,7 +26,7 @@ CACHE_REDIS_KEY_PREFIX = "plugin_autoupgrade_check_task:cached_plugin_snapshot:" CACHE_REDIS_TTL = 60 * 60 # 1 hour # A cached miss is stored as JSON null, so None has to stay a valid payload. -_CACHED_MANIFEST_ADAPTER = TypeAdapter(typing.Union[MarketplacePluginSnapshot, None]) +_CACHED_MANIFEST_ADAPTER = TypeAdapter(MarketplacePluginSnapshot | None) def _get_redis_cache_key(plugin_id: str) -> str: @@ -35,7 +34,7 @@ def _get_redis_cache_key(plugin_id: str) -> str: return f"{CACHE_REDIS_KEY_PREFIX}{plugin_id}" -def _get_cached_manifest(plugin_id: str) -> typing.Union[MarketplacePluginSnapshot, None, bool]: +def _get_cached_manifest(plugin_id: str) -> MarketplacePluginSnapshot | None | bool: """ Get cached plugin manifest from Redis. Returns: diff --git a/api/tests/unit_tests/core/mcp/session/test_base_session.py b/api/tests/unit_tests/core/mcp/session/test_base_session.py index f550ceef5fc..c4f57e60e99 100644 --- a/api/tests/unit_tests/core/mcp/session/test_base_session.py +++ b/api/tests/unit_tests/core/mcp/session/test_base_session.py @@ -4,7 +4,6 @@ import queue import time from concurrent.futures import Future, ThreadPoolExecutor from datetime import timedelta -from typing import Union from unittest.mock import MagicMock, patch import pytest @@ -54,11 +53,11 @@ class MockNotification(Notification[MockNotificationParams, str]): params: MockNotificationParams -class ReceiveRequest(RootModel[Union[MockRequest, ClientRequest]]): +class ReceiveRequest(RootModel[MockRequest | ClientRequest]): pass -class ReceiveNotification(RootModel[Union[CancelledNotification, MockNotification, JSONRPCNotification]]): +class ReceiveNotification(RootModel[CancelledNotification | MockNotification | JSONRPCNotification]): pass