mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-09-20 18:13:49 +00:00
* 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>