diff --git a/api/core/workflow/nodes/agent_v2/output_adapter.py b/api/core/workflow/nodes/agent_v2/output_adapter.py index f2d408a7999..e4e8e34b03b 100644 --- a/api/core/workflow/nodes/agent_v2/output_adapter.py +++ b/api/core/workflow/nodes/agent_v2/output_adapter.py @@ -69,15 +69,18 @@ class WorkflowAgentOutputAdapter: usage = self._usage_from_metadata(metadata) metadata_tenant_id = metadata.get("tenant_id") resolved_tenant_id = tenant_id or (metadata_tenant_id if isinstance(metadata_tenant_id, str) else None) + outputs = self._normalize_outputs( + event.output, + declared_outputs=declared_outputs, + tenant_id=resolved_tenant_id, + ) + if event.usage is not None: + outputs["usage"] = dict(event.usage) return NodeRunResult( status=WorkflowNodeExecutionStatus.SUCCEEDED, inputs=inputs, process_data=process_data, - outputs=self._normalize_outputs( - event.output, - declared_outputs=declared_outputs, - tenant_id=resolved_tenant_id, - ), + outputs=outputs, metadata=self._build_node_metadata(metadata=metadata, usage=usage), llm_usage=usage or LLMUsage.empty_usage(), ) diff --git a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_output_adapter.py b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_output_adapter.py index 33f41a5ea3d..fbf27ac7e6b 100644 --- a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_output_adapter.py +++ b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_output_adapter.py @@ -584,6 +584,20 @@ def test_success_output_adapter_maps_backend_usage_to_llm_usage_and_metadata(): assert result.llm_usage.completion_price == Decimal("0.000150") assert result.llm_usage.total_price == Decimal("0.000200") assert result.llm_usage.currency == "USD" + assert result.outputs["usage"] == { + "prompt_tokens": 10, + "prompt_unit_price": "5", + "prompt_price_unit": "0.000001", + "prompt_price": "0.000050", + "completion_tokens": 5, + "completion_unit_price": "30", + "completion_price_unit": "0.000001", + "completion_price": "0.000150", + "total_tokens": 15, + "total_price": "0.000200", + "currency": "USD", + "latency": 0.5, + } assert result.metadata[WorkflowNodeExecutionMetadataKey.TOTAL_TOKENS] == 15 assert result.metadata[WorkflowNodeExecutionMetadataKey.TOTAL_PRICE] == Decimal("0.000200") assert result.metadata[WorkflowNodeExecutionMetadataKey.CURRENCY] == "USD" diff --git a/web/app/components/workflow/nodes/agent-v2/__tests__/panel.spec.tsx b/web/app/components/workflow/nodes/agent-v2/__tests__/panel.spec.tsx index 6a722f72abb..e3ca79f39fa 100644 --- a/web/app/components/workflow/nodes/agent-v2/__tests__/panel.spec.tsx +++ b/web/app/components/workflow/nodes/agent-v2/__tests__/panel.spec.tsx @@ -446,12 +446,12 @@ describe('agent/panel', () => { ).not.toBeInTheDocument() expect(screen.getByText('text')).toBeInTheDocument() expect(screen.getByText('workflow.nodes.agent.outputVars.text')).toBeInTheDocument() - expect(screen.queryByText('usage')).not.toBeInTheDocument() + expect(screen.getByText('usage')).toBeInTheDocument() expect(screen.getByText('files')).toBeInTheDocument() expect(screen.getByText('array[file]')).toBeInTheDocument() expect(screen.getByText('workflow.nodes.agent.outputVars.files.title')).toBeInTheDocument() expect(screen.getByText('json')).toBeInTheDocument() - expect(screen.getByText('object')).toBeInTheDocument() + expect(screen.getAllByText('object')).toHaveLength(2) expect( screen.getByRole('button', { name: 'workflow.nodes.agent.outputVars.newOutput' }), ).toBeInTheDocument() diff --git a/web/app/components/workflow/nodes/agent-v2/output-variables.ts b/web/app/components/workflow/nodes/agent-v2/output-variables.ts index c4201fd18b9..382acdfe35a 100644 --- a/web/app/components/workflow/nodes/agent-v2/output-variables.ts +++ b/web/app/components/workflow/nodes/agent-v2/output-variables.ts @@ -19,6 +19,12 @@ export const defaultAgentV2DeclaredOutputs: DeclaredOutputConfig[] = [ type: 'file', }, }, + { + name: 'usage', + type: 'object', + required: false, + description: 'LLM usage reported by the agent run.', + }, { name: 'json', type: 'object',