mirror of
https://github.com/langgenius/dify.git
synced 2026-03-19 05:37:42 +08:00
WIP: P2
This commit is contained in:
@ -4,56 +4,79 @@ Console/Studio Human Input Form APIs.
|
||||
|
||||
import json
|
||||
import logging
|
||||
from collections.abc import Generator
|
||||
|
||||
from flask import g, jsonify
|
||||
from flask import Response, jsonify
|
||||
from flask_restx import Resource, reqparse
|
||||
from pydantic import BaseModel
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from controllers.console import api
|
||||
from controllers.console.wraps import account_initialization_required
|
||||
from controllers.console.wraps import account_initialization_required, setup_required
|
||||
from controllers.web.error import NotFoundError
|
||||
from core.workflow.nodes.human_input.entities import FormDefinition
|
||||
from extensions.ext_database import db
|
||||
from libs.login import login_required
|
||||
from models.human_input import HumanInputSubmissionType
|
||||
from services.human_input_form_service import (
|
||||
HumanInputFormAlreadySubmittedError,
|
||||
HumanInputFormExpiredError,
|
||||
HumanInputFormNotFoundError,
|
||||
HumanInputFormService,
|
||||
InvalidFormDataError,
|
||||
)
|
||||
from libs.login import current_account_with_tenant, login_required
|
||||
from models.human_input import HumanInputForm as HumanInputFormModel
|
||||
from services.human_input_service import HumanInputService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _FormDefinitionWithSite(FormDefinition):
|
||||
site: None
|
||||
|
||||
|
||||
def _jsonify_pydantic_model(model: BaseModel) -> Response:
|
||||
return Response(model.model_dump_json(), mimetype="application/json")
|
||||
|
||||
|
||||
class ConsoleHumanInputFormApi(Resource):
|
||||
"""Console API for getting human input form definition."""
|
||||
|
||||
@account_initialization_required
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
def get(self, form_id: str):
|
||||
"""
|
||||
Get human input form definition by form ID.
|
||||
|
||||
GET /console/api/form/human_input/<form_id>
|
||||
"""
|
||||
try:
|
||||
service = HumanInputFormService(db.session())
|
||||
form_definition = service.get_form_definition(identifier=form_id, is_token=False, include_site_info=False)
|
||||
return form_definition, 200
|
||||
service = HumanInputService(db.engine)
|
||||
form = service.get_form_definition_by_id(
|
||||
form_id=form_id,
|
||||
)
|
||||
if form is None:
|
||||
raise NotFoundError(f"form not found, id={form_id}")
|
||||
|
||||
except HumanInputFormNotFoundError:
|
||||
raise NotFoundError("Form not found")
|
||||
except HumanInputFormExpiredError:
|
||||
return jsonify(
|
||||
{"error_code": "human_input_form_expired", "description": "Human input form has expired"}
|
||||
), 400
|
||||
except HumanInputFormAlreadySubmittedError:
|
||||
return jsonify(
|
||||
{
|
||||
"error_code": "human_input_form_submitted",
|
||||
"description": "Human input form has already been submitted",
|
||||
}
|
||||
), 400
|
||||
current_user, current_tenant_id = current_account_with_tenant()
|
||||
form_model = db.session.get(HumanInputFormModel, form_id)
|
||||
if form_model is None or form_model.tenant_id != current_tenant_id:
|
||||
raise NotFoundError(f"form not found, id={form_id}")
|
||||
|
||||
from models import App
|
||||
from models.workflow import Workflow, WorkflowRun
|
||||
|
||||
workflow_run = db.session.get(WorkflowRun, form_model.workflow_run_id)
|
||||
if workflow_run is None or workflow_run.tenant_id != current_tenant_id:
|
||||
raise NotFoundError("Workflow run not found")
|
||||
|
||||
if workflow_run.app_id:
|
||||
app = db.session.get(App, workflow_run.app_id)
|
||||
if app is None or app.tenant_id != current_tenant_id:
|
||||
raise NotFoundError("App not found")
|
||||
owner_account_id = app.created_by
|
||||
else:
|
||||
workflow = db.session.get(Workflow, workflow_run.workflow_id)
|
||||
if workflow is None or workflow.tenant_id != current_tenant_id:
|
||||
raise NotFoundError("Workflow not found")
|
||||
owner_account_id = workflow.created_by
|
||||
|
||||
if owner_account_id != current_user.id:
|
||||
raise Forbidden("You do not have permission to access this human input form.")
|
||||
|
||||
return _jsonify_pydantic_model(form.get_definition())
|
||||
|
||||
|
||||
class ConsoleHumanInputFormSubmissionApi(Resource):
|
||||
@ -80,75 +103,15 @@ class ConsoleHumanInputFormSubmissionApi(Resource):
|
||||
parser.add_argument("action", type=str, required=True, location="json")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# Submit the form
|
||||
service = HumanInputFormService(db.session())
|
||||
service.submit_form(
|
||||
identifier=form_id,
|
||||
form_data=args["inputs"],
|
||||
action=args["action"],
|
||||
is_token=False,
|
||||
submission_type=HumanInputSubmissionType.web_form,
|
||||
submission_user_id=g.current_user.id,
|
||||
)
|
||||
# Submit the form
|
||||
service = HumanInputService(db.engine)
|
||||
service.submit_form_by_id(
|
||||
form_id=form_id,
|
||||
selected_action_id=args["action"],
|
||||
form_data=args["inputs"],
|
||||
)
|
||||
|
||||
return {}, 200
|
||||
|
||||
except HumanInputFormNotFoundError:
|
||||
raise NotFoundError("Form not found")
|
||||
except HumanInputFormExpiredError:
|
||||
return jsonify(
|
||||
{"error_code": "human_input_form_expired", "description": "Human input form has expired"}
|
||||
), 400
|
||||
except HumanInputFormAlreadySubmittedError:
|
||||
return jsonify(
|
||||
{
|
||||
"error_code": "human_input_form_submitted",
|
||||
"description": "Human input form has already been submitted",
|
||||
}
|
||||
), 400
|
||||
except InvalidFormDataError as e:
|
||||
return jsonify({"error_code": "invalid_form_data", "description": e.message}), 400
|
||||
|
||||
|
||||
class ConsoleWorkflowResumeWaitApi(Resource):
|
||||
"""Console API for long-polling workflow resume wait."""
|
||||
|
||||
@account_initialization_required
|
||||
@login_required
|
||||
def get(self, task_id: str):
|
||||
"""
|
||||
Get workflow execution resume notification.
|
||||
|
||||
GET /console/api/workflow/<task_id>/resume-wait
|
||||
|
||||
This is a long-polling API that waits for workflow to resume from paused state.
|
||||
"""
|
||||
import time
|
||||
|
||||
# TODO: Implement actual workflow status checking
|
||||
# For now, return a basic response
|
||||
|
||||
timeout = 30 # 30 seconds timeout for demo
|
||||
start_time = time.time()
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
# TODO: Check workflow status from database/cache
|
||||
# workflow_status = workflow_service.get_status(task_id)
|
||||
|
||||
# For demo purposes, simulate different states
|
||||
# In real implementation, this would check the actual workflow state
|
||||
workflow_status = "paused" # or "running" or "ended"
|
||||
|
||||
if workflow_status == "running":
|
||||
return {"status": "running"}, 200
|
||||
elif workflow_status == "ended":
|
||||
return {"status": "ended"}, 200
|
||||
|
||||
time.sleep(1) # Poll every second
|
||||
|
||||
# Return paused status if timeout reached
|
||||
return {"status": "paused"}, 200
|
||||
return jsonify({})
|
||||
|
||||
|
||||
class ConsoleWorkflowEventsApi(Resource):
|
||||
@ -156,7 +119,7 @@ class ConsoleWorkflowEventsApi(Resource):
|
||||
|
||||
@account_initialization_required
|
||||
@login_required
|
||||
def get(self, task_id: str):
|
||||
def get(self, workflow_run_id: str):
|
||||
"""
|
||||
Get workflow execution events stream after resume.
|
||||
|
||||
@ -164,9 +127,8 @@ class ConsoleWorkflowEventsApi(Resource):
|
||||
|
||||
Returns Server-Sent Events stream.
|
||||
"""
|
||||
from collections.abc import Generator
|
||||
|
||||
from flask import Response
|
||||
events =
|
||||
|
||||
def generate_events() -> Generator[str, None, None]:
|
||||
"""Generate SSE events for workflow execution."""
|
||||
@ -263,6 +225,5 @@ class ConsoleWorkflowPauseDetailsApi(Resource):
|
||||
# Register the APIs
|
||||
api.add_resource(ConsoleHumanInputFormApi, "/form/human_input/<string:form_id>")
|
||||
api.add_resource(ConsoleHumanInputFormSubmissionApi, "/form/human_input/<string:form_id>", methods=["POST"])
|
||||
api.add_resource(ConsoleWorkflowResumeWaitApi, "/workflow/<string:task_id>/resume-wait")
|
||||
api.add_resource(ConsoleWorkflowEventsApi, "/workflow/<string:task_id>/events")
|
||||
api.add_resource(ConsoleWorkflowEventsApi, "/workflow/<string:workflow_run_id>/events")
|
||||
api.add_resource(ConsoleWorkflowPauseDetailsApi, "/workflow/<string:workflow_run_id>/pause-details")
|
||||
|
||||
Reference in New Issue
Block a user