Skip to content

Commit 40fb1f5

Browse files
author
Anya Parry
committed
Merge branch 'main' into 'dev-local'
modify OCI_ENDPOINT example value Fixes langgenius#15336 (langgenius#15337) See merge request weihe/dify!5
2 parents 625b1f6 + 58a74fe commit 40fb1f5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2506
-1243
lines changed

.github/ISSUE_TEMPLATE/tracker.yml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: "👾 Tracker"
2+
description: For inner usages, please donot use this template.
3+
title: "[Tracker] "
4+
labels:
5+
- tracker
6+
body:
7+
- type: textarea
8+
id: content
9+
attributes:
10+
label: Blockers
11+
placeholder: "- [ ] ..."
12+
validations:
13+
required: true

api/controllers/console/app/app.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def get(self, app_id):
316316
@account_initialization_required
317317
def post(self, app_id):
318318
# add app trace
319-
if not current_user.is_admin_or_owner:
319+
if not current_user.is_editing_role:
320320
raise Forbidden()
321321
parser = reqparse.RequestParser()
322322
parser.add_argument("enabled", type=bool, required=True, location="json")

api/controllers/console/app/workflow.py

+207-20
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import json
22
import logging
3+
from typing import cast
34

45
from flask import abort, request
56
from flask_restful import Resource, inputs, marshal_with, reqparse # type: ignore
7+
from sqlalchemy.orm import Session
68
from werkzeug.exceptions import Forbidden, InternalServerError, NotFound
79

810
import services
@@ -13,6 +15,7 @@
1315
from controllers.console.wraps import account_initialization_required, setup_required
1416
from core.app.apps.base_app_queue_manager import AppQueueManager
1517
from core.app.entities.app_invoke_entities import InvokeFrom
18+
from extensions.ext_database import db
1619
from factories import variable_factory
1720
from fields.workflow_fields import workflow_fields, workflow_pagination_fields
1821
from fields.workflow_run_fields import workflow_run_node_execution_fields
@@ -24,7 +27,7 @@
2427
from models.model import AppMode
2528
from services.app_generate_service import AppGenerateService
2629
from services.errors.app import WorkflowHashNotEqualError
27-
from services.workflow_service import WorkflowService
30+
from services.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError, WorkflowService
2831

2932
logger = logging.getLogger(__name__)
3033

@@ -439,10 +442,38 @@ def post(self, app_model: App):
439442
if not isinstance(current_user, Account):
440443
raise Forbidden()
441444

445+
parser = reqparse.RequestParser()
446+
parser.add_argument("marked_name", type=str, required=False, default="", location="json")
447+
parser.add_argument("marked_comment", type=str, required=False, default="", location="json")
448+
args = parser.parse_args()
449+
450+
# Validate name and comment length
451+
if args.marked_name and len(args.marked_name) > 20:
452+
raise ValueError("Marked name cannot exceed 20 characters")
453+
if args.marked_comment and len(args.marked_comment) > 100:
454+
raise ValueError("Marked comment cannot exceed 100 characters")
455+
442456
workflow_service = WorkflowService()
443-
workflow = workflow_service.publish_workflow(app_model=app_model, account=current_user)
457+
with Session(db.engine) as session:
458+
workflow = workflow_service.publish_workflow(
459+
session=session,
460+
app_model=app_model,
461+
account=current_user,
462+
marked_name=args.marked_name or "",
463+
marked_comment=args.marked_comment or "",
464+
)
465+
466+
app_model.workflow_id = workflow.id
467+
db.session.commit()
468+
469+
workflow_created_at = TimestampField().format(workflow.created_at)
444470

445-
return {"result": "success", "created_at": TimestampField().format(workflow.created_at)}
471+
session.commit()
472+
473+
return {
474+
"result": "success",
475+
"created_at": workflow_created_at,
476+
}
446477

447478

