-
Notifications
You must be signed in to change notification settings - Fork 16
Add explicit custom baggage propagation #264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Radhika Gupta (rads-1996)
merged 19 commits into
microsoft:main
from
nikhilNava:copilot/custom-baggage-propagation
Sep 22, 2026
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
37f784f
feat: add explicit custom baggage APIs
nikhilc-microsoft d60ca42
feat: propagate opted-in custom baggage
nikhilc-microsoft 2717beb
docs: describe custom baggage propagation
nikhilc-microsoft ed652fa
fix: keep custom baggage gate local
nikhilc-microsoft db935b9
Restore unsupported inference export test
nikhilc-microsoft 2061562
fix: align custom baggage span classification
nikhilc-microsoft bad5190
fix: align custom baggage span classifier
nikhilc-microsoft 4b99656
fix: keep custom baggage on unmodeled GenAI operations
nikhilc-microsoft d0b1e55
Merge origin/main into copilot/custom-baggage-propagation
nikhilc-microsoft dd4602c
Fix custom baggage scope metadata
nikhilc-microsoft 7ab71ec
Refactor GenAI span classification
nikhilc-microsoft 0b3c5b0
Address custom baggage review comments
nikhilc-microsoft 0cb4d44
Test all GenAI instrumentation scope roots
nikhilc-microsoft 3998c70
Reduce GenAI scope comments
nikhilc-microsoft ffecbf8
Simplify custom baggage changelog entry
nikhilc-microsoft 4981fe9
Merge branch 'main' into copilot/custom-baggage-propagation
JacksonWeber 026cfa2
Document GenAI operation precedence fix
nikhilc-microsoft b3e4e68
Fix GenAI operation classification precedence
nikhilc-microsoft 09610ab
Remove ignored planning documents
nikhilc-microsoft File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,4 +60,4 @@ async def main(): | |
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| asyncio.run(main()) | ||
| asyncio.run(main()) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/microsoft/opentelemetry/a365/core/exporters/_gen_ai_span_classifier.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| """Classify spans for Agent365 GenAI baggage propagation.""" | ||
|
|
||
| from collections.abc import Mapping | ||
| from dataclasses import dataclass | ||
| from typing import Any | ||
|
|
||
| from microsoft.opentelemetry.a365.core.constants import ( | ||
| GEN_AI_INITIAL_SPAN_NAMES, | ||
| GEN_AI_INSTRUMENTATION_SCOPE_ROOTS, | ||
| GEN_AI_OPERATION_NAME_KEY, | ||
| GEN_AI_PROCESSOR_OPERATION_NAMES, | ||
| ) | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class _GenAISpanClassification: | ||
| """Whether a span is GenAI and, when identifiable, the operation it represents.""" | ||
|
|
||
| is_gen_ai_span: bool | ||
| operation_name: str | None = None | ||
|
|
||
|
|
||
| def _recognized_operation_name(value: object | None) -> str | None: | ||
| return value if isinstance(value, str) and value in GEN_AI_PROCESSOR_OPERATION_NAMES else None | ||
|
|
||
|
|
||
| def _span_name(span: Any) -> str | None: | ||
| span_name = getattr(span, "name", None) | ||
| return span_name if isinstance(span_name, str) else None | ||
|
|
||
|
|
||
| def _operation_name_from_span_name(span: Any) -> str | None: | ||
| span_name = _span_name(span) | ||
| if span_name is None: | ||
| return None | ||
|
|
||
| for operation_name in GEN_AI_PROCESSOR_OPERATION_NAMES: | ||
| if span_name == operation_name or span_name.startswith(f"{operation_name} "): | ||
| return operation_name | ||
| return None | ||
|
|
||
|
|
||
| def _has_known_initial_span_name(span: Any) -> bool: | ||
| """Match span names supported instrumentations use before renaming the span.""" | ||
| span_name = _span_name(span) | ||
| if span_name is None: | ||
| return False | ||
|
|
||
| return any(span_name == known or span_name.startswith(f"{known} ") for known in GEN_AI_INITIAL_SPAN_NAMES) | ||
|
|
||
|
|
||
| def _instrumentation_scope_name(span: Any) -> str | None: | ||
| """Read the tracer (source) name recorded on a ReadWriteSpan.""" | ||
| # pylint: disable=broad-exception-caught | ||
| for attribute_name in ("instrumentation_scope", "instrumentation_info"): | ||
| try: | ||
| scope = getattr(span, attribute_name, None) | ||
| scope_name = getattr(scope, "name", None) if scope is not None else None | ||
| except Exception: | ||
| continue | ||
| if isinstance(scope_name, str) and scope_name: | ||
| return scope_name | ||
| return None | ||
|
|
||
|
|
||
| def _is_supported_gen_ai_scope(span: Any) -> bool: | ||
| scope_name = _instrumentation_scope_name(span) | ||
| if scope_name is None: | ||
| return False | ||
|
|
||
| return any(scope_name == root or scope_name.startswith(f"{root}.") for root in GEN_AI_INSTRUMENTATION_SCOPE_ROOTS) | ||
|
|
||
|
|
||
| def _classify_gen_ai_span( | ||
| span: Any, | ||
| existing_attributes: Mapping[str, object], | ||
| baggage_map: Mapping[str, object], | ||
| ) -> _GenAISpanClassification: | ||
| """Resolve whether a span is GenAI and which operation it represents. | ||
|
|
||
| An explicit ``gen_ai.operation.name`` attribute is authoritative over the | ||
| baggage and span-name inference signals, including when it holds a value | ||
| this processor does not model (``chain``, ``embeddings``, | ||
| ``text_completion``, ``generate_content``, ``create_agent``). Such a span is | ||
| still GenAI when a supported instrumentation emitted it, but its operation | ||
| stays unknown so invoke_agent-only attributes are withheld. | ||
|
|
||
| Without an explicit operation attribute, a recognized span-name operation | ||
| takes precedence over inherited operation baggage. | ||
| """ | ||
| if GEN_AI_OPERATION_NAME_KEY in existing_attributes: | ||
| explicit_operation_name = _recognized_operation_name(existing_attributes.get(GEN_AI_OPERATION_NAME_KEY)) | ||
| if explicit_operation_name is not None: | ||
| return _GenAISpanClassification(True, explicit_operation_name) | ||
| return _GenAISpanClassification(_is_supported_gen_ai_scope(span)) | ||
|
|
||
| operation_name = _operation_name_from_span_name(span) | ||
| if operation_name is None: | ||
| operation_name = _recognized_operation_name(baggage_map.get(GEN_AI_OPERATION_NAME_KEY)) | ||
| if operation_name is not None: | ||
| return _GenAISpanClassification(True, operation_name) | ||
|
|
||
| return _GenAISpanClassification(_has_known_initial_span_name(span) or _is_supported_gen_ai_scope(span)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.