Skip to content

Commit 5d41f67

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/support-agent-sandbox
# Conflicts: # api/core/workflow/graph_events/__init__.py
2 parents ab52550 + e482588 commit 5d41f67

File tree

124 files changed

+28863
-726
lines changed

Some content is hidden

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

124 files changed

+28863
-726
lines changed

api/controllers/console/datasets/datasets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ class DatasetListApi(Resource):
286286
@enterprise_license_required
287287
def get(self):
288288
current_user, current_tenant_id = current_account_with_tenant()
289-
query = ConsoleDatasetListQuery.model_validate(request.args.to_dict(flat=False))
289+
query = ConsoleDatasetListQuery.model_validate(request.args.to_dict())
290290
# provider = request.args.get("provider", default="vendor")
291291
if query.ids:
292292
datasets, total = DatasetService.get_datasets_by_ids(query.ids, current_tenant_id)

api/controllers/service_api/dataset/dataset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class DatasetListApi(DatasetApiResource):
122122
)
123123
def get(self, tenant_id):
124124
"""Resource for getting datasets."""
125-
query = DatasetListQuery.model_validate(request.args.to_dict(flat=False))
125+
query = DatasetListQuery.model_validate(request.args.to_dict())
126126
# provider = request.args.get("provider", default="vendor")
127127

128128
datasets, total = DatasetService.get_datasets(

api/core/workflow/graph_engine/layers/base.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from abc import ABC, abstractmethod
99

1010
from core.workflow.graph_engine.protocols.command_channel import CommandChannel
11-
from core.workflow.graph_events import GraphEngineEvent
11+
from core.workflow.graph_events import GraphEngineEvent, GraphNodeEventBase
1212
from core.workflow.nodes.base.node import Node
1313
from core.workflow.runtime import ReadOnlyGraphRuntimeState
1414

@@ -98,7 +98,7 @@ def on_graph_end(self, error: Exception | None) -> None:
9898
"""
9999
pass
100100

101-
def on_node_run_start(self, node: Node) -> None: # noqa: B027
101+
def on_node_run_start(self, node: Node) -> None:
102102
"""
103103
Called immediately before a node begins execution.
104104
@@ -109,9 +109,11 @@ def on_node_run_start(self, node: Node) -> None: # noqa: B027
109109
Args:
110110
node: The node instance about to be executed
111111
"""
112-
pass
112+
return
113113

114-
def on_node_run_end(self, node: Node, error: Exception | None) -> None: # noqa: B027
114+
def on_node_run_end(
115+
self, node: Node, error: Exception | None, result_event: GraphNodeEventBase | None = None
116+
) -> None:
115117
"""
116118
Called after a node finishes execution.
117119
@@ -121,5 +123,6 @@ def on_node_run_end(self, node: Node, error: Exception | None) -> None: # noqa:
121123
Args:
122124
node: The node instance that just finished execution
123125
error: Exception instance if the node failed, otherwise None
126+
result_event: The final result event from node execution (succeeded/failed/paused), if any
124127
"""
125-
pass
128+
return

api/core/workflow/graph_engine/layers/node_parsers.py

Lines changed: 0 additions & 61 deletions
This file was deleted.

api/core/workflow/graph_engine/layers/observability.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
from configs import dify_config
1919
from core.workflow.enums import NodeType
2020
from core.workflow.graph_engine.layers.base import GraphEngineLayer
21-
from core.workflow.graph_engine.layers.node_parsers import (
21+
from core.workflow.graph_events import GraphNodeEventBase
22+
from core.workflow.nodes.base.node import Node
23+
from extensions.otel.parser import (
2224
DefaultNodeOTelParser,
25+
LLMNodeOTelParser,
2326
NodeOTelParser,
27+
RetrievalNodeOTelParser,
2428
ToolNodeOTelParser,
2529
)
26-
from core.workflow.nodes.base.node import Node
2730
from extensions.otel.runtime import is_instrument_flag_enabled
2831

2932
logger = logging.getLogger(__name__)
@@ -72,6 +75,8 @@ def _build_parser_registry(self) -> None:
7275
"""Initialize parser registry for node types."""
7376
self._parsers = {
7477
NodeType.TOOL: ToolNodeOTelParser(),
78+
NodeType.LLM: LLMNodeOTelParser(),
79+
NodeType.KNOWLEDGE_RETRIEVAL: RetrievalNodeOTelParser(),
7580
}
7681

7782
def _get_parser(self, node: Node) -> NodeOTelParser:
@@ -119,7 +124,9 @@ def on_node_run_start(self, node: Node) -> None:
119124
logger.warning("Failed to create OpenTelemetry span for node %s: %s", node.id, e)
120125

121126
@override
122-
def on_node_run_end(self, node: Node, error: Exception | None) -> None:
127+
def on_node_run_end(
128+
self, node: Node, error: Exception | None, result_event: GraphNodeEventBase | None = None
129+
) -> None:
123130
"""
124131
Called when a node finishes execution.
125132
@@ -139,7 +146,7 @@ def on_node_run_end(self, node: Node, error: Exception | None) -> None:
139146
span = node_context.span
140147
parser = self._get_parser(node)
141148
try:
142-
parser.parse(node=node, span=span, error=error)
149+
parser.parse(node=node, span=span, error=error, result_event=result_event)
143150
span.end()
144151
finally:
145152
token = node_context.token

api/core/workflow/graph_engine/worker.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from core.workflow.context import IExecutionContext
1818
from core.workflow.graph import Graph
1919
from core.workflow.graph_engine.layers.base import GraphEngineLayer
20-
from core.workflow.graph_events import GraphNodeEventBase, NodeRunFailedEvent
20+
from core.workflow.graph_events import GraphNodeEventBase, NodeRunFailedEvent, is_node_result_event
2121
from core.workflow.nodes.base.node import Node
2222

2323
from .ready_queue import ReadyQueue
@@ -131,6 +131,7 @@ def _execute_node(self, node: Node) -> None:
131131
node.ensure_execution_id()
132132

133133
error: Exception | None = None
134+
result_event: GraphNodeEventBase | None = None
134135

135136
# Execute the node with preserved context if execution context is provided
136137
if self._execution_context is not None:
@@ -140,22 +141,26 @@ def _execute_node(self, node: Node) -> None:
140141
node_events = node.run()
141142
for event in node_events:
142143
self._event_queue.put(event)
144+
if is_node_result_event(event):
145+
result_event = event
143146
except Exception as exc:
144147
error = exc
145148
raise
146149
finally:
147-
self._invoke_node_run_end_hooks(node, error)
150+
self._invoke_node_run_end_hooks(node, error, result_event)
148151
else:
149152
self._invoke_node_run_start_hooks(node)
150153
try:
151154
node_events = node.run()
152155
for event in node_events:
153156
self._event_queue.put(event)
157+
if is_node_result_event(event):
158+
result_event = event
154159
except Exception as exc:
155160
error = exc
156161
raise
157162
finally:
158-
self._invoke_node_run_end_hooks(node, error)
163+
self._invoke_node_run_end_hooks(node, error, result_event)
159164

160165
def _invoke_node_run_start_hooks(self, node: Node) -> None:
161166
"""Invoke on_node_run_start hooks for all layers."""
@@ -166,11 +171,13 @@ def _invoke_node_run_start_hooks(self, node: Node) -> None:
166171
# Silently ignore layer errors to prevent disrupting node execution
167172
continue
168173

169-
def _invoke_node_run_end_hooks(self, node: Node, error: Exception | None) -> None:
174+
def _invoke_node_run_end_hooks(
175+
self, node: Node, error: Exception | None, result_event: GraphNodeEventBase | None = None
176+
) -> None:
170177
"""Invoke on_node_run_end hooks for all layers."""
171178
for layer in self._layers:
172179
try:
173-
layer.on_node_run_end(node, error)
180+
layer.on_node_run_end(node, error, result_event)
174181
except Exception:
175182
# Silently ignore layer errors to prevent disrupting node execution
176183
continue

api/core/workflow/graph_events/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
NodeRunSucceededEvent,
4848
ToolCall,
4949
ToolResult,
50+
is_node_result_event,
5051
)
5152

