From b9f1d65d4fb601bf5b80eb9bb7df9e11b38fcc95 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Mon, 26 Jan 2026 11:23:38 +0900 Subject: [PATCH] refactor: example of refine dict / Mapping (#31498) --- api/controllers/console/app/workflow.py | 6 ++++-- api/core/app/apps/advanced_chat/app_generator.py | 13 +++++++++---- api/core/app/apps/workflow/app_generator.py | 13 +++++++++---- api/services/app_generate_service.py | 11 +++++++++-- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index b4f2ef0ba8..acaf85a6b1 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -470,7 +470,7 @@ class AdvancedChatDraftRunLoopNodeApi(Resource): Run draft workflow loop node """ current_user, _ = current_account_with_tenant() - args = LoopNodeRunPayload.model_validate(console_ns.payload or {}).model_dump(exclude_none=True) + args = LoopNodeRunPayload.model_validate(console_ns.payload or {}) try: response = AppGenerateService.generate_single_loop( @@ -508,7 +508,7 @@ class WorkflowDraftRunLoopNodeApi(Resource): Run draft workflow loop node """ current_user, _ = current_account_with_tenant() - args = LoopNodeRunPayload.model_validate(console_ns.payload or {}).model_dump(exclude_none=True) + args = LoopNodeRunPayload.model_validate(console_ns.payload or {}) try: response = AppGenerateService.generate_single_loop( @@ -999,6 +999,7 @@ class DraftWorkflowTriggerRunApi(Resource): if not event: return jsonable_encoder({"status": "waiting", "retry_in": LISTENING_RETRY_IN}) workflow_args = dict(event.workflow_args) + workflow_args[SKIP_PREPARE_USER_INPUTS_KEY] = True return helper.compact_generate_response( AppGenerateService.generate( @@ -1147,6 +1148,7 @@ class DraftWorkflowTriggerRunAllApi(Resource): try: workflow_args = dict(trigger_debug_event.workflow_args) + workflow_args[SKIP_PREPARE_USER_INPUTS_KEY] = True response = AppGenerateService.generate( app_model=app_model, diff --git a/api/core/app/apps/advanced_chat/app_generator.py b/api/core/app/apps/advanced_chat/app_generator.py index feb0d3358c..528c45f6c8 100644 --- a/api/core/app/apps/advanced_chat/app_generator.py +++ b/api/core/app/apps/advanced_chat/app_generator.py @@ -1,9 +1,11 @@ +from __future__ import annotations + import contextvars import logging import threading import uuid from collections.abc import Generator, Mapping -from typing import Any, Literal, Union, overload +from typing import TYPE_CHECKING, Any, Literal, Union, overload from flask import Flask, current_app from pydantic import ValidationError @@ -13,6 +15,9 @@ from sqlalchemy.orm import Session, sessionmaker import contexts from configs import dify_config from constants import UUID_NIL + +if TYPE_CHECKING: + from controllers.console.app.workflow import LoopNodeRunPayload from core.app.app_config.features.file_upload.manager import FileUploadConfigManager from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager from core.app.apps.advanced_chat.app_runner import AdvancedChatAppRunner @@ -304,7 +309,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): workflow: Workflow, node_id: str, user: Account | EndUser, - args: Mapping, + args: LoopNodeRunPayload, streaming: bool = True, ) -> Mapping[str, Any] | Generator[str | Mapping[str, Any], Any, None]: """ @@ -320,7 +325,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): if not node_id: raise ValueError("node_id is required") - if args.get("inputs") is None: + if args.inputs is None: raise ValueError("inputs is required") # convert to app config @@ -338,7 +343,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): stream=streaming, invoke_from=InvokeFrom.DEBUGGER, extras={"auto_generate_conversation_name": False}, - single_loop_run=AdvancedChatAppGenerateEntity.SingleLoopRunEntity(node_id=node_id, inputs=args["inputs"]), + single_loop_run=AdvancedChatAppGenerateEntity.SingleLoopRunEntity(node_id=node_id, inputs=args.inputs), ) contexts.plugin_tool_providers.set({}) contexts.plugin_tool_providers_lock.set(threading.Lock()) diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py index 2be773f103..ee205ed153 100644 --- a/api/core/app/apps/workflow/app_generator.py +++ b/api/core/app/apps/workflow/app_generator.py @@ -1,9 +1,11 @@ +from __future__ import annotations + import contextvars import logging import threading import uuid from collections.abc import Generator, Mapping, Sequence -from typing import Any, Literal, Union, overload +from typing import TYPE_CHECKING, Any, Literal, Union, overload from flask import Flask, current_app from pydantic import ValidationError @@ -40,6 +42,9 @@ from models import Account, App, EndUser, Workflow, WorkflowNodeExecutionTrigger from models.enums import WorkflowRunTriggeredFrom from services.workflow_draft_variable_service import DraftVarLoader, WorkflowDraftVariableService +if TYPE_CHECKING: + from controllers.console.app.workflow import LoopNodeRunPayload + SKIP_PREPARE_USER_INPUTS_KEY = "_skip_prepare_user_inputs" logger = logging.getLogger(__name__) @@ -381,7 +386,7 @@ class WorkflowAppGenerator(BaseAppGenerator): workflow: Workflow, node_id: str, user: Account | EndUser, - args: Mapping[str, Any], + args: LoopNodeRunPayload, streaming: bool = True, ) -> Mapping[str, Any] | Generator[str | Mapping[str, Any], None, None]: """ @@ -397,7 +402,7 @@ class WorkflowAppGenerator(BaseAppGenerator): if not node_id: raise ValueError("node_id is required") - if args.get("inputs") is None: + if args.inputs is None: raise ValueError("inputs is required") # convert to app config @@ -413,7 +418,7 @@ class WorkflowAppGenerator(BaseAppGenerator): stream=streaming, invoke_from=InvokeFrom.DEBUGGER, extras={"auto_generate_conversation_name": False}, - single_loop_run=WorkflowAppGenerateEntity.SingleLoopRunEntity(node_id=node_id, inputs=args["inputs"]), + single_loop_run=WorkflowAppGenerateEntity.SingleLoopRunEntity(node_id=node_id, inputs=args.inputs or {}), workflow_execution_id=str(uuid.uuid4()), ) contexts.plugin_tool_providers.set({}) diff --git a/api/services/app_generate_service.py b/api/services/app_generate_service.py index cc58899dc4..ce85f2e914 100644 --- a/api/services/app_generate_service.py +++ b/api/services/app_generate_service.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import uuid from collections.abc import Generator, Mapping -from typing import Any, Union +from typing import TYPE_CHECKING, Any, Union from configs import dify_config from core.app.apps.advanced_chat.app_generator import AdvancedChatAppGenerator @@ -18,6 +20,9 @@ from services.errors.app import QuotaExceededError, WorkflowIdFormatError, Workf from services.errors.llm import InvokeRateLimitError from services.workflow_service import WorkflowService +if TYPE_CHECKING: + from controllers.console.app.workflow import LoopNodeRunPayload + class AppGenerateService: @classmethod @@ -165,7 +170,9 @@ class AppGenerateService: raise ValueError(f"Invalid app mode {app_model.mode}") @classmethod - def generate_single_loop(cls, app_model: App, user: Account, node_id: str, args: Any, streaming: bool = True): + def generate_single_loop( + cls, app_model: App, user: Account, node_id: str, args: LoopNodeRunPayload, streaming: bool = True + ): if app_model.mode == AppMode.ADVANCED_CHAT: workflow = cls._get_workflow(app_model, InvokeFrom.DEBUGGER) return AdvancedChatAppGenerator.convert_to_event_stream(