microsoft-agents-a365-observability-extensions-openai 0.1.0.dev30__py3-none-any.whl → 0.2.1.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- microsoft_agents_a365/observability/extensions/openai/__init__.py +2 -1
- microsoft_agents_a365/observability/extensions/openai/constants.py +2 -1
- microsoft_agents_a365/observability/extensions/openai/trace_instrumentor.py +2 -1
- microsoft_agents_a365/observability/extensions/openai/trace_processor.py +58 -4
- microsoft_agents_a365/observability/extensions/openai/utils.py +102 -3
- {microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info → microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info}/METADATA +3 -3
- microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info/RECORD +9 -0
- {microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info → microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info}/WHEEL +1 -1
- {microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info → microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info}/top_level.txt +1 -0
- microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info/RECORD +0 -9
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
# Processor for OpenAI Agents SDK
|
|
4
5
|
|
|
@@ -21,13 +22,17 @@ from agents.tracing.span_data import (
|
|
|
21
22
|
from microsoft_agents_a365.observability.core.constants import (
|
|
22
23
|
CUSTOM_PARENT_SPAN_ID_KEY,
|
|
23
24
|
EXECUTE_TOOL_OPERATION_NAME,
|
|
25
|
+
GEN_AI_EXECUTION_TYPE_KEY,
|
|
24
26
|
GEN_AI_INPUT_MESSAGES_KEY,
|
|
25
27
|
GEN_AI_OPERATION_NAME_KEY,
|
|
26
28
|
GEN_AI_OUTPUT_MESSAGES_KEY,
|
|
27
29
|
GEN_AI_REQUEST_MODEL_KEY,
|
|
28
30
|
GEN_AI_SYSTEM_KEY,
|
|
31
|
+
GEN_AI_TOOL_CALL_ID_KEY,
|
|
32
|
+
GEN_AI_TOOL_TYPE_KEY,
|
|
29
33
|
INVOKE_AGENT_OPERATION_NAME,
|
|
30
34
|
)
|
|
35
|
+
from microsoft_agents_a365.observability.core.execution_type import ExecutionType
|
|
31
36
|
from microsoft_agents_a365.observability.core.utils import as_utc_nano, safe_json_dumps
|
|
32
37
|
from opentelemetry import trace as ot_trace
|
|
33
38
|
from opentelemetry.context import attach, detach
|
|
@@ -44,10 +49,13 @@ from openai.types.responses import (
|
|
|
44
49
|
)
|
|
45
50
|
|
|
46
51
|
from .constants import (
|
|
47
|
-
GEN_AI_GRAPH_NODE_ID,
|
|
48
52
|
GEN_AI_GRAPH_NODE_PARENT_ID,
|
|
49
53
|
)
|
|
50
54
|
from .utils import (
|
|
55
|
+
capture_input_message,
|
|
56
|
+
capture_output_message,
|
|
57
|
+
capture_tool_call_ids,
|
|
58
|
+
find_ancestor_agent_span_id,
|
|
51
59
|
get_attributes_from_function_span_data,
|
|
52
60
|
get_attributes_from_generation_span_data,
|
|
53
61
|
get_attributes_from_input,
|
|
@@ -56,6 +64,7 @@ from .utils import (
|
|
|
56
64
|
get_span_kind,
|
|
57
65
|
get_span_name,
|
|
58
66
|
get_span_status,
|
|
67
|
+
get_tool_call_id,
|
|
59
68
|
)
|
|
60
69
|
|
|
61
70
|
logger = logging.getLogger(__name__)
|
|
@@ -68,6 +77,7 @@ Custom Trace Processor for OpenAI Agents SDK
|
|
|
68
77
|
|
|
69
78
|
class OpenAIAgentsTraceProcessor(TracingProcessor):
|
|
70
79
|
_MAX_HANDOFFS_IN_FLIGHT = 1000
|
|
80
|
+
_MAX_PENDING_TOOL_CALLS = 1000
|
|
71
81
|
|
|
72
82
|
def __init__(self, tracer: Tracer) -> None:
|
|
73
83
|
self._tracer = tracer
|
|
@@ -79,6 +89,17 @@ class OpenAIAgentsTraceProcessor(TracingProcessor):
|
|
|
79
89
|
# Use an OrderedDict and _MAX_HANDOFFS_IN_FLIGHT to cap the size of the dict
|
|
80
90
|
# in case there are large numbers of orphaned handoffs
|
|
81
91
|
self._reverse_handoffs_dict: OrderedDict[str, str] = OrderedDict()
|
|
92
|
+
# Track input/output messages for agent spans (keyed by agent span_id)
|
|
93
|
+
self._agent_inputs: dict[str, str] = {}
|
|
94
|
+
self._agent_outputs: dict[str, str] = {}
|
|
95
|
+
# Track agent span IDs to find nearest ancestor
|
|
96
|
+
self._agent_span_ids: set[str] = set()
|
|
97
|
+
# Track parent-child relationships: child_span_id -> parent_span_id
|
|
98
|
+
self._span_parents: dict[str, str] = {}
|
|
99
|
+
# Track tool_call_ids from GenerationSpan: (function_name, trace_id) -> call_id
|
|
100
|
+
# Use an OrderedDict and _MAX_PENDING_TOOL_CALLS to cap the size of the dict
|
|
101
|
+
# in case tool calls are captured but never consumed
|
|
102
|
+
self._pending_tool_calls: OrderedDict[str, str] = OrderedDict()
|
|
82
103
|
|
|
83
104
|
# helper
|
|
84
105
|
def _stamp_custom_parent(self, otel_span: OtelSpan, trace_id: str) -> None:
|
|
@@ -133,6 +154,12 @@ class OpenAIAgentsTraceProcessor(TracingProcessor):
|
|
|
133
154
|
)
|
|
134
155
|
self._otel_spans[span.span_id] = otel_span
|
|
135
156
|
self._tokens[span.span_id] = attach(set_span_in_context(otel_span))
|
|
157
|
+
# Track parent-child relationship for ancestor lookup
|
|
158
|
+
if span.parent_id:
|
|
159
|
+
self._span_parents[span.span_id] = span.parent_id
|
|
160
|
+
# Track AgentSpan IDs
|
|
161
|
+
if isinstance(span.span_data, AgentSpanData):
|
|
162
|
+
self._agent_span_ids.add(span.span_id)
|
|
136
163
|
|
|
137
164
|
def on_span_end(self, span: Span[Any]) -> None:
|
|
138
165
|
"""Called when a span is finished. Should not block or raise exceptions.
|
|
@@ -142,6 +169,8 @@ class OpenAIAgentsTraceProcessor(TracingProcessor):
|
|
|
142
169
|
"""
|
|
143
170
|
if token := self._tokens.pop(span.span_id, None):
|
|
144
171
|
detach(token) # type: ignore[arg-type]
|
|
172
|
+
# Clean up parent tracking
|
|
173
|
+
self._span_parents.pop(span.span_id, None)
|
|
145
174
|
if not (otel_span := self._otel_spans.pop(span.span_id, None)):
|
|
146
175
|
return
|
|
147
176
|
otel_span.update_name(get_span_name(span))
|
|
@@ -167,6 +196,19 @@ class OpenAIAgentsTraceProcessor(TracingProcessor):
|
|
|
167
196
|
for k, v in get_attributes_from_generation_span_data(data):
|
|
168
197
|
otel_span.set_attribute(k, v)
|
|
169
198
|
self._stamp_custom_parent(otel_span, span.trace_id)
|
|
199
|
+
# Capture input/output messages for nearest ancestor agent span
|
|
200
|
+
if agent_span_id := find_ancestor_agent_span_id(
|
|
201
|
+
span.parent_id, self._agent_span_ids, self._span_parents
|
|
202
|
+
):
|
|
203
|
+
if data.input:
|
|
204
|
+
capture_input_message(agent_span_id, data.input, self._agent_inputs)
|
|
205
|
+
if data.output:
|
|
206
|
+
capture_output_message(agent_span_id, data.output, self._agent_outputs)
|
|
207
|
+
# Capture tool_call_ids for later use by FunctionSpan
|
|
208
|
+
if data.output:
|
|
209
|
+
capture_tool_call_ids(
|
|
210
|
+
data.output, self._pending_tool_calls, self._MAX_PENDING_TOOL_CALLS
|
|
211
|
+
)
|
|
170
212
|
otel_span.update_name(
|
|
171
213
|
f"{otel_span.attributes[GEN_AI_OPERATION_NAME_KEY]} {otel_span.attributes[GEN_AI_REQUEST_MODEL_KEY]}"
|
|
172
214
|
)
|
|
@@ -174,7 +216,12 @@ class OpenAIAgentsTraceProcessor(TracingProcessor):
|
|
|
174
216
|
for k, v in get_attributes_from_function_span_data(data):
|
|
175
217
|
otel_span.set_attribute(k, v)
|
|
176
218
|
self._stamp_custom_parent(otel_span, span.trace_id)
|
|
177
|
-
otel_span.
|
|
219
|
+
otel_span.set_attribute(GEN_AI_TOOL_TYPE_KEY, data.type)
|
|
220
|
+
# Set tool_call_id if available from preceding GenerationSpan
|
|
221
|
+
func_args = data.input if data.input else ""
|
|
222
|
+
if tool_call_id := get_tool_call_id(data.name, func_args, self._pending_tool_calls):
|
|
223
|
+
otel_span.set_attribute(GEN_AI_TOOL_CALL_ID_KEY, tool_call_id)
|
|
224
|
+
otel_span.update_name(f"{EXECUTE_TOOL_OPERATION_NAME} {data.name}")
|
|
178
225
|
elif isinstance(data, MCPListToolsSpanData):
|
|
179
226
|
for k, v in get_attributes_from_mcp_list_tool_span_data(data):
|
|
180
227
|
otel_span.set_attribute(k, v)
|
|
@@ -187,12 +234,19 @@ class OpenAIAgentsTraceProcessor(TracingProcessor):
|
|
|
187
234
|
while len(self._reverse_handoffs_dict) > self._MAX_HANDOFFS_IN_FLIGHT:
|
|
188
235
|
self._reverse_handoffs_dict.popitem(last=False)
|
|
189
236
|
elif isinstance(data, AgentSpanData):
|
|
190
|
-
otel_span.set_attribute(
|
|
237
|
+
otel_span.set_attribute(GEN_AI_EXECUTION_TYPE_KEY, ExecutionType.HUMAN_TO_AGENT.value)
|
|
191
238
|
# Lookup the parent node if exists
|
|
192
239
|
key = f"{data.name}:{span.trace_id}"
|
|
193
240
|
if parent_node := self._reverse_handoffs_dict.pop(key, None):
|
|
194
241
|
otel_span.set_attribute(GEN_AI_GRAPH_NODE_PARENT_ID, parent_node)
|
|
242
|
+
# Apply captured input/output messages from child spans
|
|
243
|
+
if input_msg := self._agent_inputs.pop(span.span_id, None):
|
|
244
|
+
otel_span.set_attribute(GEN_AI_INPUT_MESSAGES_KEY, input_msg)
|
|
245
|
+
if output_msg := self._agent_outputs.pop(span.span_id, None):
|
|
246
|
+
otel_span.set_attribute(GEN_AI_OUTPUT_MESSAGES_KEY, output_msg)
|
|
195
247
|
otel_span.update_name(f"{INVOKE_AGENT_OPERATION_NAME} {get_span_name(span)}")
|
|
248
|
+
# Clean up tracking
|
|
249
|
+
self._agent_span_ids.discard(span.span_id)
|
|
196
250
|
|
|
197
251
|
end_time: int | None = None
|
|
198
252
|
if span.ended_at:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
# -------------------------------------------------- #
|
|
4
5
|
# HELPER FUNCTIONS ###
|
|
@@ -22,6 +23,7 @@ from agents.tracing.span_data import (
|
|
|
22
23
|
)
|
|
23
24
|
from microsoft_agents_a365.observability.core.constants import (
|
|
24
25
|
GEN_AI_CHOICE,
|
|
26
|
+
GEN_AI_EVENT_CONTENT,
|
|
25
27
|
GEN_AI_EXECUTION_PAYLOAD_KEY,
|
|
26
28
|
GEN_AI_INPUT_MESSAGES_KEY,
|
|
27
29
|
GEN_AI_OUTPUT_MESSAGES_KEY,
|
|
@@ -364,9 +366,9 @@ def get_attributes_from_function_span_data(
|
|
|
364
366
|
) -> Iterator[tuple[str, AttributeValue]]:
|
|
365
367
|
yield GEN_AI_TOOL_NAME_KEY, obj.name
|
|
366
368
|
if obj.input:
|
|
367
|
-
yield
|
|
369
|
+
yield GEN_AI_TOOL_ARGS_KEY, obj.input
|
|
368
370
|
if obj.output is not None:
|
|
369
|
-
yield
|
|
371
|
+
yield GEN_AI_EVENT_CONTENT, _convert_to_primitive(obj.output)
|
|
370
372
|
|
|
371
373
|
|
|
372
374
|
def get_attributes_from_message_content_list(
|
|
@@ -534,3 +536,100 @@ def get_span_status(obj: Span[Any]) -> Status:
|
|
|
534
536
|
)
|
|
535
537
|
else:
|
|
536
538
|
return Status(StatusCode.OK)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def capture_tool_call_ids(
|
|
542
|
+
output_list: Any, pending_tool_calls: dict[str, str], max_size: int = 1000
|
|
543
|
+
) -> None:
|
|
544
|
+
"""Extract and store tool_call_ids from generation output for later use by FunctionSpan.
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
output_list: The generation output containing tool calls
|
|
548
|
+
pending_tool_calls: OrderedDict to store pending tool calls
|
|
549
|
+
max_size: Maximum number of pending tool calls to keep in memory
|
|
550
|
+
"""
|
|
551
|
+
if not output_list:
|
|
552
|
+
return
|
|
553
|
+
try:
|
|
554
|
+
for msg in output_list:
|
|
555
|
+
if isinstance(msg, dict) and msg.get("role") == "assistant":
|
|
556
|
+
tool_calls = msg.get("tool_calls")
|
|
557
|
+
if tool_calls:
|
|
558
|
+
for tc in tool_calls:
|
|
559
|
+
if isinstance(tc, dict):
|
|
560
|
+
call_id = tc.get("id")
|
|
561
|
+
func = tc.get("function", {})
|
|
562
|
+
func_name = func.get("name") if isinstance(func, dict) else None
|
|
563
|
+
func_args = func.get("arguments", "") if isinstance(func, dict) else ""
|
|
564
|
+
if call_id and func_name:
|
|
565
|
+
# Key by (function_name, arguments) to uniquely identify each call
|
|
566
|
+
key = f"{func_name}:{func_args}"
|
|
567
|
+
pending_tool_calls[key] = call_id
|
|
568
|
+
# Cap the size of the dict to prevent unbounded growth
|
|
569
|
+
while len(pending_tool_calls) > max_size:
|
|
570
|
+
pending_tool_calls.popitem(last=False)
|
|
571
|
+
except Exception:
|
|
572
|
+
pass
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def get_tool_call_id(
|
|
576
|
+
function_name: str, function_args: str, pending_tool_calls: dict[str, str]
|
|
577
|
+
) -> str | None:
|
|
578
|
+
"""Get and remove the tool_call_id for a function with specific arguments."""
|
|
579
|
+
key = f"{function_name}:{function_args}"
|
|
580
|
+
return pending_tool_calls.pop(key, None)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def capture_input_message(
|
|
584
|
+
parent_span_id: str, input_list: Any, agent_inputs: dict[str, str]
|
|
585
|
+
) -> None:
|
|
586
|
+
"""Extract and store the first user message from input list for parent agent span."""
|
|
587
|
+
if parent_span_id in agent_inputs:
|
|
588
|
+
return # Already captured
|
|
589
|
+
if not input_list:
|
|
590
|
+
return
|
|
591
|
+
try:
|
|
592
|
+
for msg in input_list:
|
|
593
|
+
if isinstance(msg, dict) and msg.get("role") == "user":
|
|
594
|
+
content = msg.get("content", "")
|
|
595
|
+
if content:
|
|
596
|
+
agent_inputs[parent_span_id] = str(content)
|
|
597
|
+
return
|
|
598
|
+
except Exception:
|
|
599
|
+
pass
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def capture_output_message(
|
|
603
|
+
parent_span_id: str, output_list: Any, agent_outputs: dict[str, str]
|
|
604
|
+
) -> None:
|
|
605
|
+
"""Extract and store the last assistant message with actual content (no tool calls) for parent agent span."""
|
|
606
|
+
if not output_list:
|
|
607
|
+
return
|
|
608
|
+
try:
|
|
609
|
+
# Iterate in reverse to get the last assistant message with content (not a tool call)
|
|
610
|
+
output_items = list(output_list) if not isinstance(output_list, list) else output_list
|
|
611
|
+
for msg in reversed(output_items):
|
|
612
|
+
if isinstance(msg, dict) and msg.get("role") == "assistant":
|
|
613
|
+
content = msg.get("content")
|
|
614
|
+
tool_calls = msg.get("tool_calls")
|
|
615
|
+
# Only capture if there's actual content and no tool_calls
|
|
616
|
+
# (tool_calls means this is an intermediate step, not the final response)
|
|
617
|
+
if content and not tool_calls:
|
|
618
|
+
agent_outputs[parent_span_id] = str(content)
|
|
619
|
+
return
|
|
620
|
+
except Exception:
|
|
621
|
+
pass
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def find_ancestor_agent_span_id(
|
|
625
|
+
span_id: str | None, agent_span_ids: set[str], span_parents: dict[str, str]
|
|
626
|
+
) -> str | None:
|
|
627
|
+
"""Walk up the parent chain to find the nearest ancestor AgentSpan."""
|
|
628
|
+
current = span_id
|
|
629
|
+
visited: set[str] = set() # Prevent infinite loops
|
|
630
|
+
while current and current not in visited:
|
|
631
|
+
if current in agent_span_ids:
|
|
632
|
+
return current
|
|
633
|
+
visited.add(current)
|
|
634
|
+
current = span_parents.get(current)
|
|
635
|
+
return None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-a365-observability-extensions-openai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.2.1.dev0
|
|
4
4
|
Summary: OpenAI Agents SDK observability and tracing extensions for Microsoft Agent 365
|
|
5
5
|
Author-email: Microsoft <support@microsoft.com>
|
|
6
6
|
License: MIT
|
|
@@ -57,7 +57,7 @@ For usage examples and detailed documentation, see the [Observability documentat
|
|
|
57
57
|
For issues, questions, or feedback:
|
|
58
58
|
|
|
59
59
|
- File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
|
|
60
|
-
- See the [main documentation](
|
|
60
|
+
- See the [main documentation](../../README.md) for more information
|
|
61
61
|
|
|
62
62
|
## Trademarks
|
|
63
63
|
|
|
@@ -67,4 +67,4 @@ For issues, questions, or feedback:
|
|
|
67
67
|
|
|
68
68
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
69
69
|
|
|
70
|
-
Licensed under the MIT License - see the [LICENSE](
|
|
70
|
+
Licensed under the MIT License - see the [LICENSE](../../LICENSE.md) file for details.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
microsoft_agents_a365/observability/extensions/openai/__init__.py,sha256=4rtjiX0m6pVpuhxPEcWTdEVV1gHBJ7_9YIQNnAB3zYw,287
|
|
2
|
+
microsoft_agents_a365/observability/extensions/openai/constants.py,sha256=ftbtXTXXlZP13NAEm6rq_juIv_UHXIAL19Y13aNw2yw,1536
|
|
3
|
+
microsoft_agents_a365/observability/extensions/openai/trace_instrumentor.py,sha256=hFa9CtP0VAphLA2kVnl96pc5Y0VKWy49CuuImlNj80U,2475
|
|
4
|
+
microsoft_agents_a365/observability/extensions/openai/trace_processor.py,sha256=mZn6HHxy3wEULz_1iQAJ2qP-MnLM_FoTUVBF1Ok8d68,10957
|
|
5
|
+
microsoft_agents_a365/observability/extensions/openai/utils.py,sha256=AawDN8dBBT0uZ4PjPB4IhRaarGSOAGYBv6A6jnbihiw,23684
|
|
6
|
+
microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info/METADATA,sha256=aQ0jQ2U2Tv-9Zqz8ktjl7iMXE6pbEWF36Zj-jtQmgfM,3626
|
|
7
|
+
microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
+
microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info/top_level.txt,sha256=m90AvzRnjbL6fpi20mzOj6HUVkR2LWuf2JuXm4LL9LU,27
|
|
9
|
+
microsoft_agents_a365_observability_extensions_openai-0.2.1.dev0.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
microsoft_agents_a365/observability/extensions/openai/__init__.py,sha256=C525c5EXggrjZp2rbQGtak2S5u4I3TC9rYZWxh1MRR8,262
|
|
2
|
-
microsoft_agents_a365/observability/extensions/openai/constants.py,sha256=eAptrhnCpRx0PnCEr_HmmdaHt9CYDLbPwzSDtAy3NSM,1511
|
|
3
|
-
microsoft_agents_a365/observability/extensions/openai/trace_instrumentor.py,sha256=qvZY7vwDfD4n0sRu06VMGhX2UUqxyiXyQj_yEfSGIeQ,2450
|
|
4
|
-
microsoft_agents_a365/observability/extensions/openai/trace_processor.py,sha256=aOP6uEz0ECX4C4wu1GUW-TkHVMKzMWMc00nziOhIsVA,7973
|
|
5
|
-
microsoft_agents_a365/observability/extensions/openai/utils.py,sha256=xi7akBeJHnj5F6ayJCUUZS0SRwWJYjOKeMv0p0zwBeo,19607
|
|
6
|
-
microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info/METADATA,sha256=AzhO5qIvSsn2y7cV6G4v_ONH7FQiFhiMbctz4o2H28U,3633
|
|
7
|
-
microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
|
|
9
|
-
microsoft_agents_a365_observability_extensions_openai-0.1.0.dev30.dist-info/RECORD,,
|