5253
__all__ = [
@@ -79,4 +80,5 @@
7980
"NodeRunSucceededEvent",
8081
"ToolCall",
8182
"ToolResult",
83+
"is_node_result_event",
8284
]

api/core/workflow/graph_events/node.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,26 @@ class NodeRunRetryEvent(NodeRunStartedEvent):
8383

8484
class NodeRunPauseRequestedEvent(GraphNodeEventBase):
8585
reason: PauseReason = Field(..., description="pause reason")
86+
87+
88+
def is_node_result_event(event: GraphNodeEventBase) -> bool:
89+
"""
90+
Check if an event is a final result event from node execution.
91+
92+
A result event indicates the completion of a node execution and contains
93+
runtime information such as inputs, outputs, or error details.
94+
95+
Args:
96+
event: The event to check
97+
98+
Returns:
99+
True if the event is a node result event (succeeded/failed/paused), False otherwise
100+
"""
101+
return isinstance(
102+
event,
103+
(
104+
NodeRunSucceededEvent,
105+
NodeRunFailedEvent,
106+
NodeRunPauseRequestedEvent,
107+
),
108+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""
2+
OpenTelemetry node parsers for workflow nodes.
3+
4+
This module provides parsers that extract node-specific metadata and set
5+
OpenTelemetry span attributes according to semantic conventions.
6+
"""
7+
8+
from extensions.otel.parser.base import DefaultNodeOTelParser, NodeOTelParser, safe_json_dumps
9+
from extensions.otel.parser.llm import LLMNodeOTelParser
10+
from extensions.otel.parser.retrieval import RetrievalNodeOTelParser
11+
from extensions.otel.parser.tool import ToolNodeOTelParser
12+
13+
__all__ = [
14+
"DefaultNodeOTelParser",
15+
"LLMNodeOTelParser",
16+
"NodeOTelParser",
17+
"RetrievalNodeOTelParser",
18+
"ToolNodeOTelParser",
19+
"safe_json_dumps",
20+
]

0 commit comments

Comments
 (0)