diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index bbbe1e9ec8..0967d09e9c 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -4,13 +4,13 @@ from collections.abc import Sequence from typing import cast from flask import abort, request -from flask_restx import Resource, fields, inputs, marshal_with, reqparse +from flask_restx import Resource, inputs, marshal_with, reqparse from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import services from configs import dify_config -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.app.error import ConversationCompletedError, DraftWorkflowNotExist, DraftWorkflowNotSync from controllers.console.app.wraps import get_app_model from controllers.console.wraps import account_initialization_required, setup_required @@ -57,13 +57,7 @@ def _parse_file(workflow: Workflow, files: list[dict] | None = None) -> Sequence return file_objs -@console_ns.route("/apps//workflows/draft") class DraftWorkflowApi(Resource): - @api.doc("get_draft_workflow") - @api.doc(description="Get draft workflow for an application") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Draft workflow retrieved successfully", workflow_fields) - @api.response(404, "Draft workflow not found") @setup_required @login_required @account_initialization_required @@ -92,23 +86,6 @@ class DraftWorkflowApi(Resource): @login_required @account_initialization_required @get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]) - @api.doc("sync_draft_workflow") - @api.doc(description="Sync draft workflow configuration") - @api.expect( - api.model( - "SyncDraftWorkflowRequest", - { - "graph": fields.Raw(required=True, description="Workflow graph configuration"), - "features": fields.Raw(required=True, description="Workflow features configuration"), - "hash": fields.String(description="Workflow hash for validation"), - "environment_variables": fields.List(fields.Raw, required=True, description="Environment variables"), - "conversation_variables": fields.List(fields.Raw, description="Conversation variables"), - }, - ) - ) - @api.response(200, "Draft workflow synced successfully", workflow_fields) - @api.response(400, "Invalid workflow configuration") - @api.response(403, "Permission denied") def post(self, app_model: App): """ Sync draft workflow @@ -182,25 +159,7 @@ class DraftWorkflowApi(Resource): } -@console_ns.route("/apps//advanced-chat/workflows/draft/run") class AdvancedChatDraftWorkflowRunApi(Resource): - @api.doc("run_advanced_chat_draft_workflow") - @api.doc(description="Run draft workflow for advanced chat application") - @api.doc(params={"app_id": "Application ID"}) - @api.expect( - api.model( - "AdvancedChatWorkflowRunRequest", - { - "query": fields.String(required=True, description="User query"), - "inputs": fields.Raw(description="Input variables"), - "files": fields.List(fields.Raw, description="File uploads"), - "conversation_id": fields.String(description="Conversation ID"), - }, - ) - ) - @api.response(200, "Workflow run started successfully") - @api.response(400, "Invalid request parameters") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -249,23 +208,7 @@ class AdvancedChatDraftWorkflowRunApi(Resource): raise InternalServerError() -@console_ns.route("/apps//advanced-chat/workflows/draft/iteration/nodes//run") class AdvancedChatDraftRunIterationNodeApi(Resource): - @api.doc("run_advanced_chat_draft_iteration_node") - @api.doc(description="Run draft workflow iteration node for advanced chat") - @api.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @api.expect( - api.model( - "IterationNodeRunRequest", - { - "task_id": fields.String(required=True, description="Task ID"), - "inputs": fields.Raw(description="Input variables"), - }, - ) - ) - @api.response(200, "Iteration node run started successfully") - @api.response(403, "Permission denied") - @api.response(404, "Node not found") @setup_required @login_required @account_initialization_required @@ -301,23 +244,7 @@ class AdvancedChatDraftRunIterationNodeApi(Resource): raise InternalServerError() -@console_ns.route("/apps//workflows/draft/iteration/nodes//run") class WorkflowDraftRunIterationNodeApi(Resource): - @api.doc("run_workflow_draft_iteration_node") - @api.doc(description="Run draft workflow iteration node") - @api.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @api.expect( - api.model( - "WorkflowIterationNodeRunRequest", - { - "task_id": fields.String(required=True, description="Task ID"), - "inputs": fields.Raw(description="Input variables"), - }, - ) - ) - @api.response(200, "Workflow iteration node run started successfully") - @api.response(403, "Permission denied") - @api.response(404, "Node not found") @setup_required @login_required @account_initialization_required @@ -353,23 +280,7 @@ class WorkflowDraftRunIterationNodeApi(Resource): raise InternalServerError() -@console_ns.route("/apps//advanced-chat/workflows/draft/loop/nodes//run") class AdvancedChatDraftRunLoopNodeApi(Resource): - @api.doc("run_advanced_chat_draft_loop_node") - @api.doc(description="Run draft workflow loop node for advanced chat") - @api.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @api.expect( - api.model( - "LoopNodeRunRequest", - { - "task_id": fields.String(required=True, description="Task ID"), - "inputs": fields.Raw(description="Input variables"), - }, - ) - ) - @api.response(200, "Loop node run started successfully") - @api.response(403, "Permission denied") - @api.response(404, "Node not found") @setup_required @login_required @account_initialization_required @@ -406,23 +317,7 @@ class AdvancedChatDraftRunLoopNodeApi(Resource): raise InternalServerError() -@console_ns.route("/apps//workflows/draft/loop/nodes//run") class WorkflowDraftRunLoopNodeApi(Resource): - @api.doc("run_workflow_draft_loop_node") - @api.doc(description="Run draft workflow loop node") - @api.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @api.expect( - api.model( - "WorkflowLoopNodeRunRequest", - { - "task_id": fields.String(required=True, description="Task ID"), - "inputs": fields.Raw(description="Input variables"), - }, - ) - ) - @api.response(200, "Workflow loop node run started successfully") - @api.response(403, "Permission denied") - @api.response(404, "Node not found") @setup_required @login_required @account_initialization_required @@ -459,22 +354,7 @@ class WorkflowDraftRunLoopNodeApi(Resource): raise InternalServerError() -@console_ns.route("/apps//workflows/draft/run") class DraftWorkflowRunApi(Resource): - @api.doc("run_draft_workflow") - @api.doc(description="Run draft workflow") - @api.doc(params={"app_id": "Application ID"}) - @api.expect( - api.model( - "DraftWorkflowRunRequest", - { - "inputs": fields.Raw(required=True, description="Input variables"), - "files": fields.List(fields.Raw, description="File uploads"), - }, - ) - ) - @api.response(200, "Draft workflow run started successfully") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -513,14 +393,7 @@ class DraftWorkflowRunApi(Resource): raise InvokeRateLimitHttpError(ex.description) -@console_ns.route("/apps//workflows/tasks//stop") class WorkflowTaskStopApi(Resource): - @api.doc("stop_workflow_task") - @api.doc(description="Stop running workflow task") - @api.doc(params={"app_id": "Application ID", "task_id": "Task ID"}) - @api.response(200, "Task stopped successfully") - @api.response(404, "Task not found") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -541,22 +414,7 @@ class WorkflowTaskStopApi(Resource): return {"result": "success"} -@console_ns.route("/apps//workflows/draft/nodes//run") class DraftWorkflowNodeRunApi(Resource): - @api.doc("run_draft_workflow_node") - @api.doc(description="Run draft workflow node") - @api.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @api.expect( - api.model( - "DraftWorkflowNodeRunRequest", - { - "inputs": fields.Raw(description="Input variables"), - }, - ) - ) - @api.response(200, "Node run started successfully", workflow_run_node_execution_fields) - @api.response(403, "Permission denied") - @api.response(404, "Node not found") @setup_required @login_required @account_initialization_required @@ -604,13 +462,7 @@ class DraftWorkflowNodeRunApi(Resource): return workflow_node_execution -@console_ns.route("/apps//workflows/publish") class PublishedWorkflowApi(Resource): - @api.doc("get_published_workflow") - @api.doc(description="Get published workflow for an application") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Published workflow retrieved successfully", workflow_fields) - @api.response(404, "Published workflow not found") @setup_required @login_required @account_initialization_required @@ -682,12 +534,7 @@ class PublishedWorkflowApi(Resource): } -@console_ns.route("/apps//workflows/default-block-configs") class DefaultBlockConfigsApi(Resource): - @api.doc("get_default_block_configs") - @api.doc(description="Get default block configurations for workflow") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Default block configurations retrieved successfully") @setup_required @login_required @account_initialization_required @@ -708,13 +555,7 @@ class DefaultBlockConfigsApi(Resource): return workflow_service.get_default_block_configs() -@console_ns.route("/apps//workflows/default-block-configs/") class DefaultBlockConfigApi(Resource): - @api.doc("get_default_block_config") - @api.doc(description="Get default block configuration by type") - @api.doc(params={"app_id": "Application ID", "block_type": "Block type"}) - @api.response(200, "Default block configuration retrieved successfully") - @api.response(404, "Block type not found") @setup_required @login_required @account_initialization_required @@ -747,14 +588,7 @@ class DefaultBlockConfigApi(Resource): return workflow_service.get_default_block_config(node_type=block_type, filters=filters) -@console_ns.route("/apps//convert-to-workflow") class ConvertToWorkflowApi(Resource): - @api.doc("convert_to_workflow") - @api.doc(description="Convert application to workflow mode") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Application converted to workflow successfully") - @api.response(400, "Application cannot be converted") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -791,14 +625,9 @@ class ConvertToWorkflowApi(Resource): } -@console_ns.route("/apps//workflows/config") class WorkflowConfigApi(Resource): """Resource for workflow configuration.""" - @api.doc("get_workflow_config") - @api.doc(description="Get workflow configuration") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Workflow configuration retrieved successfully") @setup_required @login_required @account_initialization_required @@ -809,12 +638,7 @@ class WorkflowConfigApi(Resource): } -@console_ns.route("/apps//workflows/published") class PublishedAllWorkflowApi(Resource): - @api.doc("get_all_published_workflows") - @api.doc(description="Get all published workflows for an application") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Published workflows retrieved successfully", workflow_pagination_fields) @setup_required @login_required @account_initialization_required @@ -865,23 +689,7 @@ class PublishedAllWorkflowApi(Resource): } -@console_ns.route("/apps//workflows/") class WorkflowByIdApi(Resource): - @api.doc("update_workflow_by_id") - @api.doc(description="Update workflow by ID") - @api.doc(params={"app_id": "Application ID", "workflow_id": "Workflow ID"}) - @api.expect( - api.model( - "UpdateWorkflowRequest", - { - "environment_variables": fields.List(fields.Raw, description="Environment variables"), - "conversation_variables": fields.List(fields.Raw, description="Conversation variables"), - }, - ) - ) - @api.response(200, "Workflow updated successfully", workflow_fields) - @api.response(404, "Workflow not found") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -972,14 +780,7 @@ class WorkflowByIdApi(Resource): return None, 204 -@console_ns.route("/apps//workflows/draft/nodes//last-run") class DraftWorkflowNodeLastRunApi(Resource): - @api.doc("get_draft_workflow_node_last_run") - @api.doc(description="Get last run result for draft workflow node") - @api.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @api.response(200, "Node last run retrieved successfully", workflow_run_node_execution_fields) - @api.response(404, "Node last run not found") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -998,3 +799,73 @@ class DraftWorkflowNodeLastRunApi(Resource): if node_exec is None: raise NotFound("last run not found") return node_exec + + +api.add_resource( + DraftWorkflowApi, + "/apps//workflows/draft", +) +api.add_resource( + WorkflowConfigApi, + "/apps//workflows/draft/config", +) +api.add_resource( + AdvancedChatDraftWorkflowRunApi, + "/apps//advanced-chat/workflows/draft/run", +) +api.add_resource( + DraftWorkflowRunApi, + "/apps//workflows/draft/run", +) +api.add_resource( + WorkflowTaskStopApi, + "/apps//workflow-runs/tasks//stop", +) +api.add_resource( + DraftWorkflowNodeRunApi, + "/apps//workflows/draft/nodes//run", +) +api.add_resource( + AdvancedChatDraftRunIterationNodeApi, + "/apps//advanced-chat/workflows/draft/iteration/nodes//run", +) +api.add_resource( + WorkflowDraftRunIterationNodeApi, + "/apps//workflows/draft/iteration/nodes//run", +) +api.add_resource( + AdvancedChatDraftRunLoopNodeApi, + "/apps//advanced-chat/workflows/draft/loop/nodes//run", +) +api.add_resource( + WorkflowDraftRunLoopNodeApi, + "/apps//workflows/draft/loop/nodes//run", +) +api.add_resource( + PublishedWorkflowApi, + "/apps//workflows/publish", +) +api.add_resource( + PublishedAllWorkflowApi, + "/apps//workflows", +) +api.add_resource( + DefaultBlockConfigsApi, + "/apps//workflows/default-workflow-block-configs", +) +api.add_resource( + DefaultBlockConfigApi, + "/apps//workflows/default-workflow-block-configs/", +) +api.add_resource( + ConvertToWorkflowApi, + "/apps//convert-to-workflow", +) +api.add_resource( + WorkflowByIdApi, + "/apps//workflows/", +) +api.add_resource( + DraftWorkflowNodeLastRunApi, + "/apps//workflows/draft/nodes//last-run", +) diff --git a/api/controllers/console/app/workflow_app_log.py b/api/controllers/console/app/workflow_app_log.py index eb64faf6a5..76f02041ef 100644 --- a/api/controllers/console/app/workflow_app_log.py +++ b/api/controllers/console/app/workflow_app_log.py @@ -3,7 +3,7 @@ from flask_restx import Resource, marshal_with, reqparse from flask_restx.inputs import int_range from sqlalchemy.orm import Session -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.app.wraps import get_app_model from controllers.console.wraps import account_initialization_required, setup_required from core.workflow.entities.workflow_execution import WorkflowExecutionStatus @@ -15,24 +15,7 @@ from models.model import AppMode from services.workflow_app_service import WorkflowAppService -@console_ns.route("/apps//workflow-app-logs") class WorkflowAppLogApi(Resource): - @api.doc("get_workflow_app_logs") - @api.doc(description="Get workflow application execution logs") - @api.doc(params={"app_id": "Application ID"}) - @api.doc( - params={ - "keyword": "Search keyword for filtering logs", - "status": "Filter by execution status (succeeded, failed, stopped, partial-succeeded)", - "created_at__before": "Filter logs created before this timestamp", - "created_at__after": "Filter logs created after this timestamp", - "created_by_end_user_session_id": "Filter by end user session ID", - "created_by_account": "Filter by account", - "page": "Page number (1-99999)", - "limit": "Number of items per page (1-100)", - } - ) - @api.response(200, "Workflow app logs retrieved successfully", workflow_app_log_pagination_fields) @setup_required @login_required @account_initialization_required @@ -95,3 +78,6 @@ class WorkflowAppLogApi(Resource): ) return workflow_app_log_pagination + + +api.add_resource(WorkflowAppLogApi, "/apps//workflow-app-logs") diff --git a/api/controllers/console/app/workflow_draft_variable.py b/api/controllers/console/app/workflow_draft_variable.py index eff25eb2e5..bdcd79f339 100644 --- a/api/controllers/console/app/workflow_draft_variable.py +++ b/api/controllers/console/app/workflow_draft_variable.py @@ -6,7 +6,7 @@ from flask_restx import Resource, fields, inputs, marshal, marshal_with, reqpars from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.app.error import ( DraftWorkflowNotExist, ) @@ -144,13 +144,7 @@ def _api_prerequisite(f): return wrapper -@console_ns.route("/apps//workflows/draft/variables") class WorkflowVariableCollectionApi(Resource): - @api.doc("get_workflow_variables") - @api.doc(description="Get draft workflow variables") - @api.doc(params={"app_id": "Application ID"}) - @api.doc(params={"page": "Page number (1-100000)", "limit": "Number of items per page (1-100)"}) - @api.response(200, "Workflow variables retrieved successfully", _WORKFLOW_DRAFT_VARIABLE_LIST_WITHOUT_VALUE_FIELDS) @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_LIST_WITHOUT_VALUE_FIELDS) def get(self, app_model: App): @@ -179,9 +173,6 @@ class WorkflowVariableCollectionApi(Resource): return workflow_vars - @api.doc("delete_workflow_variables") - @api.doc(description="Delete all draft workflow variables") - @api.response(204, "Workflow variables deleted successfully") @_api_prerequisite def delete(self, app_model: App): draft_var_srv = WorkflowDraftVariableService( @@ -210,12 +201,7 @@ def validate_node_id(node_id: str) -> NoReturn | None: return None -@console_ns.route("/apps//workflows/draft/nodes//variables") class NodeVariableCollectionApi(Resource): - @api.doc("get_node_variables") - @api.doc(description="Get variables for a specific node") - @api.doc(params={"app_id": "Application ID", "node_id": "Node ID"}) - @api.response(200, "Node variables retrieved successfully", _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) def get(self, app_model: App, node_id: str): @@ -228,9 +214,6 @@ class NodeVariableCollectionApi(Resource): return node_vars - @api.doc("delete_node_variables") - @api.doc(description="Delete all variables for a specific node") - @api.response(204, "Node variables deleted successfully") @_api_prerequisite def delete(self, app_model: App, node_id: str): validate_node_id(node_id) @@ -240,16 +223,10 @@ class NodeVariableCollectionApi(Resource): return Response("", 204) -@console_ns.route("/apps//workflows/draft/variables/") class VariableApi(Resource): _PATCH_NAME_FIELD = "name" _PATCH_VALUE_FIELD = "value" - @api.doc("get_variable") - @api.doc(description="Get a specific workflow variable") - @api.doc(params={"app_id": "Application ID", "variable_id": "Variable ID"}) - @api.response(200, "Variable retrieved successfully", _WORKFLOW_DRAFT_VARIABLE_FIELDS) - @api.response(404, "Variable not found") @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_FIELDS) def get(self, app_model: App, variable_id: str): @@ -263,19 +240,6 @@ class VariableApi(Resource): raise NotFoundError(description=f"variable not found, id={variable_id}") return variable - @api.doc("update_variable") - @api.doc(description="Update a workflow variable") - @api.expect( - api.model( - "UpdateVariableRequest", - { - "name": fields.String(description="Variable name"), - "value": fields.Raw(description="Variable value"), - }, - ) - ) - @api.response(200, "Variable updated successfully", _WORKFLOW_DRAFT_VARIABLE_FIELDS) - @api.response(404, "Variable not found") @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_FIELDS) def patch(self, app_model: App, variable_id: str): @@ -338,10 +302,6 @@ class VariableApi(Resource): db.session.commit() return variable - @api.doc("delete_variable") - @api.doc(description="Delete a workflow variable") - @api.response(204, "Variable deleted successfully") - @api.response(404, "Variable not found") @_api_prerequisite def delete(self, app_model: App, variable_id: str): draft_var_srv = WorkflowDraftVariableService( @@ -357,14 +317,7 @@ class VariableApi(Resource): return Response("", 204) -@console_ns.route("/apps//workflows/draft/variables//reset") class VariableResetApi(Resource): - @api.doc("reset_variable") - @api.doc(description="Reset a workflow variable to its default value") - @api.doc(params={"app_id": "Application ID", "variable_id": "Variable ID"}) - @api.response(200, "Variable reset successfully", _WORKFLOW_DRAFT_VARIABLE_FIELDS) - @api.response(204, "Variable reset (no content)") - @api.response(404, "Variable not found") @_api_prerequisite def put(self, app_model: App, variable_id: str): draft_var_srv = WorkflowDraftVariableService( @@ -405,13 +358,7 @@ def _get_variable_list(app_model: App, node_id) -> WorkflowDraftVariableList: return draft_vars -@console_ns.route("/apps//workflows/draft/conversation-variables") class ConversationVariableCollectionApi(Resource): - @api.doc("get_conversation_variables") - @api.doc(description="Get conversation variables for workflow") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Conversation variables retrieved successfully", _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) - @api.response(404, "Draft workflow not found") @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) def get(self, app_model: App): @@ -427,25 +374,14 @@ class ConversationVariableCollectionApi(Resource): return _get_variable_list(app_model, CONVERSATION_VARIABLE_NODE_ID) -@console_ns.route("/apps//workflows/draft/system-variables") class SystemVariableCollectionApi(Resource): - @api.doc("get_system_variables") - @api.doc(description="Get system variables for workflow") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "System variables retrieved successfully", _WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) def get(self, app_model: App): return _get_variable_list(app_model, SYSTEM_VARIABLE_NODE_ID) -@console_ns.route("/apps//workflows/draft/environment-variables") class EnvironmentVariableCollectionApi(Resource): - @api.doc("get_environment_variables") - @api.doc(description="Get environment variables for workflow") - @api.doc(params={"app_id": "Application ID"}) - @api.response(200, "Environment variables retrieved successfully") - @api.response(404, "Draft workflow not found") @_api_prerequisite def get(self, app_model: App): """ @@ -477,3 +413,16 @@ class EnvironmentVariableCollectionApi(Resource): ) return {"items": env_vars_list} + + +api.add_resource( + WorkflowVariableCollectionApi, + "/apps//workflows/draft/variables", +) +api.add_resource(NodeVariableCollectionApi, "/apps//workflows/draft/nodes//variables") +api.add_resource(VariableApi, "/apps//workflows/draft/variables/") +api.add_resource(VariableResetApi, "/apps//workflows/draft/variables//reset") + +api.add_resource(ConversationVariableCollectionApi, "/apps//workflows/draft/conversation-variables") +api.add_resource(SystemVariableCollectionApi, "/apps//workflows/draft/system-variables") +api.add_resource(EnvironmentVariableCollectionApi, "/apps//workflows/draft/environment-variables") diff --git a/api/controllers/console/app/workflow_run.py b/api/controllers/console/app/workflow_run.py index 23ba63845c..dccbfd8648 100644 --- a/api/controllers/console/app/workflow_run.py +++ b/api/controllers/console/app/workflow_run.py @@ -4,7 +4,7 @@ from flask_login import current_user from flask_restx import Resource, marshal_with, reqparse from flask_restx.inputs import int_range -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.app.wraps import get_app_model from controllers.console.wraps import account_initialization_required, setup_required from fields.workflow_run_fields import ( @@ -19,13 +19,7 @@ from models import Account, App, AppMode, EndUser from services.workflow_run_service import WorkflowRunService -@console_ns.route("/apps//advanced-chat/workflow-runs") class AdvancedChatAppWorkflowRunListApi(Resource): - @api.doc("get_advanced_chat_workflow_runs") - @api.doc(description="Get advanced chat workflow run list") - @api.doc(params={"app_id": "Application ID"}) - @api.doc(params={"last_id": "Last run ID for pagination", "limit": "Number of items per page (1-100)"}) - @api.response(200, "Workflow runs retrieved successfully", advanced_chat_workflow_run_pagination_fields) @setup_required @login_required @account_initialization_required @@ -46,13 +40,7 @@ class AdvancedChatAppWorkflowRunListApi(Resource): return result -@console_ns.route("/apps//workflow-runs") class WorkflowRunListApi(Resource): - @api.doc("get_workflow_runs") - @api.doc(description="Get workflow run list") - @api.doc(params={"app_id": "Application ID"}) - @api.doc(params={"last_id": "Last run ID for pagination", "limit": "Number of items per page (1-100)"}) - @api.response(200, "Workflow runs retrieved successfully", workflow_run_pagination_fields) @setup_required @login_required @account_initialization_required @@ -73,13 +61,7 @@ class WorkflowRunListApi(Resource): return result -@console_ns.route("/apps//workflow-runs/") class WorkflowRunDetailApi(Resource): - @api.doc("get_workflow_run_detail") - @api.doc(description="Get workflow run detail") - @api.doc(params={"app_id": "Application ID", "run_id": "Workflow run ID"}) - @api.response(200, "Workflow run detail retrieved successfully", workflow_run_detail_fields) - @api.response(404, "Workflow run not found") @setup_required @login_required @account_initialization_required @@ -97,13 +79,7 @@ class WorkflowRunDetailApi(Resource): return workflow_run -@console_ns.route("/apps//workflow-runs//node-executions") class WorkflowRunNodeExecutionListApi(Resource): - @api.doc("get_workflow_run_node_executions") - @api.doc(description="Get workflow run node execution list") - @api.doc(params={"app_id": "Application ID", "run_id": "Workflow run ID"}) - @api.response(200, "Node executions retrieved successfully", workflow_run_node_execution_list_fields) - @api.response(404, "Workflow run not found") @setup_required @login_required @account_initialization_required @@ -124,3 +100,9 @@ class WorkflowRunNodeExecutionListApi(Resource): ) return {"data": node_executions} + + +api.add_resource(AdvancedChatAppWorkflowRunListApi, "/apps//advanced-chat/workflow-runs") +api.add_resource(WorkflowRunListApi, "/apps//workflow-runs") +api.add_resource(WorkflowRunDetailApi, "/apps//workflow-runs/") +api.add_resource(WorkflowRunNodeExecutionListApi, "/apps//workflow-runs//node-executions") diff --git a/api/controllers/console/app/workflow_statistic.py b/api/controllers/console/app/workflow_statistic.py index 535e7cadd6..da7216086e 100644 --- a/api/controllers/console/app/workflow_statistic.py +++ b/api/controllers/console/app/workflow_statistic.py @@ -7,7 +7,7 @@ from flask import jsonify from flask_login import current_user from flask_restx import Resource, reqparse -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.app.wraps import get_app_model from controllers.console.wraps import account_initialization_required, setup_required from extensions.ext_database import db @@ -17,13 +17,7 @@ from models.enums import WorkflowRunTriggeredFrom from models.model import AppMode -@console_ns.route("/apps//workflow/statistics/daily-conversations") class WorkflowDailyRunsStatistic(Resource): - @api.doc("get_workflow_daily_runs_statistic") - @api.doc(description="Get workflow daily runs statistics") - @api.doc(params={"app_id": "Application ID"}) - @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"}) - @api.response(200, "Daily runs statistics retrieved successfully") @get_app_model @setup_required @login_required @@ -85,13 +79,7 @@ WHERE return jsonify({"data": response_data}) -@console_ns.route("/apps//workflow/statistics/daily-terminals") class WorkflowDailyTerminalsStatistic(Resource): - @api.doc("get_workflow_daily_terminals_statistic") - @api.doc(description="Get workflow daily terminals statistics") - @api.doc(params={"app_id": "Application ID"}) - @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"}) - @api.response(200, "Daily terminals statistics retrieved successfully") @get_app_model @setup_required @login_required @@ -153,13 +141,7 @@ WHERE return jsonify({"data": response_data}) -@console_ns.route("/apps//workflow/statistics/token-costs") class WorkflowDailyTokenCostStatistic(Resource): - @api.doc("get_workflow_daily_token_cost_statistic") - @api.doc(description="Get workflow daily token cost statistics") - @api.doc(params={"app_id": "Application ID"}) - @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"}) - @api.response(200, "Daily token cost statistics retrieved successfully") @get_app_model @setup_required @login_required @@ -226,13 +208,7 @@ WHERE return jsonify({"data": response_data}) -@console_ns.route("/apps//workflow/statistics/average-app-interactions") class WorkflowAverageAppInteractionStatistic(Resource): - @api.doc("get_workflow_average_app_interaction_statistic") - @api.doc(description="Get workflow average app interaction statistics") - @api.doc(params={"app_id": "Application ID"}) - @api.doc(params={"start": "Start date and time (YYYY-MM-DD HH:MM)", "end": "End date and time (YYYY-MM-DD HH:MM)"}) - @api.response(200, "Average app interaction statistics retrieved successfully") @setup_required @login_required @account_initialization_required @@ -309,3 +285,11 @@ GROUP BY ) return jsonify({"data": response_data}) + + +api.add_resource(WorkflowDailyRunsStatistic, "/apps//workflow/statistics/daily-conversations") +api.add_resource(WorkflowDailyTerminalsStatistic, "/apps//workflow/statistics/daily-terminals") +api.add_resource(WorkflowDailyTokenCostStatistic, "/apps//workflow/statistics/token-costs") +api.add_resource( + WorkflowAverageAppInteractionStatistic, "/apps//workflow/statistics/average-app-interactions" +) diff --git a/api/controllers/console/datasets/datasets.py b/api/controllers/console/datasets/datasets.py index 6ed3d39a2b..9fb092607a 100644 --- a/api/controllers/console/datasets/datasets.py +++ b/api/controllers/console/datasets/datasets.py @@ -1,13 +1,13 @@ import flask_restx from flask import request from flask_login import current_user -from flask_restx import Resource, fields, marshal, marshal_with, reqparse +from flask_restx import Resource, marshal, marshal_with, reqparse from sqlalchemy import select from werkzeug.exceptions import Forbidden, NotFound import services from configs import dify_config -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.apikey import api_key_fields, api_key_list from controllers.console.app.error import ProviderNotInitializeError from controllers.console.datasets.error import DatasetInUseError, DatasetNameDuplicateError, IndexingEstimateError @@ -48,21 +48,7 @@ def _validate_description_length(description): return description -@console_ns.route("/datasets") class DatasetListApi(Resource): - @api.doc("get_datasets") - @api.doc(description="Get list of datasets") - @api.doc( - params={ - "page": "Page number (default: 1)", - "limit": "Number of items per page (default: 20)", - "ids": "Filter by dataset IDs (list)", - "keyword": "Search keyword", - "tag_ids": "Filter by tag IDs (list)", - "include_all": "Include all datasets (default: false)", - } - ) - @api.response(200, "Datasets retrieved successfully") @setup_required @login_required @account_initialization_required @@ -114,24 +100,6 @@ class DatasetListApi(Resource): response = {"data": data, "has_more": len(datasets) == limit, "limit": limit, "total": total, "page": page} return response, 200 - @api.doc("create_dataset") - @api.doc(description="Create a new dataset") - @api.expect( - api.model( - "CreateDatasetRequest", - { - "name": fields.String(required=True, description="Dataset name (1-40 characters)"), - "description": fields.String(description="Dataset description (max 400 characters)"), - "indexing_technique": fields.String(description="Indexing technique"), - "permission": fields.String(description="Dataset permission"), - "provider": fields.String(description="Provider"), - "external_knowledge_api_id": fields.String(description="External knowledge API ID"), - "external_knowledge_id": fields.String(description="External knowledge ID"), - }, - ) - ) - @api.response(201, "Dataset created successfully") - @api.response(400, "Invalid request parameters") @setup_required @login_required @account_initialization_required @@ -204,14 +172,7 @@ class DatasetListApi(Resource): return marshal(dataset, dataset_detail_fields), 201 -@console_ns.route("/datasets/") class DatasetApi(Resource): - @api.doc("get_dataset") - @api.doc(description="Get dataset details") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Dataset retrieved successfully", dataset_detail_fields) - @api.response(404, "Dataset not found") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -254,23 +215,6 @@ class DatasetApi(Resource): return data, 200 - @api.doc("update_dataset") - @api.doc(description="Update dataset details") - @api.expect( - api.model( - "UpdateDatasetRequest", - { - "name": fields.String(description="Dataset name"), - "description": fields.String(description="Dataset description"), - "permission": fields.String(description="Dataset permission"), - "indexing_technique": fields.String(description="Indexing technique"), - "external_retrieval_model": fields.Raw(description="External retrieval model settings"), - }, - ) - ) - @api.response(200, "Dataset updated successfully", dataset_detail_fields) - @api.response(404, "Dataset not found") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -400,12 +344,7 @@ class DatasetApi(Resource): raise DatasetInUseError() -@console_ns.route("/datasets//use-check") class DatasetUseCheckApi(Resource): - @api.doc("check_dataset_use") - @api.doc(description="Check if dataset is in use") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Dataset use status retrieved successfully") @setup_required @login_required @account_initialization_required @@ -416,12 +355,7 @@ class DatasetUseCheckApi(Resource): return {"is_using": dataset_is_using}, 200 -@console_ns.route("/datasets//queries") class DatasetQueryApi(Resource): - @api.doc("get_dataset_queries") - @api.doc(description="Get dataset query history") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Query history retrieved successfully", dataset_query_detail_fields) @setup_required @login_required @account_initialization_required @@ -451,11 +385,7 @@ class DatasetQueryApi(Resource): return response, 200 -@console_ns.route("/datasets/indexing-estimate") class DatasetIndexingEstimateApi(Resource): - @api.doc("estimate_dataset_indexing") - @api.doc(description="Estimate dataset indexing cost") - @api.response(200, "Indexing estimate calculated successfully") @setup_required @login_required @account_initialization_required @@ -556,12 +486,7 @@ class DatasetIndexingEstimateApi(Resource): return response.model_dump(), 200 -@console_ns.route("/datasets//related-apps") class DatasetRelatedAppListApi(Resource): - @api.doc("get_dataset_related_apps") - @api.doc(description="Get applications related to dataset") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Related apps retrieved successfully", related_app_list) @setup_required @login_required @account_initialization_required @@ -588,12 +513,7 @@ class DatasetRelatedAppListApi(Resource): return {"data": related_apps, "total": len(related_apps)}, 200 -@console_ns.route("/datasets//indexing-status") class DatasetIndexingStatusApi(Resource): - @api.doc("get_dataset_indexing_status") - @api.doc(description="Get dataset indexing status") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Indexing status retrieved successfully") @setup_required @login_required @account_initialization_required @@ -640,15 +560,11 @@ class DatasetIndexingStatusApi(Resource): return data, 200 -@console_ns.route("/datasets/api-keys") class DatasetApiKeyApi(Resource): max_keys = 10 token_prefix = "dataset-" resource_type = "dataset" - @api.doc("get_dataset_api_keys") - @api.doc(description="Get dataset API keys") - @api.response(200, "API keys retrieved successfully", api_key_list) @setup_required @login_required @account_initialization_required @@ -693,14 +609,9 @@ class DatasetApiKeyApi(Resource): return api_token, 200 -@console_ns.route("/datasets/api-keys/") class DatasetApiDeleteApi(Resource): resource_type = "dataset" - @api.doc("delete_dataset_api_key") - @api.doc(description="Delete dataset API key") - @api.doc(params={"api_key_id": "API key ID"}) - @api.response(204, "API key deleted successfully") @setup_required @login_required @account_initialization_required @@ -730,11 +641,7 @@ class DatasetApiDeleteApi(Resource): return {"result": "success"}, 204 -@console_ns.route("/datasets/api-base-info") class DatasetApiBaseUrlApi(Resource): - @api.doc("get_dataset_api_base_info") - @api.doc(description="Get dataset API base information") - @api.response(200, "API base info retrieved successfully") @setup_required @login_required @account_initialization_required @@ -742,11 +649,7 @@ class DatasetApiBaseUrlApi(Resource): return {"api_base_url": (dify_config.SERVICE_API_URL or request.host_url.rstrip("/")) + "/v1"} -@console_ns.route("/datasets/retrieval-setting") class DatasetRetrievalSettingApi(Resource): - @api.doc("get_dataset_retrieval_setting") - @api.doc(description="Get dataset retrieval settings") - @api.response(200, "Retrieval settings retrieved successfully") @setup_required @login_required @account_initialization_required @@ -797,12 +700,7 @@ class DatasetRetrievalSettingApi(Resource): raise ValueError(f"Unsupported vector db type {vector_type}.") -@console_ns.route("/datasets/retrieval-setting/") class DatasetRetrievalSettingMockApi(Resource): - @api.doc("get_dataset_retrieval_setting_mock") - @api.doc(description="Get mock dataset retrieval settings by vector type") - @api.doc(params={"vector_type": "Vector store type"}) - @api.response(200, "Mock retrieval settings retrieved successfully") @setup_required @login_required @account_initialization_required @@ -851,13 +749,7 @@ class DatasetRetrievalSettingMockApi(Resource): raise ValueError(f"Unsupported vector db type {vector_type}.") -@console_ns.route("/datasets//error-docs") class DatasetErrorDocs(Resource): - @api.doc("get_dataset_error_docs") - @api.doc(description="Get dataset error documents") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Error documents retrieved successfully") - @api.response(404, "Dataset not found") @setup_required @login_required @account_initialization_required @@ -871,14 +763,7 @@ class DatasetErrorDocs(Resource): return {"data": [marshal(item, document_status_fields) for item in results], "total": len(results)}, 200 -@console_ns.route("/datasets//permission-part-users") class DatasetPermissionUserListApi(Resource): - @api.doc("get_dataset_permission_users") - @api.doc(description="Get dataset permission user list") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Permission users retrieved successfully") - @api.response(404, "Dataset not found") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -899,13 +784,7 @@ class DatasetPermissionUserListApi(Resource): }, 200 -@console_ns.route("/datasets//auto-disable-logs") class DatasetAutoDisableLogApi(Resource): - @api.doc("get_dataset_auto_disable_logs") - @api.doc(description="Get dataset auto disable logs") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.response(200, "Auto disable logs retrieved successfully") - @api.response(404, "Dataset not found") @setup_required @login_required @account_initialization_required @@ -915,3 +794,20 @@ class DatasetAutoDisableLogApi(Resource): if dataset is None: raise NotFound("Dataset not found.") return DatasetService.get_dataset_auto_disable_logs(dataset_id_str), 200 + + +api.add_resource(DatasetListApi, "/datasets") +api.add_resource(DatasetApi, "/datasets/") +api.add_resource(DatasetUseCheckApi, "/datasets//use-check") +api.add_resource(DatasetQueryApi, "/datasets//queries") +api.add_resource(DatasetErrorDocs, "/datasets//error-docs") +api.add_resource(DatasetIndexingEstimateApi, "/datasets/indexing-estimate") +api.add_resource(DatasetRelatedAppListApi, "/datasets//related-apps") +api.add_resource(DatasetIndexingStatusApi, "/datasets//indexing-status") +api.add_resource(DatasetApiKeyApi, "/datasets/api-keys") +api.add_resource(DatasetApiDeleteApi, "/datasets/api-keys/") +api.add_resource(DatasetApiBaseUrlApi, "/datasets/api-base-info") +api.add_resource(DatasetRetrievalSettingApi, "/datasets/retrieval-setting") +api.add_resource(DatasetRetrievalSettingMockApi, "/datasets/retrieval-setting/") +api.add_resource(DatasetPermissionUserListApi, "/datasets//permission-part-users") +api.add_resource(DatasetAutoDisableLogApi, "/datasets//auto-disable-logs") diff --git a/api/controllers/console/datasets/datasets_document.py b/api/controllers/console/datasets/datasets_document.py index 0b65967445..f943fb3ccb 100644 --- a/api/controllers/console/datasets/datasets_document.py +++ b/api/controllers/console/datasets/datasets_document.py @@ -5,12 +5,12 @@ from typing import Literal, cast from flask import request from flask_login import current_user -from flask_restx import Resource, fields, marshal, marshal_with, reqparse +from flask_restx import Resource, marshal, marshal_with, reqparse from sqlalchemy import asc, desc, select from werkzeug.exceptions import Forbidden, NotFound import services -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.app.error import ( ProviderModelCurrentlyNotSupportError, ProviderNotInitializeError, @@ -98,12 +98,7 @@ class DocumentResource(Resource): return documents -@console_ns.route("/datasets/process-rule") class GetProcessRuleApi(Resource): - @api.doc("get_process_rule") - @api.doc(description="Get dataset document processing rules") - @api.doc(params={"document_id": "Document ID (optional)"}) - @api.response(200, "Process rules retrieved successfully") @setup_required @login_required @account_initialization_required @@ -145,21 +140,7 @@ class GetProcessRuleApi(Resource): return {"mode": mode, "rules": rules, "limits": limits} -@console_ns.route("/datasets//documents") class DatasetDocumentListApi(Resource): - @api.doc("get_dataset_documents") - @api.doc(description="Get documents in a dataset") - @api.doc( - params={ - "dataset_id": "Dataset ID", - "page": "Page number (default: 1)", - "limit": "Number of items per page (default: 20)", - "keyword": "Search keyword", - "sort": "Sort order (default: -created_at)", - "fetch": "Fetch full details (default: false)", - } - ) - @api.response(200, "Documents retrieved successfully") @setup_required @login_required @account_initialization_required @@ -343,23 +324,7 @@ class DatasetDocumentListApi(Resource): return {"result": "success"}, 204 -@console_ns.route("/datasets/init") class DatasetInitApi(Resource): - @api.doc("init_dataset") - @api.doc(description="Initialize dataset with documents") - @api.expect( - api.model( - "DatasetInitRequest", - { - "upload_file_id": fields.String(required=True, description="Upload file ID"), - "indexing_technique": fields.String(description="Indexing technique"), - "process_rule": fields.Raw(description="Processing rules"), - "data_source": fields.Raw(description="Data source configuration"), - }, - ) - ) - @api.response(201, "Dataset initialized successfully", dataset_and_document_fields) - @api.response(400, "Invalid request parameters") @setup_required @login_required @account_initialization_required @@ -429,14 +394,7 @@ class DatasetInitApi(Resource): return response -@console_ns.route("/datasets//documents//indexing-estimate") class DocumentIndexingEstimateApi(DocumentResource): - @api.doc("estimate_document_indexing") - @api.doc(description="Estimate document indexing cost") - @api.doc(params={"dataset_id": "Dataset ID", "document_id": "Document ID"}) - @api.response(200, "Indexing estimate calculated successfully") - @api.response(404, "Document not found") - @api.response(400, "Document already finished") @setup_required @login_required @account_initialization_required @@ -635,13 +593,7 @@ class DocumentBatchIndexingStatusApi(DocumentResource): return data -@console_ns.route("/datasets//documents//indexing-status") class DocumentIndexingStatusApi(DocumentResource): - @api.doc("get_document_indexing_status") - @api.doc(description="Get document indexing status") - @api.doc(params={"dataset_id": "Dataset ID", "document_id": "Document ID"}) - @api.response(200, "Indexing status retrieved successfully") - @api.response(404, "Document not found") @setup_required @login_required @account_initialization_required @@ -683,21 +635,9 @@ class DocumentIndexingStatusApi(DocumentResource): return marshal(document_dict, document_status_fields) -@console_ns.route("/datasets//documents/") class DocumentApi(DocumentResource): METADATA_CHOICES = {"all", "only", "without"} - @api.doc("get_document") - @api.doc(description="Get document details") - @api.doc( - params={ - "dataset_id": "Dataset ID", - "document_id": "Document ID", - "metadata": "Metadata inclusion (all/only/without)", - } - ) - @api.response(200, "Document retrieved successfully") - @api.response(404, "Document not found") @setup_required @login_required @account_initialization_required @@ -806,16 +746,7 @@ class DocumentApi(DocumentResource): return {"result": "success"}, 204 -@console_ns.route("/datasets//documents//processing/") class DocumentProcessingApi(DocumentResource): - @api.doc("update_document_processing") - @api.doc(description="Update document processing status (pause/resume)") - @api.doc( - params={"dataset_id": "Dataset ID", "document_id": "Document ID", "action": "Action to perform (pause/resume)"} - ) - @api.response(200, "Processing status updated successfully") - @api.response(404, "Document not found") - @api.response(400, "Invalid action") @setup_required @login_required @account_initialization_required @@ -850,23 +781,7 @@ class DocumentProcessingApi(DocumentResource): return {"result": "success"}, 200 -@console_ns.route("/datasets//documents//metadata") class DocumentMetadataApi(DocumentResource): - @api.doc("update_document_metadata") - @api.doc(description="Update document metadata") - @api.doc(params={"dataset_id": "Dataset ID", "document_id": "Document ID"}) - @api.expect( - api.model( - "UpdateDocumentMetadataRequest", - { - "doc_type": fields.String(description="Document type"), - "doc_metadata": fields.Raw(description="Document metadata"), - }, - ) - ) - @api.response(200, "Document metadata updated successfully") - @api.response(404, "Document not found") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -1100,3 +1015,26 @@ class WebsiteDocumentSyncApi(DocumentResource): DocumentService.sync_website_document(dataset_id, document) return {"result": "success"}, 200 + + +api.add_resource(GetProcessRuleApi, "/datasets/process-rule") +api.add_resource(DatasetDocumentListApi, "/datasets//documents") +api.add_resource(DatasetInitApi, "/datasets/init") +api.add_resource( + DocumentIndexingEstimateApi, "/datasets//documents//indexing-estimate" +) +api.add_resource(DocumentBatchIndexingEstimateApi, "/datasets//batch//indexing-estimate") +api.add_resource(DocumentBatchIndexingStatusApi, "/datasets//batch//indexing-status") +api.add_resource(DocumentIndexingStatusApi, "/datasets//documents//indexing-status") +api.add_resource(DocumentApi, "/datasets//documents/") +api.add_resource( + DocumentProcessingApi, "/datasets//documents//processing/" +) +api.add_resource(DocumentMetadataApi, "/datasets//documents//metadata") +api.add_resource(DocumentStatusApi, "/datasets//documents/status//batch") +api.add_resource(DocumentPauseApi, "/datasets//documents//processing/pause") +api.add_resource(DocumentRecoverApi, "/datasets//documents//processing/resume") +api.add_resource(DocumentRetryApi, "/datasets//retry") +api.add_resource(DocumentRenameApi, "/datasets//documents//rename") + +api.add_resource(WebsiteDocumentSyncApi, "/datasets//documents//website-sync") diff --git a/api/controllers/console/datasets/external.py b/api/controllers/console/datasets/external.py index 7195a5dd11..043f39f623 100644 --- a/api/controllers/console/datasets/external.py +++ b/api/controllers/console/datasets/external.py @@ -1,10 +1,10 @@ from flask import request from flask_login import current_user -from flask_restx import Resource, fields, marshal, reqparse +from flask_restx import Resource, marshal, reqparse from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import services -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.datasets.error import DatasetNameDuplicateError from controllers.console.wraps import account_initialization_required, setup_required from fields.dataset_fields import dataset_detail_fields @@ -21,18 +21,7 @@ def _validate_name(name): return name -@console_ns.route("/datasets/external-knowledge-api") class ExternalApiTemplateListApi(Resource): - @api.doc("get_external_api_templates") - @api.doc(description="Get external knowledge API templates") - @api.doc( - params={ - "page": "Page number (default: 1)", - "limit": "Number of items per page (default: 20)", - "keyword": "Search keyword", - } - ) - @api.response(200, "External API templates retrieved successfully") @setup_required @login_required @account_initialization_required @@ -90,13 +79,7 @@ class ExternalApiTemplateListApi(Resource): return external_knowledge_api.to_dict(), 201 -@console_ns.route("/datasets/external-knowledge-api/") class ExternalApiTemplateApi(Resource): - @api.doc("get_external_api_template") - @api.doc(description="Get external knowledge API template details") - @api.doc(params={"external_knowledge_api_id": "External knowledge API ID"}) - @api.response(200, "External API template retrieved successfully") - @api.response(404, "Template not found") @setup_required @login_required @account_initialization_required @@ -155,12 +138,7 @@ class ExternalApiTemplateApi(Resource): return {"result": "success"}, 204 -@console_ns.route("/datasets/external-knowledge-api//use-check") class ExternalApiUseCheckApi(Resource): - @api.doc("check_external_api_usage") - @api.doc(description="Check if external knowledge API is being used") - @api.doc(params={"external_knowledge_api_id": "External knowledge API ID"}) - @api.response(200, "Usage check completed successfully") @setup_required @login_required @account_initialization_required @@ -173,24 +151,7 @@ class ExternalApiUseCheckApi(Resource): return {"is_using": external_knowledge_api_is_using, "count": count}, 200 -@console_ns.route("/datasets/external") class ExternalDatasetCreateApi(Resource): - @api.doc("create_external_dataset") - @api.doc(description="Create external knowledge dataset") - @api.expect( - api.model( - "CreateExternalDatasetRequest", - { - "external_knowledge_api_id": fields.String(required=True, description="External knowledge API ID"), - "external_knowledge_id": fields.String(required=True, description="External knowledge ID"), - "name": fields.String(required=True, description="Dataset name"), - "description": fields.String(description="Dataset description"), - }, - ) - ) - @api.response(201, "External dataset created successfully", dataset_detail_fields) - @api.response(400, "Invalid parameters") - @api.response(403, "Permission denied") @setup_required @login_required @account_initialization_required @@ -230,24 +191,7 @@ class ExternalDatasetCreateApi(Resource): return marshal(dataset, dataset_detail_fields), 201 -@console_ns.route("/datasets//external-hit-testing") class ExternalKnowledgeHitTestingApi(Resource): - @api.doc("test_external_knowledge_retrieval") - @api.doc(description="Test external knowledge retrieval for dataset") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.expect( - api.model( - "ExternalHitTestingRequest", - { - "query": fields.String(required=True, description="Query text for testing"), - "retrieval_model": fields.Raw(description="Retrieval model configuration"), - "external_retrieval_model": fields.Raw(description="External retrieval model configuration"), - }, - ) - ) - @api.response(200, "External hit testing completed successfully") - @api.response(404, "Dataset not found") - @api.response(400, "Invalid parameters") @setup_required @login_required @account_initialization_required @@ -284,22 +228,8 @@ class ExternalKnowledgeHitTestingApi(Resource): raise InternalServerError(str(e)) -@console_ns.route("/test/retrieval") class BedrockRetrievalApi(Resource): # this api is only for internal testing - @api.doc("bedrock_retrieval_test") - @api.doc(description="Bedrock retrieval test (internal use only)") - @api.expect( - api.model( - "BedrockRetrievalTestRequest", - { - "retrieval_setting": fields.Raw(required=True, description="Retrieval settings"), - "query": fields.String(required=True, description="Query text"), - "knowledge_id": fields.String(required=True, description="Knowledge ID"), - }, - ) - ) - @api.response(200, "Bedrock retrieval test completed") def post(self): parser = reqparse.RequestParser() parser.add_argument("retrieval_setting", nullable=False, required=True, type=dict, location="json") @@ -317,3 +247,12 @@ class BedrockRetrievalApi(Resource): args["retrieval_setting"], args["query"], args["knowledge_id"] ) return result, 200 + + +api.add_resource(ExternalKnowledgeHitTestingApi, "/datasets//external-hit-testing") +api.add_resource(ExternalDatasetCreateApi, "/datasets/external") +api.add_resource(ExternalApiTemplateListApi, "/datasets/external-knowledge-api") +api.add_resource(ExternalApiTemplateApi, "/datasets/external-knowledge-api/") +api.add_resource(ExternalApiUseCheckApi, "/datasets/external-knowledge-api//use-check") +# this api is only for internal test +api.add_resource(BedrockRetrievalApi, "/test/retrieval") diff --git a/api/controllers/console/datasets/hit_testing.py b/api/controllers/console/datasets/hit_testing.py index abaca88090..2ad192571b 100644 --- a/api/controllers/console/datasets/hit_testing.py +++ b/api/controllers/console/datasets/hit_testing.py @@ -1,6 +1,6 @@ -from flask_restx import Resource, fields +from flask_restx import Resource -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.datasets.hit_testing_base import DatasetsHitTestingBase from controllers.console.wraps import ( account_initialization_required, @@ -10,25 +10,7 @@ from controllers.console.wraps import ( from libs.login import login_required -@console_ns.route("/datasets//hit-testing") class HitTestingApi(Resource, DatasetsHitTestingBase): - @api.doc("test_dataset_retrieval") - @api.doc(description="Test dataset knowledge retrieval") - @api.doc(params={"dataset_id": "Dataset ID"}) - @api.expect( - api.model( - "HitTestingRequest", - { - "query": fields.String(required=True, description="Query text for testing"), - "retrieval_model": fields.Raw(description="Retrieval model configuration"), - "top_k": fields.Integer(description="Number of top results to return"), - "score_threshold": fields.Float(description="Score threshold for filtering results"), - }, - ) - ) - @api.response(200, "Hit testing completed successfully") - @api.response(404, "Dataset not found") - @api.response(400, "Invalid parameters") @setup_required @login_required @account_initialization_required @@ -41,3 +23,6 @@ class HitTestingApi(Resource, DatasetsHitTestingBase): self.hit_testing_args_check(args) return self.perform_hit_testing(dataset, args) + + +api.add_resource(HitTestingApi, "/datasets//hit-testing") diff --git a/api/controllers/console/datasets/website.py b/api/controllers/console/datasets/website.py index b9c1f65bfd..bdaa268462 100644 --- a/api/controllers/console/datasets/website.py +++ b/api/controllers/console/datasets/website.py @@ -1,32 +1,13 @@ -from flask_restx import Resource, fields, reqparse +from flask_restx import Resource, reqparse -from controllers.console import api, console_ns +from controllers.console import api from controllers.console.datasets.error import WebsiteCrawlError from controllers.console.wraps import account_initialization_required, setup_required from libs.login import login_required from services.website_service import WebsiteCrawlApiRequest, WebsiteCrawlStatusApiRequest, WebsiteService -@console_ns.route("/website/crawl") class WebsiteCrawlApi(Resource): - @api.doc("crawl_website") - @api.doc(description="Crawl website content") - @api.expect( - api.model( - "WebsiteCrawlRequest", - { - "provider": fields.String( - required=True, - description="Crawl provider (firecrawl/watercrawl/jinareader)", - enum=["firecrawl", "watercrawl", "jinareader"], - ), - "url": fields.String(required=True, description="URL to crawl"), - "options": fields.Raw(required=True, description="Crawl options"), - }, - ) - ) - @api.response(200, "Website crawl initiated successfully") - @api.response(400, "Invalid crawl parameters") @setup_required @login_required @account_initialization_required @@ -58,14 +39,7 @@ class WebsiteCrawlApi(Resource): return result, 200 -@console_ns.route("/website/crawl/status/") class WebsiteCrawlStatusApi(Resource): - @api.doc("get_crawl_status") - @api.doc(description="Get website crawl status") - @api.doc(params={"job_id": "Crawl job ID", "provider": "Crawl provider (firecrawl/watercrawl/jinareader)"}) - @api.response(200, "Crawl status retrieved successfully") - @api.response(404, "Crawl job not found") - @api.response(400, "Invalid provider") @setup_required @login_required @account_initialization_required @@ -88,3 +62,7 @@ class WebsiteCrawlStatusApi(Resource): except Exception as e: raise WebsiteCrawlError(str(e)) return result, 200 + + +api.add_resource(WebsiteCrawlApi, "/website/crawl") +api.add_resource(WebsiteCrawlStatusApi, "/website/crawl/status/")