Skip to content

feat(workflow): expose prompt and completion tokens in workflow run responses#37531

Open
priyansh19 wants to merge 2 commits into
langgenius:mainfrom
priyansh19:feat-34315-workflow-io-tokens
Open

feat(workflow): expose prompt and completion tokens in workflow run responses#37531
priyansh19 wants to merge 2 commits into
langgenius:mainfrom
priyansh19:feat-34315-workflow-io-tokens

Conversation

@priyansh19

Copy link
Copy Markdown

Summary

Fixes #34315.

Workflow apps previously returned only total_tokens in their run results, while
chatflow apps already expose prompt_tokens (input) and completion_tokens (output)
through the usage metadata. Because most model providers price input and output
tokens differently, workflow consumers had no way to break down or estimate cost from
the run result.

The graph runtime already aggregates the input/output split across all LLM-backed nodes
in graph_runtime_state.llm_usage (maintained alongside the scalar total_tokens).
This PR surfaces that existing data without changing how it is computed:

  • WorkflowFinishStreamResponse.Data (the streaming workflow_finished event) now
    includes prompt_tokens and completion_tokens, populated from
    graph_runtime_state.llm_usage.
  • WorkflowAppBlockingResponse.Data (the blocking workflow run response) carries the
    same two fields through from the finish event.
  • The OpenAPI WorkflowRunData schema documents the new fields.

The new fields default to 0, so behavior is unchanged for runs without LLM usage and
the change is backward compatible for existing API consumers.

Note on persistence scope

This change exposes the split in the live run responses (streaming + blocking), which is
where the issue reports the gap. Persisting prompt_tokens / completion_tokens on the
workflow_runs table (so historical reads via the console/service log APIs also show the
split) would require carrying the fields on the WorkflowExecution domain entity, which
currently lives in the external graphon package and only holds total_tokens. That can
follow as a separate change once the upstream entity supports it; happy to do it in a
follow-up if maintainers prefer.

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.
  • I ran make lint && make type-check (backend) and cd web && pnpm exec vp staged (frontend) to appease the lint gods

From Cursor

…esponses

Workflow apps previously only returned total_tokens, while chatflow apps expose prompt_tokens and completion_tokens via usage. Most providers price input and output tokens differently, so workflow consumers could not break down cost. The graph runtime already aggregates the split in graph_runtime_state.llm_usage; this surfaces it in the workflow_finished stream event and the blocking workflow response (plus the OpenAPI schema), defaulting to 0 when no LLM usage is recorded. Closes langgenius#34315.
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jun 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Pyrefly Diff

base → PR
--- /tmp/pyrefly_base.txt	2026-06-16 14:18:18.592313562 +0000
+++ /tmp/pyrefly_pr.txt	2026-06-16 14:18:09.689232697 +0000
@@ -3023,6 +3023,10 @@
   --> tests/unit_tests/core/app/apps/common/test_workflow_response_converter_resumption.py:31:37
 ERROR Argument `SimpleNamespace` is not assignable to parameter `user` with type `Account | EndUser` in function `core.app.apps.common.workflow_response_converter.WorkflowResponseConverter.__init__` [bad-argument-type]
   --> tests/unit_tests/core/app/apps/common/test_workflow_response_converter_resumption.py:32:14
+ERROR Argument `SimpleNamespace` is not assignable to parameter `application_generate_entity` with type `AdvancedChatAppGenerateEntity | WorkflowAppGenerateEntity` in function `core.app.apps.common.workflow_response_converter.WorkflowResponseConverter.__init__` [bad-argument-type]
+  --> tests/unit_tests/core/app/apps/common/test_workflow_response_converter_token_usage.py:34:37
+ERROR Argument `SimpleNamespace` is not assignable to parameter `user` with type `Account | EndUser` in function `core.app.apps.common.workflow_response_converter.WorkflowResponseConverter.__init__` [bad-argument-type]
+  --> tests/unit_tests/core/app/apps/common/test_workflow_response_converter_token_usage.py:35:14
 ERROR Object of class `NoneType` has no attribute `data` [missing-attribute]
    --> tests/unit_tests/core/app/apps/common/test_workflow_response_converter_truncation.py:588:16
 ERROR Argument `dict[str, dict[str, str]]` is not assignable to parameter `override_config_dict` with type `AppModelConfigDict | None` in function `core.app.apps.completion.app_config_manager.CompletionAppConfigManager.get_app_config` [bad-argument-type]

@autofix-ci autofix-ci Bot requested a review from crazywoola as a code owner June 16, 2026 14:19
@github-actions

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 48.59% 48.59% -0.00%
Strict coverage 48.10% 48.10% -0.00%
Typed symbols 27,994 27,994 0
Untyped symbols 29,922 29,925 +3
Modules 2892 2893 +1

@github-actions github-actions Bot added the web This relates to changes on the web. label Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files. web This relates to changes on the web.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Workflow usage should track input/output tokens separately

1 participant