Skip to content

Commit b71b99f

Browse files
Add missing methods and attributes (#15)
* add missing methods and attributes * organize tests * remove unused attribute * address ci copilot comments * fix imports --------- Co-authored-by: Nikhil Chitlur Navakiran (from Dev Box) <nikhilc@microsoft.com>
1 parent 2f1f244 commit b71b99f

File tree

41 files changed

+555
-72
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+555
-72
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,19 @@ jobs:
120120

121121
- name: Run unit tests
122122
run: |
123-
uv run --frozen python -m pytest tests/ -v --tb=short -m "not integration"
123+
uv run --frozen pytest tests/ -v --tb=short -m "not integration"
124124
125125
- name: Run integration tests
126126
# Only run integration tests if secrets are available
127127
if: ${{ vars.RUN_INTEGRATION_TESTS == 'true' }}
128128
run: |
129-
uv run --frozen python -m pytest tests/integration/ -v --tb=short -m integration
129+
uv run --frozen pytest -m integration -v --tb=short
130130
env:
131131
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
132132
AZURE_OPENAI_ENDPOINT: ${{ vars.AZURE_OPENAI_ENDPOINT }}
133133
AZURE_OPENAI_DEPLOYMENT: ${{ vars.AZURE_OPENAI_DEPLOYMENT }}
134134
AZURE_OPENAI_API_VERSION: ${{ vars.AZURE_OPENAI_API_VERSION }}
135+
ENABLE_OBSERVABILITY: true
135136

136137
# Copy package and samples to drop folder
137138
- name: Copy package and samples to drop folder
Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,42 @@
1-
# Copyright (c) Microsoft. All rights reserved.
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
23

3-
# Agent details class.
44
from dataclasses import dataclass
5+
from typing import Optional
6+
7+
from .models.agent_type import AgentType
58

69

710
@dataclass
811
class AgentDetails:
9-
"""Details about an AI agent."""
12+
"""Details about an AI agent in the system."""
1013

1114
agent_id: str
12-
conversation_id: str | None = None
13-
agent_name: str | None = None
14-
agent_description: str | None = None
15-
icon_uri: str | None = None
15+
"""The unique identifier for the AI agent."""
16+
17+
agent_name: Optional[str] = None
18+
"""The human-readable name of the AI agent."""
19+
20+
agent_description: Optional[str] = None
21+
"""A description of the AI agent's purpose or capabilities."""
22+
23+
agent_auid: Optional[str] = None
24+
"""Optional Agent User ID for the agent."""
25+
26+
agent_upn: Optional[str] = None
27+
"""Optional User Principal Name (UPN) for the agent."""
28+
29+
agent_blueprint_id: Optional[str] = None
30+
"""Optional Blueprint/Application ID for the agent."""
31+
32+
agent_type: Optional[AgentType] = None
33+
"""The agent type."""
34+
35+
tenant_id: Optional[str] = None
36+
"""Optional Tenant ID for the agent."""
37+
38+
conversation_id: Optional[str] = None
39+
"""Optional conversation ID for compatibility."""
40+
41+
icon_uri: Optional[str] = None
42+
"""Optional icon URI for the agent."""

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
GEN_AI_RESPONSE_MODEL_KEY = "gen_ai.response.model"
3535
GEN_AI_SYSTEM_KEY = "gen_ai.system"
3636
GEN_AI_SYSTEM_VALUE = "az.ai.agent365"
37+
GEN_AI_THOUGHT_PROCESS_KEY = "gen_ai.agent.thought.process"
3738

3839
GEN_AI_AGENT_ID_KEY = "gen_ai.agent.id"
3940
GEN_AI_AGENT_NAME_KEY = "gen_ai.agent.name"
@@ -45,6 +46,7 @@
4546
GEN_AI_USAGE_OUTPUT_TOKENS_KEY = "gen_ai.usage.output_tokens"
4647
GEN_AI_CHOICE = "gen_ai.choice"
4748
GEN_AI_PROVIDER_NAME_KEY = "gen_ai.provider.name"
49+
GEN_AI_AGENT_TYPE_KEY = "gen_ai.agent.type"
4850

4951
GEN_AI_SYSTEM_INSTRUCTIONS_KEY = "gen_ai.system_instructions"
5052
GEN_AI_INPUT_MESSAGES_KEY = "gen_ai.input.messages"
@@ -74,6 +76,7 @@
7476
GEN_AI_CALLER_AGENT_NAME_KEY = "gen_ai.caller.agent.name"
7577
GEN_AI_CALLER_AGENT_ID_KEY = "gen_ai.caller.agent.id"
7678
GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = "gen_ai.caller.agent.applicationid"
79+
GEN_AI_CALLER_AGENT_TYPE_KEY = "gen_ai.caller.agent.type"
7780

7881
# Agent-specific dimensions
7982
AGENT_ID_KEY = "gen_ai.agent.id"

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/execute_tool_scope.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
# Copyright (c) Microsoft. All rights reserved.
2-
3-
# Execute tool scope for tracing tool execution.
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
43

54
from .agent_details import AgentDetails
65
from .constants import (
76
EXECUTE_TOOL_OPERATION_NAME,
7+
GEN_AI_EVENT_CONTENT,
8+
GEN_AI_TOOL_ARGS_KEY,
89
GEN_AI_TOOL_CALL_ID_KEY,
910
GEN_AI_TOOL_DESCRIPTION_KEY,
1011
GEN_AI_TOOL_NAME_KEY,
@@ -26,7 +27,7 @@ def start(
2627
agent_details: AgentDetails,
2728
tenant_details: TenantDetails,
2829
) -> "ExecuteToolScope":
29-
"""Create and start a new scope for tool execution tracing.
30+
"""Creates and starts a new scope for tool execution tracing.
3031
3132
Args:
3233
details: The details of the tool call
@@ -54,18 +55,34 @@ def __init__(
5455
super().__init__(
5556
kind="Internal",
5657
operation_name=EXECUTE_TOOL_OPERATION_NAME,
57-
activity_name=f"execute_tool {details.tool_name}",
58+
activity_name=f"{EXECUTE_TOOL_OPERATION_NAME} {details.tool_name}",
5859
agent_details=agent_details,
5960
tenant_details=tenant_details,
6061
)
6162

62-
self.set_tag_maybe(GEN_AI_TOOL_NAME_KEY, details.tool_name)
63-
self.set_tag_maybe("gen_ai.tool.arguments", details.arguments)
64-
self.set_tag_maybe(GEN_AI_TOOL_TYPE_KEY, details.tool_type)
65-
self.set_tag_maybe(GEN_AI_TOOL_CALL_ID_KEY, details.tool_call_id)
66-
self.set_tag_maybe(GEN_AI_TOOL_DESCRIPTION_KEY, details.description)
63+
# Extract details using deconstruction-like approach
64+
tool_name = details.tool_name
65+
arguments = details.arguments
66+
tool_call_id = details.tool_call_id
67+
description = details.description
68+
tool_type = details.tool_type
69+
endpoint = details.endpoint
70+
71+
self.set_tag_maybe(GEN_AI_TOOL_NAME_KEY, tool_name)
72+
self.set_tag_maybe(GEN_AI_TOOL_ARGS_KEY, arguments)
73+
self.set_tag_maybe(GEN_AI_TOOL_TYPE_KEY, tool_type)
74+
self.set_tag_maybe(GEN_AI_TOOL_CALL_ID_KEY, tool_call_id)
75+
self.set_tag_maybe(GEN_AI_TOOL_DESCRIPTION_KEY, description)
76+
77+
if endpoint:
78+
self.set_tag_maybe(SERVER_ADDRESS_KEY, endpoint.hostname)
79+
if endpoint.port and endpoint.port != 443:
80+
self.set_tag_maybe(SERVER_PORT_KEY, endpoint.port)
6781

68-
if details.endpoint:
69-
self.set_tag_maybe(SERVER_ADDRESS_KEY, details.endpoint.hostname)
70-
if details.endpoint.port and details.endpoint.port != 443:
71-
self.set_tag_maybe(SERVER_PORT_KEY, details.endpoint.port)
82+
def record_response(self, response: str) -> None:
83+
"""Records response information for telemetry tracking.
84+
85+
Args:
86+
response: The response to record
87+
"""
88+
self.set_tag_maybe(GEN_AI_EVENT_CONTENT, response)
Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1-
# Copyright (c) Microsoft. All rights reserved.
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
from typing import List
25

36
from .agent_details import AgentDetails
47
from .constants import (
58
GEN_AI_INPUT_MESSAGES_KEY,
9+
GEN_AI_OPERATION_NAME_KEY,
10+
GEN_AI_OUTPUT_MESSAGES_KEY,
611
GEN_AI_PROVIDER_NAME_KEY,
712
GEN_AI_REQUEST_MODEL_KEY,
813
GEN_AI_RESPONSE_FINISH_REASONS_KEY,
914
GEN_AI_RESPONSE_ID_KEY,
15+
GEN_AI_THOUGHT_PROCESS_KEY,
1016
GEN_AI_USAGE_INPUT_TOKENS_KEY,
1117
GEN_AI_USAGE_OUTPUT_TOKENS_KEY,
1218
)
1319
from .inference_call_details import InferenceCallDetails
1420
from .opentelemetry_scope import OpenTelemetryScope
1521
from .request import Request
1622
from .tenant_details import TenantDetails
23+
from .utils import safe_json_dumps
1724

1825

1926
class InferenceScope(OpenTelemetryScope):
20-
"""Provides OpenTelemetry tracing scope for inference call."""
27+
"""Provides OpenTelemetry tracing scope for generative AI inference operations."""
2128

2229
@staticmethod
2330
def start(
@@ -26,7 +33,7 @@ def start(
2633
tenant_details: TenantDetails,
2734
request: Request | None = None,
2835
) -> "InferenceScope":
29-
"""Create and start a new scope for inference call.
36+
"""Creates and starts a new scope for inference tracing.
3037
3138
Args:
3239
details: The details of the inference call
@@ -41,40 +48,93 @@ def start(
4148

4249
def __init__(
4350
self,
44-
inference_call_details: InferenceCallDetails,
51+
details: InferenceCallDetails,
4552
agent_details: AgentDetails,
4653
tenant_details: TenantDetails,
4754
request: Request | None = None,
4855
):
49-
"""Initialize the agent invocation scope.
56+
"""Initialize the inference scope.
5057
5158
Args:
52-
inference_call_details: The details of the inference call
59+
details: The details of the inference call
5360
agent_details: The details of the agent making the call
5461
tenant_details: The details of the tenant
5562
request: Optional request details for additional context
5663
"""
5764

5865
super().__init__(
5966
kind="Client",
60-
operation_name=inference_call_details.operationName.value,
61-
activity_name=f"{inference_call_details.operationName.value} {inference_call_details.model}",
67+
operation_name=details.operationName.value,
68+
activity_name=f"{details.operationName.value} {details.model}",
6269
agent_details=agent_details,
6370
tenant_details=tenant_details,
6471
)
6572

66-
# Set request content if provided
6773
if request:
6874
self.set_tag_maybe(GEN_AI_INPUT_MESSAGES_KEY, request.content)
6975

70-
self.set_tag_maybe(GEN_AI_REQUEST_MODEL_KEY, inference_call_details.model)
71-
self.set_tag_maybe(GEN_AI_PROVIDER_NAME_KEY, inference_call_details.providerName)
72-
self.set_tag_maybe(GEN_AI_USAGE_INPUT_TOKENS_KEY, inference_call_details.inputTokens)
73-
self.set_tag_maybe(GEN_AI_USAGE_OUTPUT_TOKENS_KEY, inference_call_details.outputTokens)
76+
self.set_tag_maybe(GEN_AI_OPERATION_NAME_KEY, details.operationName.value)
77+
self.set_tag_maybe(GEN_AI_REQUEST_MODEL_KEY, details.model)
78+
self.set_tag_maybe(GEN_AI_PROVIDER_NAME_KEY, details.providerName)
79+
self.set_tag_maybe(
80+
GEN_AI_USAGE_INPUT_TOKENS_KEY,
81+
str(details.inputTokens) if details.inputTokens is not None else None,
82+
)
83+
self.set_tag_maybe(
84+
GEN_AI_USAGE_OUTPUT_TOKENS_KEY,
85+
str(details.outputTokens) if details.outputTokens is not None else None,
86+
)
7487
self.set_tag_maybe(
7588
GEN_AI_RESPONSE_FINISH_REASONS_KEY,
76-
",".join(inference_call_details.finishReasons)
77-
if inference_call_details.finishReasons
78-
else None,
89+
safe_json_dumps(details.finishReasons) if details.finishReasons else None,
7990
)
80-
self.set_tag_maybe(GEN_AI_RESPONSE_ID_KEY, inference_call_details.responseId)
91+
self.set_tag_maybe(GEN_AI_RESPONSE_ID_KEY, details.responseId)
92+
93+
def record_input_messages(self, messages: List[str]) -> None:
94+
"""Records the input messages for telemetry tracking.
95+
96+
Args:
97+
messages: List of input messages
98+
"""
99+
self.set_tag_maybe(GEN_AI_INPUT_MESSAGES_KEY, safe_json_dumps(messages))
100+
101+
def record_output_messages(self, messages: List[str]) -> None:
102+
"""Records the output messages for telemetry tracking.
103+
104+
Args:
105+
messages: List of output messages
106+
"""
107+
self.set_tag_maybe(GEN_AI_OUTPUT_MESSAGES_KEY, safe_json_dumps(messages))
108+
109+
def record_input_tokens(self, input_tokens: int) -> None:
110+
"""Records the number of input tokens for telemetry tracking.
111+
112+
Args:
113+
input_tokens: Number of input tokens
114+
"""
115+
self.set_tag_maybe(GEN_AI_USAGE_INPUT_TOKENS_KEY, str(input_tokens))
116+
117+
def record_output_tokens(self, output_tokens: int) -> None:
118+
"""Records the number of output tokens for telemetry tracking.
119+
120+
Args:
121+
output_tokens: Number of output tokens
122+
"""
123+
self.set_tag_maybe(GEN_AI_USAGE_OUTPUT_TOKENS_KEY, str(output_tokens))
124+
125+
def record_finish_reasons(self, finish_reasons: List[str]) -> None:
126+
"""Records the finish reasons for telemetry tracking.
127+
128+
Args:
129+
finish_reasons: List of finish reasons
130+
"""
131+
if finish_reasons:
132+
self.set_tag_maybe(GEN_AI_RESPONSE_FINISH_REASONS_KEY, safe_json_dumps(finish_reasons))
133+
134+
def record_thought_process(self, thought_process: str) -> None:
135+
"""Records the thought process.
136+
137+
Args:
138+
thought_process: The thought process to record
139+
"""
140+
self.set_tag_maybe(GEN_AI_THOUGHT_PROCESS_KEY, thought_process)

0 commit comments

Comments
 (0)