Skip to content

Commit 569e1b0

Browse files
authored
Merge branch 'main' into users/akjind/addUserMessageToHeader
2 parents b1b3113 + 402f822 commit 569e1b0

File tree

107 files changed

+5292
-1615
lines changed

Some content is hidden

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

107 files changed

+5292
-1615
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,13 @@ jobs:
107107
run: uv lock && uv sync --locked --all-extras --dev
108108

109109
- name: Verify centralized version constraints
110-
run: python scripts/verify_constraints.py
110+
run: uv run --frozen tox -e verify-constraints
111111

112112
- name: Check linting
113-
run: |
114-
uv run --frozen ruff check . --preview
113+
run: uv run --frozen tox -e lint
115114

116115
- name: Check formatting
117-
run: |
118-
uv run --frozen ruff format --check .
116+
run: uv run --frozen tox -e format
119117

120118
- name: Build package
121119
run: |
@@ -124,14 +122,12 @@ jobs:
124122
AGENT365_PYTHON_SDK_PACKAGE_VERSION: ${{ needs.version-number.outputs.PACKAGE_VERSION }}
125123

126124
- name: Run unit tests
127-
run: |
128-
uv run --frozen pytest tests/ -v --tb=short -m "not integration"
125+
run: uv run --frozen tox -e py3${{ matrix.python-version == '3.11' && '11' || '12' }}
129126

130127
- name: Run integration tests
131128
# Only run integration tests if secrets are available
132129
if: ${{ vars.RUN_INTEGRATION_TESTS == 'true' }}
133-
run: |
134-
uv run --frozen pytest -m integration -v --tb=short
130+
run: uv run --frozen tox -e integration
135131
env:
136132
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
137133
AZURE_OPENAI_ENDPOINT: ${{ vars.AZURE_OPENAI_ENDPOINT }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ dist/
7272
build/
7373
.eggs/
7474
.pytest_cache/
75+
.tox/
7576
_version.py
7677

7778
# Test coverage and reports

CLAUDE.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ pytest tests/ --cov=libraries --cov-report=html -v
6161
- `unit`: Fast, mocked tests (default)
6262
- `integration`: Slow tests requiring real services/API keys
6363

64+
### Running with tox
65+
66+
```bash
67+
# Run all default environments (lint, format, unit tests on 3.11 + 3.12)
68+
uv run tox
69+
70+
# Run a specific environment
71+
uv run tox -e lint
72+
uv run tox -e format
73+
uv run tox -e py311
74+
uv run tox -e py312
75+
76+
# Run integration tests (requires env vars)
77+
uv run tox -e integration
78+
79+
# Verify centralized dependency constraints
80+
uv run tox -e verify-constraints
81+
82+
# Pass extra args to pytest
83+
uv run tox -e py311 -- -k "environment"
84+
85+
# List all available environments
86+
uv run tox list
87+
```
88+
6489
### Linting and Formatting
6590

6691
```bash
@@ -241,7 +266,8 @@ Place it before imports with one blank line after.
241266
The `.github/workflows/ci.yml` pipeline:
242267
- Runs on pushes to `main` and `release/*` branches
243268
- Tests both Python 3.11 and 3.12
244-
- Executes: lint check → format check → build → unit tests → integration tests (if secrets available)
269+
- Uses **tox** (via `uv run --frozen tox -e <env>`) to run lint, format, test, and constraint verification steps
270+
- Executes: verify-constraints → lint → format → build → unit tests → integration tests (if secrets available)
245271
- Only publishes packages on `release/*` branches when SDK changes detected
246272
- Uses git-based versioning (tags on release branches = official versions, others = dev versions)
247273

docs/design.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,23 @@ The foundation for distributed tracing in agent applications. Built on OpenTelem
8181

8282
| Class | Purpose |
8383
|-------|---------|
84-
| `InvokeAgentDetails` | Agent endpoint, session ID, and invocation metadata |
84+
| `InvokeAgentScopeDetails` | Agent endpoint and invocation metadata |
8585
| `AgentDetails` | Agent identification and metadata |
86-
| `TenantDetails` | Tenant identification for multi-tenant scenarios |
86+
| `UserDetails` | Human caller identification (user ID, email, name, IP) |
87+
| `CallerDetails` | Wrapper for user details and/or caller agent details |
88+
| `SpanDetails` | Parent context, timing, and span kind for custom spans |
8789
| `InferenceCallDetails` | Model name, tokens, provider information |
8890
| `ToolCallDetails` | Tool name, arguments, endpoint |
89-
| `Request` | Execution context and correlation ID |
91+
| `Request` | Content, correlation ID, and conversation ID |
9092

9193
**Usage Example:**
9294

9395
```python
9496
from microsoft_agents_a365.observability.core import (
9597
configure,
9698
InvokeAgentScope,
97-
InvokeAgentDetails,
98-
TenantDetails,
99+
InvokeAgentScopeDetails,
100+
AgentDetails,
99101
Request,
100102
BaggageBuilder,
101103
)
@@ -112,9 +114,9 @@ configure(
112114
with BaggageBuilder().tenant_id(tenant_id).agent_id(agent_id).build():
113115
# Trace agent invocation
114116
with InvokeAgentScope.start(
115-
invoke_agent_details=InvokeAgentDetails(...),
116-
tenant_details=TenantDetails(...),
117-
request=Request(...)
117+
request=Request(content="Hello"),
118+
invoke_scope_details=InvokeAgentScopeDetails(...),
119+
agent_details=AgentDetails(...),
118120
) as scope:
119121
# Agent logic here
120122
scope.record_response("result")

libraries/microsoft-agents-a365-notifications/pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[build-system]
2-
requires = ["setuptools>=68", "wheel", "tzdata"]
3-
build-backend = "setuptools.build_meta"
2+
requires = ["setuptools>=68", "wheel", "tzdata", "tomlkit", "packaging"]
3+
build-backend = "build_backend"
4+
backend-path = ["../../versioning/helper"]
45

56
[project]
67
name = "microsoft-agents-a365-notifications"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Changelog — microsoft-agents-a365-observability-core
2+
3+
All notable changes to this package will be documented in this file.
4+
5+
## [Unreleased]
6+
7+
### Breaking Changes
8+
9+
- **`InvokeAgentDetails` renamed to `InvokeAgentScopeDetails`** — Now contains only scope-level config (`endpoint`). Agent identity (`AgentDetails`) is a separate parameter. `session_id` moved to `Request`.
10+
- **`InvokeAgentScope.start()`**: New signature `start(request, invoke_scope_details, agent_details, caller_details?, span_details?)`. `request` is required.
11+
- **`InferenceScope.start()`**: New signature `start(request, details, agent_details, user_details?, span_details?)`. `request` is required.
12+
- **`ExecuteToolScope.start()`**: New signature `start(request, details, agent_details, user_details?, span_details?)`. Same pattern as `InferenceScope`.
13+
- **`OutputScope.start()`**: New signature `start(request, response, agent_details, user_details?, span_details?)`. Same pattern.
14+
- **`CallerDetails` renamed to `UserDetails`** — Fields renamed: `caller_id``user_id`, `caller_upn``user_email`, `caller_name``user_name`, `caller_client_ip``user_client_ip`.
15+
- **`CallerDetails` is now a composite wrapper** — Groups `user_details: UserDetails` and `caller_agent_details: AgentDetails` for A2A scenarios.
16+
- **`TenantDetails` removed**`tenant_id` is now on `AgentDetails.tenant_id`. Removed from all scope `start()` methods.
17+
- **`ExecutionType` enum removed** — Removed from `Request`. `GEN_AI_EXECUTION_TYPE_KEY` constant also removed.
18+
- **`AgentDetails` fields renamed**`agent_auid``agentic_user_id`, `agent_upn``agentic_user_email`. `conversation_id` moved to `Request`.
19+
- **`Request` model updated** — Removed `execution_type`. Added `conversation_id`. `content` is now optional.
20+
- **`BaggageBuilder` methods renamed**`agent_upn()``agentic_user_email()`, `agent_auid()``agentic_user_id()`, `caller_id()``user_id()`, `caller_name()``user_name()`, `caller_upn()``user_email()`, `caller_client_ip()``user_client_ip()`.
21+
22+
### Added
23+
24+
- **`SpanDetails`** — Groups `span_kind`, `parent_context`, `start_time`, `end_time` for scope construction.
25+
- **`UserDetails`** — Human caller identity with `user_id`, `user_email`, `user_name`, `user_client_ip`.
26+
- **`CallerDetails`** (new wrapper) — Groups `user_details` and `caller_agent_details` for A2A scenarios.
27+
- **`InvokeAgentScopeDetails`** — Scope-level config with `endpoint` only.
28+
- **`Request.conversation_id`** — Conversation ID field on the unified `Request` model.
29+
- **`ERROR_TYPE_CANCELLED`** constant — `"TaskCanceledException"`, used by `record_cancellation()`.
30+
- **`OutputScope`** now exported from `microsoft_agents_a365.observability.core`.

libraries/microsoft-agents-a365-observability-core/docs/design.md

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ configure(
6565
class OpenTelemetryScope:
6666
"""Base class for OpenTelemetry tracing scopes."""
6767

68-
def __init__(self, kind, operation_name, activity_name, agent_details, tenant_details):
68+
def __init__(self, kind, operation_name, activity_name, agent_details):
6969
# Creates span with given parameters
7070
# Sets common attributes (gen_ai.system, operation name)
71-
# Sets agent/tenant details as span attributes
71+
# Sets agent details as span attributes
7272

7373
def __enter__(self):
7474
# Makes span active in current context
@@ -96,29 +96,23 @@ Traces agent invocation operations (entry point for agent requests):
9696
```python
9797
from microsoft_agents_a365.observability.core import (
9898
InvokeAgentScope,
99-
InvokeAgentDetails,
100-
TenantDetails,
99+
InvokeAgentScopeDetails,
101100
AgentDetails,
101+
Request,
102102
)
103103

104104
with InvokeAgentScope.start(
105-
invoke_agent_details=InvokeAgentDetails(
106-
endpoint=parsed_url,
107-
session_id="session-123",
108-
details=AgentDetails(agent_id="agent-456", agent_name="MyAgent")
109-
),
110-
tenant_details=TenantDetails(tenant_id="tenant-789"),
111-
request=Request(content="Hello", execution_type=ExecutionType.CHAT),
105+
request=Request(content="Hello"),
106+
invoke_scope_details=InvokeAgentScopeDetails(endpoint=parsed_url),
107+
agent_details=AgentDetails(agent_id="agent-456", agent_name="MyAgent"),
112108
) as scope:
113109
# Agent processing
114110
scope.record_response("Agent response")
115111
```
116112

117113
**Span attributes recorded:**
118114
- Server address and port
119-
- Session ID
120115
- Execution source metadata
121-
- Execution type
122116
- Input/output messages
123117
- Caller details (if provided)
124118

@@ -127,15 +121,15 @@ with InvokeAgentScope.start(
127121
Traces LLM/AI model inference calls:
128122

129123
```python
130-
from microsoft_agents_a365.observability.core import InferenceScope, InferenceCallDetails
124+
from microsoft_agents_a365.observability.core import InferenceScope, InferenceCallDetails, Request
131125

132126
with InferenceScope.start(
133-
inference_call_details=InferenceCallDetails(
127+
request=Request(content="Hello"),
128+
details=InferenceCallDetails(
134129
model_name="gpt-4",
135130
provider="openai"
136131
),
137132
agent_details=agent_details,
138-
tenant_details=tenant_details,
139133
) as scope:
140134
# LLM call
141135
scope.record_input_tokens(100)
@@ -148,15 +142,15 @@ with InferenceScope.start(
148142
Traces tool execution operations:
149143

150144
```python
151-
from microsoft_agents_a365.observability.core import ExecuteToolScope, ToolCallDetails
145+
from microsoft_agents_a365.observability.core import ExecuteToolScope, ToolCallDetails, Request
152146

153147
with ExecuteToolScope.start(
154-
tool_call_details=ToolCallDetails(
148+
request=Request(content="search for weather"),
149+
details=ToolCallDetails(
155150
tool_name="search",
156151
tool_arguments={"query": "weather"}
157152
),
158153
agent_details=agent_details,
159-
tenant_details=tenant_details,
160154
) as scope:
161155
# Tool execution
162156
scope.record_response("Tool result")
@@ -174,7 +168,7 @@ with BaggageBuilder() \
174168
.tenant_id("tenant-123") \
175169
.agent_id("agent-456") \
176170
.correlation_id("corr-789") \
177-
.caller_id("user-abc") \
171+
.user_id("user-abc") \
178172
.session_id("session-xyz") \
179173
.build():
180174
# All child spans inherit this baggage
@@ -195,9 +189,11 @@ with BaggageBuilder.set_request_context(
195189
| `tenant_id(value)` | `tenant_id` |
196190
| `agent_id(value)` | `gen_ai.agent.id` |
197191
| `agent_auid(value)` | `gen_ai.agent.auid` |
198-
| `agent_upn(value)` | `gen_ai.agent.upn` |
192+
| `agent_email(value)` | `gen_ai.agent.upn` |
199193
| `correlation_id(value)` | `correlation_id` |
200-
| `caller_id(value)` | `gen_ai.caller.id` |
194+
| `user_id(value)` | `gen_ai.caller.id` |
195+
| `user_name(value)` | `gen_ai.caller.name` |
196+
| `user_email(value)` | `gen_ai.caller.upn` |
201197
| `session_id(value)` | `session_id` |
202198
| `conversation_id(value)` | `gen_ai.conversation.id` |
203199
| `channel_name(value)` | `gen_ai.execution.source.name` |
@@ -246,14 +242,12 @@ options = Agent365ExporterOptions(
246242

247243
## Data Classes
248244

249-
### InvokeAgentDetails
245+
### InvokeAgentScopeDetails
250246

251247
```python
252248
@dataclass
253-
class InvokeAgentDetails:
249+
class InvokeAgentScopeDetails:
254250
endpoint: ParseResult | None # Parsed URL of the agent endpoint
255-
session_id: str | None # Session identifier
256-
details: AgentDetails # Agent metadata
257251
```
258252

259253
### AgentDetails
@@ -265,20 +259,42 @@ class AgentDetails:
265259
agent_name: str | None
266260
agent_description: str | None
267261
agent_auid: str | None # Agent unique identifier
268-
agent_upn: str | None # User principal name
262+
agent_email: str | None # Agent email address
269263
agent_blueprint_id: str | None
270264
agent_type: AgentType | None
271265
tenant_id: str | None
272-
conversation_id: str | None
273266
icon_uri: str | None
274267
```
275268

276-
### TenantDetails
269+
### UserDetails
277270

278271
```python
279272
@dataclass
280-
class TenantDetails:
281-
tenant_id: str | None
273+
class UserDetails:
274+
user_id: str | None
275+
user_email: str | None
276+
user_name: str | None
277+
caller_client_ip: str | None
278+
```
279+
280+
### CallerDetails
281+
282+
```python
283+
@dataclass
284+
class CallerDetails:
285+
user_details: UserDetails | None
286+
caller_agent_details: AgentDetails | None
287+
```
288+
289+
### SpanDetails
290+
291+
```python
292+
@dataclass
293+
class SpanDetails:
294+
parent_context: Context | None
295+
start_time: int | None
296+
end_time: int | None
297+
span_kind: SpanKind | None
282298
```
283299

284300
### InferenceCallDetails
@@ -358,14 +374,15 @@ microsoft_agents_a365/observability/core/
358374
├── invoke_agent_scope.py # Agent invocation tracing
359375
├── inference_scope.py # LLM inference tracing
360376
├── execute_tool_scope.py # Tool execution tracing
377+
├── output_scope.py # Output tracing
361378
├── agent_details.py # AgentDetails dataclass
362-
├── tenant_details.py # TenantDetails dataclass
363-
├── invoke_agent_details.py # InvokeAgentDetails dataclass
379+
├── invoke_agent_scope_details.py # InvokeAgentScopeDetails dataclass
380+
├── user_details.py # UserDetails dataclass
381+
├── span_details.py # SpanDetails dataclass
364382
├── inference_call_details.py # InferenceCallDetails dataclass
365383
├── tool_call_details.py # ToolCallDetails dataclass
366384
├── request.py # Request dataclass
367385
├── source_metadata.py # SourceMetadata dataclass
368-
├── execution_type.py # ExecutionType enum
369386
├── inference_operation_type.py # InferenceOperationType enum
370387
├── tool_type.py # ToolType enum
371388
├── constants.py # Attribute key constants

0 commit comments

Comments
 (0)