ADR-024: Dashboard Widgets
- Status
-
Accepted
- Date
-
2026-04
- Authors
-
Netresearch DTT GmbH
Context
tx_nrllm_service_usage has tracked per-request cost and usage from day
one, but the data was only reachable through the backend module's report
views. Administrators wanted an at-a-glance view next to everything else
they already follow — scheduled tasks, indexing, form submissions — which
lives on TYPO3's dashboard.
Decision
Ship two widgets that reuse TYPO3's built-in widget classes and wire them up with nr-llm-specific data providers:
- AI cost this month —
Numberbacked byWith Icon Widget Monthly, which delegates toCost Data Provider Usage. Returns dollars floored to an integer; the dashboard tile is a glance-value, not an accounting figure.Tracker Service:: get Current Month Cost () - AI requests by provider (7d) —
Barbacked byChart Widget Requests, which aggregates every service type (chat, vision, translation, speech, image) byBy Provider Data Provider service_providerover the last seven days.
Both are registered in a dedicated Configuration/
imported conditionally from Configuration/ when
TYPO3\ exists. Without that
guard, TYPO3 instances that do not have typo3/ installed
would fail at container compile time on the unresolved widget class.
Classes/Widgets/* is excluded from the global auto-registration in
Services. for the same reason — the data provider classes
import dashboard interfaces and must not be loaded when dashboard is
absent.
Trade-offs
- + Reuse core widget classes. Two core TYPO3 widget types cover the useful shapes. Writing a custom widget buys nothing.
- + Optional dependency.
typo3/is acms- dashboard suggest, not a hardrequire. Installs without dashboard lose the widgets but pay no runtime cost and see no container errors. - - Two data-shape spots. The row-shaping logic on
Requestsis static for unit-testability, but the SQL lives in an instance method bound toBy Provider Data Provider:: shape Chart Data () Connection. The trade-off keeps unit tests honest and functional coverage narrow.Pool - - Flooring the cost. Displaying
$12.as97 12is jarring for cost-sensitive users but the widget API returnsint. Follow-up: a custom template could render the subtitle with fractional digits once we have one.
Alternatives considered
- Custom widget classes implementing
Widgetdirectly. Rejected — duplicates what the core widgets already do.Interface - Per-day time series instead of per-provider aggregate. Interesting but the current 7-day window is short enough that the distribution is the more useful glance value.
- One combined widget with cost + count + top provider in a single
tile. Rejected — mixes two summary numbers into one, and forcing both
to share the
Numbershape cripples both.With Icon Widget