448479
class DefaultBlockConfigsApi(Resource):
@@ -564,37 +595,193 @@ def get(self, app_model: App):
564595
parser = reqparse.RequestParser()
565596
parser.add_argument("page", type=inputs.int_range(1, 99999), required=False, default=1, location="args")
566597
parser.add_argument("limit", type=inputs.int_range(1, 100), required=False, default=20, location="args")
598+
parser.add_argument("user_id", type=str, required=False, location="args")
599+
parser.add_argument("named_only", type=inputs.boolean, required=False, default=False, location="args")
600+
args = parser.parse_args()
601+
page = int(args.get("page", 1))
602+
limit = int(args.get("limit", 10))
603+
user_id = args.get("user_id")
604+
named_only = args.get("named_only", False)
605+
606+
if user_id:
607+
if user_id != current_user.id:
608+
raise Forbidden()
609+
user_id = cast(str, user_id)
610+
611+
workflow_service = WorkflowService()
612+
with Session(db.engine) as session:
613+
workflows, has_more = workflow_service.get_all_published_workflow(
614+
session=session,
615+
app_model=app_model,
616+
page=page,
617+
limit=limit,
618+
user_id=user_id,
619+
named_only=named_only,
620+
)
621+
622+
return {
623+
"items": workflows,
624+
"page": page,
625+
"limit": limit,
626+
"has_more": has_more,
627+
}
628+
629+
630+
class WorkflowByIdApi(Resource):
631+
@setup_required
632+
@login_required
633+
@account_initialization_required
634+
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
635+
@marshal_with(workflow_fields)
636+
def patch(self, app_model: App, workflow_id: str):
637+
"""
638+
Update workflow attributes
639+
"""
640+
# Check permission
641+
if not current_user.is_editor:
642+
raise Forbidden()
643+
644+
if not isinstance(current_user, Account):
645+
raise Forbidden()
646+
647+
parser = reqparse.RequestParser()
648+
parser.add_argument("marked_name", type=str, required=False, location="json")
649+
parser.add_argument("marked_comment", type=str, required=False, location="json")
567650
args = parser.parse_args()
568-
page = args.get("page")
569-
limit = args.get("limit")
651+
652+
# Validate name and comment length
653+
if args.marked_name and len(args.marked_name) > 20:
654+
raise ValueError("Marked name cannot exceed 20 characters")
655+
if args.marked_comment and len(args.marked_comment) > 100:
656+
raise ValueError("Marked comment cannot exceed 100 characters")
657+
args = parser.parse_args()
658+
659+
# Prepare update data
660+
update_data = {}
661+
if args.get("marked_name") is not None:
662+
update_data["marked_name"] = args["marked_name"]
663+
if args.get("marked_comment") is not None:
664+
update_data["marked_comment"] = args["marked_comment"]
665+
666+
if not update_data:
667+
return {"message": "No valid fields to update"}, 400
668+
570669
workflow_service = WorkflowService()
571-
workflows, has_more = workflow_service.get_all_published_workflow(app_model=app_model, page=page, limit=limit)
572670

573-
return {"items": workflows, "page": page, "limit": limit, "has_more": has_more}
671+
# Create a session and manage the transaction
672+
with Session(db.engine, expire_on_commit=False) as session:
673+
workflow = workflow_service.update_workflow(
674+
session=session,
675+
workflow_id=workflow_id,
676+
tenant_id=app_model.tenant_id,
677+
account_id=current_user.id,
678+
data=update_data,
679+
)
680+
681+
if not workflow:
682+
raise NotFound("Workflow not found")
683+
684+
# Commit the transaction in the controller
685+
session.commit()
686+
687+
return workflow
688+
689+
@setup_required
690+
@login_required
691+
@account_initialization_required
692+
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
693+
def delete(self, app_model: App, workflow_id: str):
694+
"""
695+
Delete workflow
696+
"""
697+
# Check permission
698+
if not current_user.is_editor:
699+
raise Forbidden()
700+
701+
if not isinstance(current_user, Account):
702+
raise Forbidden()
703+
704+
workflow_service = WorkflowService()
705+
706+
# Create a session and manage the transaction
707+
with Session(db.engine) as session:
708+
try:
709+
workflow_service.delete_workflow(
710+
session=session, workflow_id=workflow_id, tenant_id=app_model.tenant_id
711+
)
712+
# Commit the transaction in the controller
713+
session.commit()
714+
except WorkflowInUseError as e:
715+
abort(400, description=str(e))
716+
except DraftWorkflowDeletionError as e:
717+
abort(400, description=str(e))
718+
except ValueError as e:
719+
raise NotFound(str(e))
720+
721+
return None, 204
574722

575723

