Skip to content

fix(agent): preserve complete model usage pricing#39201

Merged
lyzno1 merged 2 commits into
mainfrom
fix/dify-2815-agent-usage-price-main
Jul 20, 2026
Merged

fix(agent): preserve complete model usage pricing#39201
lyzno1 merged 2 commits into
mainfrom
fix/dify-2815-agent-usage-price-main

Conversation

@zyssyz123

Copy link
Copy Markdown
Contributor

Summary

  • preserve the complete Graphon LLMUsage returned by plugin daemon model calls instead of reducing it to token counts
  • aggregate pricing and timing metadata across streaming, tool-call, and retry rounds without double-counting cumulative stream usage
  • emit the complete usage payload from Agent backend so Agent App messages and Workflow Agent nodes persist token prices, total price, currency, and latency
  • keep backward compatibility when replaying older token-only Agent events

Root cause

The Agent model adapter converted plugin daemon LLMUsage into Pydantic AI RequestUsage, which only carries token counts. The terminal Agent event therefore omitted pricing fields. API reconstructed LLMUsage from that incomplete payload and persisted total_price = 0, so Agent Monitor correctly aggregated a zero database value.

Verification

  • Dify Agent targeted tests: 79 passed
  • API event adapter, Agent App runner, and Workflow Agent output adapter: 68 passed
  • Ruff: passed
  • basedpyright on changed production files: 0 errors
  • dev deployment from deploy/agent verified both Agent backend and API/worker images use the fix commit
  • dev Agent App SSE + history + database + Monitor E2E: 5,368 tokens / $0.027165
  • dev multi-round knowledge tool E2E (knowledge_base_search): aggregated 11,401 tokens / $0.05853, matching persisted message usage
  • dev Workflow Agent Node E2E: node and workflow completed with 6,412 tokens / $0.040760, matching node execution metadata

Linear: https://linear.app/dify/issue/DIFY-2815/agent-monitor-%E7%9A%84-token-usage-%E6%98%BE%E7%A4%BA%E9%97%AE%E9%A2%98

@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Diff

base → PR
--- /tmp/pyrefly_base.txt	2026-07-20 07:49:06.884176334 +0000
+++ /tmp/pyrefly_pr.txt	2026-07-20 07:49:02.088167554 +0000
@@ -1763,17 +1763,17 @@
 ERROR Argument `dict[str, object]` is not assignable to parameter `configs` with type `Mapping[str, LayerConfigInput] | None` in function `agenton.compositor.core.Compositor._create_run` [bad-argument-type]
   --> tests/unit_tests/clients/agent_backend/test_cleanup_composition_compositor_integration.py:97:17
 ERROR Cannot index into `bool` [bad-index]
-  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:55:12
+  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:57:12
 ERROR Cannot index into `float` [bad-index]
-  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:55:12
+  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:57:12
 ERROR Cannot index into `int` [bad-index]
-  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:55:12
+  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:57:12
 ERROR Cannot index into `list[JsonValue]` [bad-index]
-  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:55:12
+  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:57:12
 ERROR Cannot index into `str` [bad-index]
-  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:55:12
+  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:57:12
 ERROR `None` is not subscriptable [unsupported-operation]
-  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:55:12
+  --> tests/unit_tests/clients/agent_backend/test_event_adapter.py:57:12
 ERROR Object of class `list` has no attribute `startswith` [missing-attribute]
    --> tests/unit_tests/clients/agent_backend/test_request_builder.py:159:12
 ERROR Class member `_FailedStatusClient.wait_run` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
@@ -3180,29 +3180,37 @@
 ERROR Object of class `FunctionType` has no attribute `return_value` [missing-attribute]
    --> tests/unit_tests/core/app/apps/agent_app/test_app_generator.py:465:25
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:563:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:576:12
 ERROR Object of class `NoneType` has no attribute `model` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:564:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:577:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:646:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:659:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:666:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:679:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:713:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:726:12
 ERROR Object of class `NoneType` has no attribute `usage` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:714:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:727:12
 ERROR Object of class `NoneType` has no attribute `usage` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:715:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:728:12
 ERROR Object of class `NoneType` has no attribute `usage` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:716:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:729:12
+ERROR Object of class `NoneType` has no attribute `usage` [missing-attribute]
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:730:12
+ERROR Object of class `NoneType` has no attribute `usage` [missing-attribute]
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:731:12
+ERROR Object of class `NoneType` has no attribute `usage` [missing-attribute]
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:732:12
+ERROR Object of class `NoneType` has no attribute `usage` [missing-attribute]
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:733:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:737:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:754:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:755:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:772:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:773:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:790:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
-   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:794:12
+   --> tests/unit_tests/core/app/apps/agent_app/test_app_runner.py:811:12
 ERROR Object of class `NoneType` has no attribute `message` [missing-attribute]
   --> tests/unit_tests/core/app/apps/agent_app/test_input_guards.py:67:12
 ERROR Object of class `NoneType` has no attribute `prompt_messages` [missing-attribute]
@@ -6304,7 +6312,7 @@
 ERROR Cannot index into `dict[str, JsonValue]` [bad-index]
    --> tests/unit_tests/core/workflow/nodes/agent_v2/test_dify_tools_builder.py:393:43
 ERROR Argument `object` is not assignable to parameter `output` with type `Decimal | Iterable[Any] | Mapping[str, Any] | bool | bytearray | bytes | float | int | str | None` in function `clients.agent_backend.event_adapter.AgentBackendRunSucceededInternalEvent.__init__` [bad-argument-type]
-  --> tests/unit_tests/core/workflow/nodes/agent_v2/test_output_adapter.py:49:16
+  --> tests/unit_tests/core/workflow/nodes/agent_v2/test_output_adapter.py:50:16
 ERROR Object of class `FromClause` has no attribute `create` [missing-attribute]
   --> tests/unit_tests/core/workflow/nodes/agent_v2/test_output_file_rebacker.py:22:5
 ERROR Cannot index into `object` [bad-index]

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 54.08% 54.08% 0.00%
Strict coverage 53.56% 53.56% 0.00%
Typed symbols 34,505 34,505 0
Untyped symbols 29,585 29,585 0
Modules 3029 3029 0

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.68%. Comparing base (f93a5b9) to head (bd13c98).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #39201      +/-   ##
==========================================
- Coverage   85.73%   85.68%   -0.06%     
==========================================
  Files        5122     4960     -162     
  Lines      281146   276518    -4628     
  Branches    55277    54055    -1222     
==========================================
- Hits       241036   236923    -4113     
+ Misses      35497    34983     -514     
+ Partials     4613     4612       -1     
Flag Coverage Δ
api 86.00% <ø> (+<0.01%) ⬆️
cli ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zyssyz123
zyssyz123 enabled auto-merge July 20, 2026 02:14
@zyssyz123
zyssyz123 added this pull request to the merge queue Jul 20, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 20, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Jul 20, 2026
@zyssyz123
zyssyz123 added this pull request to the merge queue Jul 20, 2026
@lyzno1
lyzno1 removed this pull request from the merge queue due to a manual request Jul 20, 2026
@lyzno1
lyzno1 added this pull request to the merge queue Jul 20, 2026
@lyzno1
lyzno1 removed this pull request from the merge queue due to a manual request Jul 20, 2026
@lyzno1
lyzno1 enabled auto-merge July 20, 2026 07:49
@lyzno1
lyzno1 added this pull request to the merge queue Jul 20, 2026
Merged via the queue into main with commit 1101656 Jul 20, 2026
47 checks passed
@lyzno1
lyzno1 deleted the fix/dify-2815-agent-usage-price-main branch July 20, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants