fix: #3586473 Finalize streaming OTel spans on the terminal event
Summary
Fixes #3586473 (closed). Streaming OTel spans dropped final token usage: the span was
ended on the first event (un-consumed iterator), before the real usage arrived
on the terminal PostStreamingResponseEvent.
Changes
- Defer span finalization to the terminal event for streaming, and unset the
stored span after ending it (also fixes unbounded
$otelSpansgrowth). - Add a shutdown guard (
DestructableInterface) so spans left open by iterators that never dispatch the terminal event are ended at request shutdown. - Tests: the bug repro and the leak guard.
Out of scope, tracked on the issue: metrics operation_type dimension,
operationType/configuration on the terminal event, finish-reason preservation.
Verification
Against drupal/ai 1.4.0 (PHP 8.4, PHPUnit 11.5, real OTel in-memory exporter):
ai_observability Unit suite 17/17, phpstan and phpcs clean.
AI-Generated: Yes (Claude Code; verified against drupal/ai 1.4.0 with a real OpenTelemetry SDK.)
Edited by George Kastanis