576-
api.add_resource(DraftWorkflowApi, "/apps/<uuid:app_id>/workflows/draft")
577-
api.add_resource(WorkflowConfigApi, "/apps/<uuid:app_id>/workflows/draft/config")
578-
api.add_resource(AdvancedChatDraftWorkflowRunApi, "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run")
579-
api.add_resource(DraftWorkflowRunApi, "/apps/<uuid:app_id>/workflows/draft/run")
580-
api.add_resource(WorkflowTaskStopApi, "/apps/<uuid:app_id>/workflow-runs/tasks/<string:task_id>/stop")
581-
api.add_resource(DraftWorkflowNodeRunApi, "/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/run")
724+
api.add_resource(
725+
DraftWorkflowApi,
726+
"/apps/<uuid:app_id>/workflows/draft",
727+
)
728+
api.add_resource(
729+
WorkflowConfigApi,
730+
"/apps/<uuid:app_id>/workflows/draft/config",
731+
)
732+
api.add_resource(
733+
AdvancedChatDraftWorkflowRunApi,
734+
"/apps/<uuid:app_id>/advanced-chat/workflows/draft/run",
735+
)
736+
api.add_resource(
737+
DraftWorkflowRunApi,
738+
"/apps/<uuid:app_id>/workflows/draft/run",
739+
)
740+
api.add_resource(
741+
WorkflowTaskStopApi,
742+
"/apps/<uuid:app_id>/workflow-runs/tasks/<string:task_id>/stop",
743+
)
744+
api.add_resource(
745+
DraftWorkflowNodeRunApi,
746+
"/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/run",
747+
)
582748
api.add_resource(
583749
AdvancedChatDraftRunIterationNodeApi,
584750
"/apps/<uuid:app_id>/advanced-chat/workflows/draft/iteration/nodes/<string:node_id>/run",
585751
)
586752
api.add_resource(
587-
WorkflowDraftRunIterationNodeApi, "/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run"
753+
WorkflowDraftRunIterationNodeApi,
754+
"/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run",
588755
)
589756
api.add_resource(
590757
AdvancedChatDraftRunLoopNodeApi,
591758
"/apps/<uuid:app_id>/advanced-chat/workflows/draft/loop/nodes/<string:node_id>/run",
592759
)
593-
api.add_resource(WorkflowDraftRunLoopNodeApi, "/apps/<uuid:app_id>/workflows/draft/loop/nodes/<string:node_id>/run")
594-
api.add_resource(PublishedWorkflowApi, "/apps/<uuid:app_id>/workflows/publish")
595-
api.add_resource(PublishedAllWorkflowApi, "/apps/<uuid:app_id>/workflows")
596-
api.add_resource(DefaultBlockConfigsApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs")
597760
api.add_resource(
598-
DefaultBlockConfigApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>"
761+
WorkflowDraftRunLoopNodeApi,
762+
"/apps/<uuid:app_id>/workflows/draft/loop/nodes/<string:node_id>/run",
763+
)
764+
api.add_resource(
765+
PublishedWorkflowApi,
766+
"/apps/<uuid:app_id>/workflows/publish",
767+
)
768+
api.add_resource(
769+
PublishedAllWorkflowApi,
770+
"/apps/<uuid:app_id>/workflows",
771+
)
772+
api.add_resource(
773+
DefaultBlockConfigsApi,
774+
"/apps/<uuid:app_id>/workflows/default-workflow-block-configs",
775+
)
776+
api.add_resource(
777+
DefaultBlockConfigApi,
778+
"/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>",
779+
)
780+
api.add_resource(
781+
ConvertToWorkflowApi,
782+
"/apps/<uuid:app_id>/convert-to-workflow",
783+
)
784+
api.add_resource(
785+
WorkflowByIdApi,
786+
"/apps/<uuid:app_id>/workflows/<string:workflow_id>",
599787
)
600-
api.add_resource(ConvertToWorkflowApi, "/apps/<uuid:app_id>/convert-to-workflow")

api/controllers/console/app/workflow_app_log.py

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
from datetime import datetime
2+
13
from flask_restful import Resource, marshal_with, reqparse # type: ignore
24
from flask_restful.inputs import int_range # type: ignore
5+
from sqlalchemy.orm import Session
36

47
from controllers.console import api
58
from controllers.console.app.wraps import get_app_model
69
from controllers.console.wraps import account_initialization_required, setup_required
10+
from extensions.ext_database import db
711
from fields.workflow_app_log_fields import workflow_app_log_pagination_fields
812
from libs.login import login_required
913
from models import App
1014
from models.model import AppMode
15+
from models.workflow import WorkflowRunStatus
1116
from services.workflow_app_service import WorkflowAppService
1217

1318

@@ -24,17 +29,38 @@ def get(self, app_model: App):
2429
parser = reqparse.RequestParser()
2530
parser.add_argument("keyword", type=str, location="args")
2631
parser.add_argument("status", type=str, choices=["succeeded", "failed", "stopped"], location="args")
32+
parser.add_argument(
33+
"created_at__before", type=str, location="args", help="Filter logs created before this timestamp"
34+
)
35+
parser.add_argument(
36+
"created_at__after", type=str, location="args", help="Filter logs created after this timestamp"
37+
)
2738
parser.add_argument("page", type=int_range(1, 99999), default=1, location="args")
2839
parser.add_argument("limit", type=int_range(1, 100), default=20, location="args")
2940
args = parser.parse_args()
3041

42+
args.status = WorkflowRunStatus(args.status) if args.status else None
43+
if args.created_at__before:
44+
args.created_at__before = datetime.fromisoformat(args.created_at__before.replace("Z", "+00:00"))
45+
46+
if args.created_at__after:
47+
args.created_at__after = datetime.fromisoformat(args.created_at__after.replace("Z", "+00:00"))
48+
3149
# get paginate workflow app logs
3250
workflow_app_service = WorkflowAppService()
33-
workflow_app_log_pagination = workflow_app_service.get_paginate_workflow_app_logs(
34-
app_model=app_model, args=args
35-
)
51+
with Session(db.engine) as session:
52+
workflow_app_log_pagination = workflow_app_service.get_paginate_workflow_app_logs(
53+
session=session,
54+
app_model=app_model,
55+
keyword=args.keyword,
56+
status=args.status,
57+
created_at_before=args.created_at__before,
58+
created_at_after=args.created_at__after,
59+
page=args.page,
60+
limit=args.limit,
61+
)
3662

37-
return workflow_app_log_pagination
63+
return workflow_app_log_pagination
3864

3965

4066
api.add_resource(WorkflowAppLogApi, "/apps/<uuid:app_id>/workflow-app-logs")

0 commit comments

Comments
 (0)