Files
crewAI/lib/crewai/tests/telemetry
João Moura cba6c03646 feat(events): record how a crew run ended, for every user (#7118)
* feat(events): record how a crew run ended, for every user

Crew was the one level with no ungated terminal record. `Crew Execution` and
`end_crew` are both behind `share_crew`, which defaults False, so for
essentially every run there is no end-of-crew span at all - not one with fields
missing. Task outcomes ship ungated, flow outcomes ship ungated; crew being the
exception looks like an accident of history rather than a decision.

Adds `Crew Completed` carrying `outcome` and an explicit `duration_ms`, keyed by
crew_key/crew_id so it joins the existing ungated `Crew Created`. Modelled
directly on `flow_completed_span`, including its reasoning: a separate span
rather than holding `Crew Execution` open, because that span is emitted and
closed at start, so holding it would drop every run that is killed or crashes.

`on_crew_failed` called no telemetry at all before this, so a failed crew
produced nothing. It deliberately does not call `end_crew`, which writes onto
the gated execution span a failed run may never have opened.

Deliberately NOT included, each for a reason:

- Tokens. `crew.token_usage` sums per-agent LLM counters, and two agents sharing
  one LLM object share one counter, so the total double-counts today. Putting it
  on a span would propagate a known-wrong number into a metric. The dedup keys on
  `id(llm._token_usage)`, not `id(llm)` - `Agent.copy()` shallow-copies the LLM -
  and it changes the value of public `Crew.calculate_usage_metrics`, so it earns
  its own change.
- Models. Already on the ungated `Crew Created` span at 99.86% coverage; this
  joins to them by crew_id rather than duplicating.
- Tool counts. The ungated `Tool Usage` span covers only the ReAct path, the
  plan/step path double-emits, and nested crews share one RuntimeState - the
  count needs a design decision on cache hits before it is worth emitting.
- error_type. Needs 4.1's exception-class field factored out of task_events so
  both events share it, rather than duplicated hours after that merged.

Tests use the exporter pattern this suite already uses rather than mocking
`EventListener._telemetry`: EventListener is a singleton, so swapping that leaks
a MagicMock into every later test. The listener tests assert on the stamp
lifecycle instead. Verified order-independent over five randomized runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(telemetry): docstring the crew-completed helpers

Eight of the thirteen functions in this file carried a docstring and five
did not, which is an inconsistency inside the file this PR adds rather
than anything inherited. Documents the two fixtures, the span lookup, the
event-bus runner and the two test methods that were missing one.

No behaviour change: 9 passed, and re-run under random ordering on two
seeds to confirm order-independence.

Deliberately not addressed: the reviewer's 52.17% docstring-coverage
figure is dominated by event_listener.py, where 84 functions - nearly
every pre-existing on_* handler - carry no docstring. That is the file's
convention, and documenting them here would be an unrelated refactor.
The public API this PR adds, Telemetry.crew_completed_span, is documented.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 13:06:51 -03:00
..
2025-10-20 14:10:19 -07:00