refactor: streamline trigger event node metadata handling and update async workflow service for JSON serialization

- Removed unnecessary input data from the TriggerEventNode's metadata.
- Updated AsyncWorkflowService to use model_dump_json() for trigger metadata serialization.
- Added a comment in WorkflowAppService to address the large size of the workflow_app_log table and the use of an additional details field.
This commit is contained in:
Harry
2025-10-28 13:54:44 +08:00
parent db352c0a18
commit 0d686fc6ae
9 changed files with 125 additions and 5 deletions

View File

@ -111,6 +111,7 @@ class AsyncWorkflowService:
app_id=trigger_data.app_id,
workflow_id=workflow.id,
root_node_id=trigger_data.root_node_id,
trigger_metadata=trigger_data.trigger_metadata.model_dump_json(),
trigger_type=trigger_data.trigger_type,
trigger_data=trigger_data.model_dump_json(),
inputs=json.dumps(dict(trigger_data.inputs)),

View File

@ -19,6 +19,12 @@ class AsyncTriggerStatus(StrEnum):
TIMEOUT = "timeout"
class TriggerMetadata(BaseModel):
"""Trigger metadata"""
pass
class TriggerData(BaseModel):
"""Base trigger data model for async workflow execution"""
@ -30,6 +36,7 @@ class TriggerData(BaseModel):
files: Sequence[Mapping[str, Any]] = Field(default_factory=list)
trigger_type: AppTriggerType
trigger_from: WorkflowRunTriggeredFrom
trigger_metadata: TriggerMetadata = Field(default_factory=TriggerMetadata)
model_config = ConfigDict(use_enum_values=True)
@ -48,6 +55,17 @@ class ScheduleTriggerData(TriggerData):
trigger_from: WorkflowRunTriggeredFrom = WorkflowRunTriggeredFrom.SCHEDULE
class PluginTriggerMetadata(TriggerMetadata):
"""Plugin trigger metadata"""
plugin_id: str
endpoint_id: str
plugin_unique_identifier: str
provider_id: str
icon_url: str
icon_dark_url: str
class PluginTriggerData(TriggerData):
"""Plugin webhook trigger data"""

View File

@ -1,3 +1,4 @@
import json
import uuid
from datetime import datetime
@ -7,6 +8,35 @@ from sqlalchemy.orm import Session
from core.workflow.enums import WorkflowExecutionStatus
from models import Account, App, EndUser, WorkflowAppLog, WorkflowRun
from models.enums import CreatorUserRole
from models.trigger import WorkflowTriggerLog
# Since the workflow_app_log table has exceeded 100 million records, we use an additional details field to extend it
class LogView:
"""Lightweight wrapper for WorkflowAppLog with computed details.
- Exposes `details_` for marshalling to `details` in API response
- Proxies all other attributes to the underlying `WorkflowAppLog`
"""
def __init__(self, log: WorkflowAppLog, details: dict | None):
self.log = log
self.details_ = details
def __getattr__(self, name):
return getattr(self.log, name)
# Helpers
def _safe_json_loads(val):
if not val:
return None
if isinstance(val, str):
try:
return json.loads(val)
except Exception:
return None
return val
class WorkflowAppService:
@ -21,6 +51,7 @@ class WorkflowAppService:
created_at_after: datetime | None = None,
page: int = 1,
limit: int = 20,
detail: bool = False,
created_by_end_user_session_id: str | None = None,
created_by_account: str | None = None,
):
@ -34,6 +65,7 @@ class WorkflowAppService:
:param created_at_after: filter logs created after this timestamp
:param page: page number
:param limit: items per page
:param detail: whether to return detailed logs
:param created_by_end_user_session_id: filter by end user session id
:param created_by_account: filter by account email
:return: Pagination object
@ -43,8 +75,24 @@ class WorkflowAppService:
WorkflowAppLog.tenant_id == app_model.tenant_id, WorkflowAppLog.app_id == app_model.id
)
if detail:
# Correlated scalar subquery: fetch latest trigger_metadata per workflow_run_id
meta_expr = (
select(WorkflowTriggerLog.trigger_metadata)
.where(
WorkflowTriggerLog.workflow_run_id == WorkflowAppLog.workflow_run_id,
WorkflowTriggerLog.app_id == app_model.id,
WorkflowTriggerLog.tenant_id == app_model.tenant_id,
)
.order_by(WorkflowTriggerLog.created_at.desc())
.limit(1)
.scalar_subquery()
)
stmt = stmt.add_columns(meta_expr)
if keyword or status:
stmt = stmt.join(WorkflowRun, WorkflowRun.id == WorkflowAppLog.workflow_run_id)
# Join to workflow run for filtering when needed.
if keyword:
keyword_like_val = f"%{keyword[:30].encode('unicode_escape').decode('utf-8')}%".replace(r"\u", r"\\u")
@ -108,9 +156,14 @@ class WorkflowAppService:
# Apply pagination limits
offset_stmt = stmt.offset((page - 1) * limit).limit(limit)
# Execute query and get items
items = list(session.scalars(offset_stmt).all())
# wrapper moved to module scope as `LogView`
# Execute query and get items
if detail:
rows = session.execute(offset_stmt).all()
items = [LogView(log, {"trigger_metadata": _safe_json_loads(meta_val)}) for log, meta_val in rows]
else:
items = [LogView(log, None) for log in session.scalars(offset_stmt).all()]
return {
"page": page,
"limit": limit,