Agent Log Event Pipeline#
The agent log event pipeline carries structured, per-step log messages from an agent strategy plugin all the way to the frontend UI. It is distinct from the older QueueAgentThoughtEvent path (used by Agent Apps) and exists specifically for workflow-embedded Agent Nodes. Each log entry represents one logical step (a round, a model call, or a tool invocation) and can be nested via parent–child IDs.
As of PR #39845, AgentLogEvent and NodeRunAgentLogEvent are defined locally in core.workflow.nodes.agent.events, separating AgentNode-specific events from graphon's generic event system while preserving the pipeline flow.
Agent strategy plugin (ToolInvokeMessage.LOG)
│
▼
AgentMessageTransformer.transform() ← api/core/workflow/nodes/agent/message_transformer.py
│ yields AgentLogEvent (node event)
▼
AgentNode._dispatch() ← api/core/workflow/nodes/agent/agent_node.py
│ AgentLogEvent → NodeRunAgentLogEvent
▼
WorkflowBasedAppRunner ← api/core/app/apps/workflow_app_runner.py
│ NodeRunAgentLogEvent → QueueAgentLogEvent
▼
WorkflowAppGenerateTaskPipeline ← api/core/app/apps/workflow/generate_task_pipeline.py
│ _handle_agent_log_event → AgentLogStreamResponse (SSE: event=agent_log)
▼
Frontend: useWorkflowAgentLog / AgentLogItem
Key Data Structures#
QueueAgentLogEvent#
Defined in queue_entities.py. Fields: id, label, node_execution_id, parent_id (nullable), error (nullable), status, data (arbitrary mapping), metadata (generic mapping), node_id. The event discriminator is QueueEvent.AGENT_LOG .
AgentLogItem (frontend)#
Typed in web/types/workflow.ts. Mirrors the backend payload with the addition of a metadata.elapsed_time?: number field used for the UI timer display .
AgentLogItemWithChildren#
Extends AgentLogItem with a recursive children: AgentLogItemWithChildren[] array and a hasCircle?: boolean flag for cycle detection . The flat list emitted over SSE is converted to this tree by listToTree() in web/app/components/workflow/run/utils/format-log/agent/index.ts.
Backend: From Plugin to Queue Event#
1. Plugin emits ToolInvokeMessage.LOG#
Agent strategy plugins (e.g., the official FunctionCalling and ReAct strategies) emit ToolInvokeMessage.MessageType.LOG messages. Each message carries a LogMessage with id, parent_id, label, status, data, and a metadata dict. The metadata dict is populated by the plugin with fields including:
elapsed_time— computed viatime.perf_counter()at round, model-call, and tool-call granularityprovider— tool provider name, used to resolve the display icon
2. AgentMessageTransformer enriches icon metadata#
AgentMessageTransformer.transform() iterates the message stream. On LOG messages it:
- Resolves the provider icon — first from plugin declarations, then from builtin tool listings .
- Constructs an
AgentLogEvent(defined incore.workflow.nodes.agent.events) from the message fields . - Deduplicates by
message_id—later events with the same ID overwrite earlier ones, enabling in-place status updates fromstart→success/error. - Yields the
AgentLogEventimmediately so it streams to the client before the node completes .
At node completion, agent_logs are also serialised into execution_metadata[AGENT_LOG] and json output .
3. AgentNode dispatches to graph event#
AgentNode._dispatch() overrides the base node dispatcher with a singledispatchmethod that converts local AgentLogEvent (node event) to NodeRunAgentLogEvent (graph event, also defined in core.workflow.nodes.agent.events). Other node events are delegated to the base _dispatch() method, which handles them via graphon's generic event conversion.
4. WorkflowBasedAppRunner bridges to queue#
The NodeRunAgentLogEvent is caught in workflow_app_runner.py and re-published as a QueueAgentLogEvent, passing all fields through unchanged.
5. Task pipeline converts to SSE#
WorkflowAppGenerateTaskPipeline._handle_agent_log_event() calls workflow_response_converter.handle_agent_log(), which returns an AgentLogStreamResponse with event=agent_log. The SSE payload is a flat Data object mirroring QueueAgentLogEvent fields.
Frontend: Streaming and Historical Display#
Streaming path#
Incoming agent_log SSE events are handled by useWorkflowAgentLog. The hook:
- Finds the matching node in
workflowRunningData.tracingbynode_id. - Upserts the log entry into
execution_metadata.agent_logbymessage_id, so anupdateevent overwrites the originalstartentry .
AgentNode and ToolNode entries accumulate log items in-place without re-sorting the tracing array.
Tree conversion#
Once logs are populated, format() in web/app/components/workflow/run/utils/format-log/agent/index.ts transforms the flat agent_log array into an AgentLogItemWithChildren[] tree via listToTree(). The function uses message_id/parent_id pairs to build parent–child relationships, deduplicates siblings by ID, and calls removeCircleLogItem() to strip circular references (which can appear if a round log wraps its own child tool log).
Rendering#
AgentLogItem renders one log entry:
- Icon — resolved from
metadata.icon; falls back to a generic Agent block icon if absent . - Status badge — maps
"start"→"running"for theNodeStatusIcon. - Elapsed time —
metadata.elapsed_time?.toFixed(3) + "s"displayed only when present . - Nested actions — when expanded and
childrenexist, shows an"{n} Action Logs"button that drills into child logs viaonShowAgentOrToolLog. - Data panel — renders raw
dataas pretty-printed JSON when expanded .
The trigger that opens the log panel is AgentLogTrigger, rendered by result-panel.tsx for any Agent or Tool node that has execution_metadata.agent_log data.
Notes on Historical View#
For completed workflow runs, agent logs are not re-fetched via a dedicated endpoint—they are persisted in execution_metadata.agent_log on the WorkflowNodeExecution record. The frontend reads these from the node tracing API and runs the same format() tree conversion. The AgentLogTrigger component checks for this field and renders identically for both the live and historical cases.
Key Entry Points#
| Layer | File | Purpose |
|---|---|---|
| Plugin SDK | agent-strategies/cot_agent/strategies/function_calling.py | Emits LOG messages with elapsed_time |
| Plugin SDK | agent-strategies/cot_agent/strategies/ReAct.py | Same for ReAct strategy |
| Event types | api/core/workflow/nodes/agent/events.py | AgentLogEvent (node event), NodeRunAgentLogEvent (graph event) |
| Transformer | api/core/workflow/nodes/agent/message_transformer.py | Converts ToolInvokeMessage.LOG → AgentLogEvent, resolves icons |
| Node | api/core/workflow/nodes/agent/agent_node.py | Calls AgentMessageTransformer.transform(), dispatches AgentLogEvent → NodeRunAgentLogEvent |
| Queue entities | api/core/app/entities/queue_entities.py | QueueAgentLogEvent definition |
| App runner | api/core/app/apps/workflow_app_runner.py | NodeRunAgentLogEvent → QueueAgentLogEvent |
| Task pipeline | api/core/app/apps/workflow/generate_task_pipeline.py | Routes to handle_agent_log() |
| Frontend hook | web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts | SSE → store upsert |
| Frontend util | web/app/components/workflow/run/utils/format-log/agent/index.ts | Flat list → tree |
| Frontend UI | web/app/components/workflow/run/agent-log/agent-log-item.tsx | Renders one log entry |
| Frontend types | web/types/workflow.ts | AgentLogItem, AgentLogItemWithChildren |