feat: #3586479 Add gen_ai.* semantic conventions to ai_observability
Summary
ai_observability now emits the OpenTelemetry GenAI semantic-convention attributes (gen_ai.*) next to the existing ad-hoc attributes and counters. The old names are kept so current dashboards keep working. This covers the non-streaming path. Streaming finish reasons depend on #3586473 (closed) and come in a follow-up.
Spans: gen_ai.provider.name, gen_ai.operation.name (chat, text_completion, embeddings only), gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens (explicit 0 kept), gen_ai.response.model, and gen_ai.response.finish_reasons when the provider returns them.
Metrics: the gen_ai.client.token.usage histogram for input and output, with gen_ai.token.type, gen_ai.provider.name and gen_ai.request.model dimensions.
GenAiAttributeMapper maps provider and operation ids to the well-known values and falls back to the raw id. README and the docs page describe the schema and mark the ad-hoc names as legacy (removal targeted 2.0.x).
Closes #3586479 (closed)
Testing
Unit: vendor/bin/phpunit modules/ai_observability/tests/src/Unit
Verified against a live provider: a real chat through OpenRouter (gpt-4o-mini) produced a span with gen_ai.provider.name=openai, gen_ai.operation.name=chat, gen_ai.request.model, gen_ai.usage.input/output_tokens, gen_ai.response.model and gen_ai.response.finish_reasons=["stop"], next to the legacy attributes, plus the gen_ai.client.token.usage histogram. Confirmed in the exported OTLP payload.
AI Compliance
AI-Generated: Yes (Used Claude Code to write the mapper, the span and metric emission, the unit tests and the docs. Reviewed and verified by the author, including a live end-to-end run, before submission.)