Commit 7b0ad1f
authored
feat(observability): route eval scores through ScoreEvent bus (#16185)
## Description
Wires Mastra eval scores through the unified observability bus so every
exporter implementing `onScoreEvent` automatically receives them.
Replaces the deprecated per-exporter `addScoreToTrace` side-channel that
bypassed the pipeline. Also unblocks Mastra spans appearing under
Braintrust experiment eval spans (#11097) as a side effect of the
unified path.
- Producer alignment: `packages/core/src/mastra/hooks.ts` no longer
iterates exporters with `addScoreToTrace`. `MastraScorer.run()` (already
on main from PR #14920) remains the single producer of `ScoreEvent`s
through `mastra.observability.addScore()`.
- Consumer side: implemented `onScoreEvent` on Langfuse
(`LangfuseClient.score.create`), Laminar (`/v1/evaluators/score`),
Braintrust (`logger.logFeedback`), Datadog
(`tracer.llmobs.submitEvaluation`), and LangSmith
(`Client.createFeedback`).
- Backwards compatibility: Langfuse `addScoreToTrace` and Laminar
`_addScoreToTrace` are preserved as `@deprecated` wrappers that forward
to a shared private `submitScore` helper.
- Bug fix in `observability/mastra/src/recorded.ts`: `buildScoreEvent`
now forwards `scorerName` and `targetEntityType` from `ScoreInput` to
`ExportedScore` (without this, exporters silently fell back to
`scorerId`).
- Tests: rewrote core hook test to assert the hook no longer publishes a
`ScoreEvent`; added per-exporter `onScoreEvent` tests; added a
regression test for `buildScoreEvent` field forwarding.
- Seven single-package changesets created (`@mastra/core`,
`@mastra/observability`, `@mastra/langfuse`, `@mastra/laminar`,
`@mastra/braintrust`, `@mastra/datadog`, `@mastra/langsmith`) per the
project's changeset style guide.
## Related Issue(s)
Fixes #10896 (also unblocks #11097)
## Type of Change
- [ ] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update
## Checklist
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [x] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
When Mastra assigns an evaluation score, this PR sends that score
exactly once into a single observability "bus" so all
monitoring/exporter integrations (Langfuse, Laminar, Braintrust,
Datadog, LangSmith, etc.) automatically receive it — avoiding
exporter-specific wiring and duplicate deliveries.
## Overview
Routes Mastra evaluation scores through the unified observability
ScoreEvent bus (mastra.observability.addScore) so any exporter
implementing onScoreEvent receives them. MastraScorer.run() remains the
single producer of ScoreEvent; the core scorer hook no longer iterates
exporters to call addScoreToTrace. Per-exporter
addScoreToTrace/_addScoreToTrace behaviors are preserved as @deprecated
wrappers that forward to shared submit helpers where applicable. Fixes a
bug in buildScoreEvent so scorerName and targetEntityType from
ScoreInput are forwarded into emitted ExportedScore.
## Changes by Component
### Core (@mastra/core)
- Removed exporter-iteration and addScoreToTrace republishing from
createOnScorerHook; the hook now only persists scores to the legacy
scores store.
- Ensures MastraScorer.run() is the single producer of ScoreEvent.
- Registers static workflow-step scorers synchronously during
addWorkflow and resolves dynamic scorers asynchronously so scorers are
registered before use.
- Updated core test to assert the hook does not emit ScoreEvent.
### Observability (@mastra/observability)
- Fix: buildScoreEvent (observability/mastra/src/recorded.ts) now
includes scorerName and targetEntityType in emitted ScoreEvent.
- Bus routing: route-event now prefers onScoreEvent; if an exporter
lacks onScoreEvent but implements deprecated addScoreToTrace, the bus
maps the ScoreEvent into the legacy addScoreToTrace payload and calls it
(avoids duplicate delivery when both exist).
- Added unit tests for score routing behavior and the buildScoreEvent
regression.
### Exporter Integrations (new onScoreEvent handlers + tests)
- Langfuse (@mastra/langfuse)
- Added onScoreEvent → LangfuseClient.score.create with a shared
submitScore helper and centralized error logging.
- Preserved addScoreToTrace as a deprecated wrapper forwarding to
submitScore.
- Tests updated to cover onScoreEvent and mark addScoreToTrace
deprecated.
- Laminar (@mastra/laminar)
- Added onScoreEvent delegating to a private submitScore that POSTS to
/v1/evaluators/score.
- submitScore converts trace/span IDs to Laminar/OTel UUIDs, merges
metadata/reason, enforces timeouts via AbortController, and logs
non-OK/timed-out responses.
- Preserved _addScoreToTrace as a @deprecated wrapper forwarding to
submitScore.
- Tests verify POST payload mapping and wrapper behavior.
- Braintrust (@mastra/braintrust)
- Added onScoreEvent that calls logger.logFeedback using spanId (falling
back to traceId) and skips when neither is present.
- Maps scorer name/ID, reason, and metadata into the feedback payload
and logs failures.
- Tests cover span/trace fallback and skip behavior.
- Datadog (@mastra/datadog)
- Added onScoreEvent: validates traceId/spanId, looks up exported
dd-trace span context (traceState), and calls
tracer.llmobs.submitEvaluation with mapped fields
(label/value/metricType/mlApp/timestamp/reason/metadata).
- Drops scores when identifiers or exported span context are missing and
warns when scores arrive before the span is exported.
- Tests cover successful submission, unknown IDs (no-op), and dropping
out-of-order scores.
- LangSmith (@mastra/langsmith)
- Added onScoreEvent that submits Client.createFeedback when a
spanId→LangSmith runId mapping exists.
- Introduced a configurable bounded LRU spanId→runId cache
(runIdCacheMaxEntries) populated from RunTree building; evicts oldest
entries when full and is cleared on shutdown.
- Ensures authoritative scorerId/scoreSource are preserved in sourceInfo
and ignores events when span/run mapping is absent.
- Tests cover mapping semantics, eviction, metadata protection, and
skipping unseen spans.
## Tests
- Core hook test rewritten to assert the hook does not publish
ScoreEvent.
- Per-exporter onScoreEvent test suites added for Langfuse, Laminar,
Braintrust, Datadog, and LangSmith covering field mapping, identifier
fallbacks, ordering/dropping, timeout handling, and cache eviction.
- Bus tests added to assert onScoreEvent preferred and legacy
addScoreToTrace fallback behavior.
- Regression test added to ensure buildScoreEvent forwards scorerName
and targetEntityType.
## Backwards Compatibility
- Deprecated addScoreToTrace/_addScoreToTrace preserved (where present)
as wrappers that forward to the new onScoreEvent/submit helpers to avoid
breaking existing callers.
- Observability interface docs updated to deprecate addScoreToTrace in
favor of onScoreEvent/ScoreEvent bus.
- No breaking public API surface changes beyond documented additions and
deprecations.
## Commits & Release Notes
- Seven package-scoped changesets created for @mastra/core,
@mastra/observability, @mastra/langfuse, @mastra/laminar,
@mastra/langsmith, @mastra/datadog, and @mastra/braintrust documenting
onScoreEvent support and the buildScoreEvent fix.
- Exporter-specific notes documented (Datadog ordering caveat; Laminar
timeout/error logging; LangSmith runId cache behavior).
## Related Issues
Fixes #10896 and unblocks #11097.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->1 parent 6068a6c commit 7b0ad1f
26 files changed
Lines changed: 829 additions & 247 deletions
File tree
- .changeset
- docs/src/content/en/reference/observability/tracing
- observability
- braintrust/src
- datadog/src
- laminar/src
- langfuse/src
- langsmith/src
- mastra/src
- bus
- packages/core/src
- mastra
- storage/domains/observability
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
Lines changed: 6 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
159 | | - | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
160 | 165 | | |
161 | 166 | | |
162 | 167 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2182 | 2182 | | |
2183 | 2183 | | |
2184 | 2184 | | |
| 2185 | + | |
| 2186 | + | |
| 2187 | + | |
| 2188 | + | |
| 2189 | + | |
| 2190 | + | |
| 2191 | + | |
| 2192 | + | |
| 2193 | + | |
| 2194 | + | |
| 2195 | + | |
| 2196 | + | |
| 2197 | + | |
| 2198 | + | |
| 2199 | + | |
| 2200 | + | |
| 2201 | + | |
| 2202 | + | |
| 2203 | + | |
| 2204 | + | |
| 2205 | + | |
| 2206 | + | |
| 2207 | + | |
| 2208 | + | |
| 2209 | + | |
| 2210 | + | |
| 2211 | + | |
| 2212 | + | |
| 2213 | + | |
| 2214 | + | |
| 2215 | + | |
| 2216 | + | |
| 2217 | + | |
| 2218 | + | |
| 2219 | + | |
| 2220 | + | |
| 2221 | + | |
| 2222 | + | |
| 2223 | + | |
| 2224 | + | |
| 2225 | + | |
| 2226 | + | |
| 2227 | + | |
| 2228 | + | |
| 2229 | + | |
| 2230 | + | |
| 2231 | + | |
| 2232 | + | |
| 2233 | + | |
| 2234 | + | |
| 2235 | + | |
| 2236 | + | |
| 2237 | + | |
| 2238 | + | |
| 2239 | + | |
2185 | 2240 | | |
2186 | 2241 | | |
2187 | 2242 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| |||
134 | 134 | | |
135 | 135 | | |
136 | 136 | | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
137 | 176 | | |
138 | 177 | | |
139 | 178 | | |
| |||
0 commit comments