Skip to content

Commit d076d5c

Browse files
authored
Merge branch 'main' into mohessie/unify_output/tool_call_accuracy
2 parents d3c4092 + a529fa5 commit d076d5c

106 files changed

Lines changed: 4415 additions & 1524 deletions

File tree

Some content is hidden

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

sdk/agentserver/azure-ai-agentserver-core/CHANGELOG.md

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,55 @@
11
# Release History
22

3-
## 2.0.0b1 (Unreleased)
3+
## 2.0.0b1 (2026-04-14)
44

5-
### Features Added
6-
7-
- `AgentServerHost` base class (Starlette subclass) with health probe (`/readiness`), graceful shutdown, and port binding.
8-
- Automatic OpenTelemetry tracing with Azure Monitor and OTLP exporters (included as primary dependencies).
9-
- `request_span()` host method and `end_span()` / `record_error()` / `trace_stream()` public functions for protocol SDK tracing.
10-
- Overridable tracing setup via `configure_tracing` constructor parameter.
11-
- `create_error_response()` utility for standard error envelope responses.
12-
- Cooperative mixin inheritance for multi-protocol composition.
13-
- Hypercorn-based ASGI server with HTTP/1.1 support.
5+
This is a major architectural rewrite. The package has been redesigned as a lightweight hosting
6+
foundation. Protocol implementations that were previously bundled in this package have moved to
7+
dedicated protocol packages (`azure-ai-agentserver-responses`, `azure-ai-agentserver-invocations`).
8+
See the [Migration Guide](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/agentserver/azure-ai-agentserver-core/MigrationGuide.md)
9+
for upgrading from 1.x versions.
1410

1511
### Breaking Changes
1612

17-
- Renamed `AgentHost``AgentServerHost`; now inherits from `Starlette` directly.
18-
- Removed `register_routes()` — protocol packages now subclass `AgentServerHost` and pass routes via `super().__init__()`.
19-
- Removed lazy `app` property — `AgentServerHost` IS the ASGI app.
20-
- Replaced `TracingHelper` class with module-level functions (`request_span`, `end_span`, `record_error`, `trace_stream`, `configure_tracing`).
21-
- Replaced `ErrorResponse.create()` static method with module-level `create_error_response()` function.
22-
- Removed `AgentLogger` / `get_logger()` — use `logging.getLogger("azure.ai.agentserver")` directly.
23-
- Removed `AGENT_LOG_LEVEL` and `AGENT_GRACEFUL_SHUTDOWN_TIMEOUT` environment variable support from `Constants`.
24-
- Removed `leaf_customer_span_id` baggage mechanism and W3C Baggage propagation.
25-
- OpenTelemetry is now a required dependency (was optional `[tracing]` extras).
26-
- Renamed health endpoint from `/healthy` to `/readiness`.
13+
- **Package split**: All Responses API protocol types (models, handler decorators, SSE streaming)
14+
have moved to `azure-ai-agentserver-responses`. All Invocations protocol types have moved to
15+
`azure-ai-agentserver-invocations`. This package now contains only the shared hosting foundation.
16+
- **`FoundryCBAgent` removed**: Replaced by `AgentServerHost`, a Starlette subclass that IS the
17+
ASGI app (no separate `.app` property or `register_routes()`).
18+
- **`AgentRunContext` removed**: Protocol packages provide their own context types
19+
(`ResponseContext` in Responses, `request.state` in Invocations).
20+
- **`TracingHelper` class removed**: Replaced by module-level functions (`request_span`,
21+
`end_span`, `record_error`, `trace_stream`) for a simpler functional API.
22+
- **`AgentLogger` / `get_logger()` removed**: Use `logging.getLogger("azure.ai.agentserver")`
23+
directly, or rely on the SDK's automatic console logging setup.
24+
- **`ErrorResponse.create()` removed**: Replaced by `create_error_response()` module-level function.
25+
- **Health endpoint renamed**: `/healthy``/readiness`.
26+
- **OpenTelemetry is now a required dependency** (was optional `[tracing]` extras in 1.x).
27+
- **Environment variables changed**: `AGENT_LOG_LEVEL` and `AGENT_GRACEFUL_SHUTDOWN_TIMEOUT` are
28+
no longer read from `Constants`. Use the `log_level` and `graceful_shutdown_timeout` constructor
29+
parameters instead.
30+
31+
### Features Added
32+
33+
- `AgentServerHost` base class with built-in health probe (`/readiness`), graceful shutdown
34+
(configurable timeout), and Hypercorn-based ASGI serving.
35+
- Cooperative mixin inheritance for multi-protocol composition — a single server can host both
36+
Responses and Invocations endpoints.
37+
- Automatic OpenTelemetry tracing with Azure Monitor and OTLP exporters.
38+
- `configure_observability` constructor parameter for overridable logging + tracing setup.
39+
Console `StreamHandler` is attached to the root logger by default so user `logging.info()`
40+
calls are visible without any extra configuration.
41+
- `request_span()` context manager for creating request-scoped OTel spans with GenAI semantic
42+
convention attributes.
43+
- `end_span()`, `record_error()`, `flush_spans()`, `trace_stream()` public functions for
44+
protocol SDK tracing lifecycle.
45+
- `set_current_span()` / `detach_context()` for explicit OTel context management during
46+
streaming, ensuring child spans are correctly parented.
47+
- `AgentConfig` dataclass for resolved configuration from environment variables (Foundry agent
48+
name, version, project ID, session ID, etc.).
49+
- `create_error_response()` utility for standard error envelope JSON responses.
50+
- `build_server_version()` for constructing `x-platform-server` header segments.
51+
- HTTP access logging with configurable format via `access_log` and `access_log_format`
52+
constructor parameters.
2753

2854
## 1.0.0b1 (2025-11-07)
2955

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Guide for migrating to the new azure-ai-agentserver package architecture
2+
3+
This guide helps you migrate from `azure-ai-agentserver-core` 1.x to the new three-package
4+
architecture introduced in `azure-ai-agentserver-core` 2.0.0b1.
5+
6+
## Table of contents
7+
8+
- [Migration benefits](#migration-benefits)
9+
- [Package changes](#package-changes)
10+
- [API changes](#api-changes)
11+
- [Handler registration](#handler-registration)
12+
- [Streaming handler](#streaming-handler)
13+
- [Server startup](#server-startup)
14+
- [Tracing](#tracing)
15+
- [Logging](#logging)
16+
- [Error responses](#error-responses)
17+
- [Import changes](#import-changes)
18+
- [Multi-protocol composition](#multi-protocol-composition)
19+
- [Additional information](#additional-information)
20+
21+
## Migration benefits
22+
23+
The new package architecture provides:
24+
25+
- **Separation of concerns** — protocol implementations (Responses API, Invocations) are in
26+
dedicated packages rather than bundled into a monolithic Core package.
27+
- **Dramatically simpler API** — the old approach required manually constructing SSE events,
28+
tracking sequence numbers, and building response objects. The new API provides decorator-based
29+
handler registration with builder methods that handle all of this automatically.
30+
- **Type-safe event builders**`ResponseEventStream` and its convenience generators manage
31+
event sequencing, output indices, and content indices. You cannot accidentally emit events in
32+
the wrong order.
33+
- **Built-in convenience methods** — common patterns like "emit a text message" or "stream
34+
tokens" are one-liners via `ResponseEventStream` generators or `TextResponse`.
35+
- **Zero-config startup**`app.run()` replaces manual server configuration with sensible
36+
defaults including OpenTelemetry, health endpoints, and user-agent headers.
37+
- **Multi-protocol support** — a single server can host both Responses and Invocations endpoints
38+
via cooperative mixin inheritance.
39+
40+
## Package changes
41+
42+
| Before | After | Notes |
43+
|--------|-------|-------|
44+
| `azure-ai-agentserver-core` 1.x | `azure-ai-agentserver-core` 2.x | Stripped to hosting foundation only |
45+
| _(bundled in core)_ | `azure-ai-agentserver-responses` 1.x | New — Responses API protocol |
46+
| _(bundled in core)_ | `azure-ai-agentserver-invocations` 1.x | New — Invocations protocol |
47+
48+
Update your dependencies:
49+
50+
```bash
51+
# Install the protocol package you need (transitively brings in core 2.x)
52+
pip install azure-ai-agentserver-responses
53+
54+
# If you also need the Invocations protocol:
55+
pip install azure-ai-agentserver-invocations
56+
```
57+
58+
> **Note:** Both `azure-ai-agentserver-responses` and `azure-ai-agentserver-invocations`
59+
> depend on `azure-ai-agentserver-core`, so you do not need to install Core separately.
60+
61+
## API changes
62+
63+
### Handler registration
64+
65+
**Before (1.x):**
66+
67+
```python
68+
from azure.ai.agentserver.core import FoundryCBAgent, AgentRunContext
69+
70+
class MyAgent(FoundryCBAgent):
71+
def register_routes(self):
72+
self.app.add_route("/responses", self.handle_create, methods=["POST"])
73+
74+
async def handle_create(self, request):
75+
# Manually parse request, build SSE events, track sequence numbers
76+
...
77+
```
78+
79+
**After (2.x) — Responses protocol:**
80+
81+
```python
82+
from azure.ai.agentserver.responses import (
83+
CreateResponse,
84+
ResponseContext,
85+
ResponsesAgentServerHost,
86+
TextResponse,
87+
)
88+
89+
app = ResponsesAgentServerHost()
90+
91+
@app.response_handler
92+
async def handle_create(request: CreateResponse, context: ResponseContext, cancellation_signal):
93+
input_text = await context.get_input_text()
94+
return TextResponse(context, request, text=f"Echo: {input_text}")
95+
96+
app.run()
97+
```
98+
99+
**After (2.x) — Invocations protocol:**
100+
101+
```python
102+
from starlette.requests import Request
103+
from starlette.responses import JSONResponse, Response
104+
from azure.ai.agentserver.invocations import InvocationAgentServerHost
105+
106+
app = InvocationAgentServerHost()
107+
108+
@app.invoke_handler
109+
async def handle(request: Request) -> Response:
110+
data = await request.json()
111+
return JSONResponse({"greeting": f"Hello, {data['name']}!"})
112+
113+
app.run()
114+
```
115+
116+
### Streaming handler
117+
118+
**Before (1.x):**
119+
120+
```python
121+
# Manually construct every SSE event, track sequence numbers and indices
122+
seq = 0
123+
yield {"type": "response.created", "sequence_number": seq, "response": {...}}
124+
seq += 1
125+
yield {"type": "response.output_item.added", "sequence_number": seq, ...}
126+
seq += 1
127+
# ... many more events with manual index tracking
128+
```
129+
130+
**After (2.x):**
131+
132+
```python
133+
from azure.ai.agentserver.responses import (
134+
CreateResponse,
135+
ResponseContext,
136+
ResponseEventStream,
137+
ResponsesAgentServerHost,
138+
)
139+
140+
app = ResponsesAgentServerHost()
141+
142+
@app.response_handler
143+
async def handle_create(request: CreateResponse, context: ResponseContext, cancellation_signal):
144+
stream = ResponseEventStream(context, request)
145+
stream.emit_created()
146+
147+
# All inner events (output_item.added, content_part.added, deltas, done events)
148+
# are emitted automatically with correct sequence numbers and indices
149+
async for token in get_tokens():
150+
yield from stream.output_item_message(token)
151+
152+
stream.emit_completed()
153+
```
154+
155+
Or, for the simplest case:
156+
157+
```python
158+
return TextResponse(context, request, text="Hello!")
159+
```
160+
161+
### Server startup
162+
163+
**Before (1.x):**
164+
165+
```python
166+
agent = MyAgent()
167+
agent.run() # or manual uvicorn/hypercorn setup
168+
```
169+
170+
**After (2.x):**
171+
172+
```python
173+
# Responses protocol
174+
app = ResponsesAgentServerHost()
175+
176+
@app.response_handler
177+
async def handle(request, context, cancellation_signal):
178+
...
179+
180+
app.run() # Built-in Hypercorn server with OpenTelemetry, health endpoint, graceful shutdown
181+
```
182+
183+
Configuration is via constructor parameters:
184+
185+
```python
186+
app = ResponsesAgentServerHost(
187+
log_level="DEBUG", # Console log level
188+
graceful_shutdown_timeout=60, # Drain period in seconds
189+
applicationinsights_connection_string="InstrumentationKey=...", # Azure Monitor
190+
configure_observability=None, # Disable SDK logging/tracing setup
191+
)
192+
```
193+
194+
## Import changes
195+
196+
| Before (1.x) | After (2.x) |
197+
|---------------|-------------|
198+
| `from azure.ai.agentserver.core import FoundryCBAgent` | `from azure.ai.agentserver.core import AgentServerHost` |
199+
| `from azure.ai.agentserver.core import AgentRunContext` | `from azure.ai.agentserver.responses import ResponseContext` |
200+
| _(n/a)_ | `from azure.ai.agentserver.responses import ResponsesAgentServerHost` |
201+
| _(n/a)_ | `from azure.ai.agentserver.responses import TextResponse` |
202+
| _(n/a)_ | `from azure.ai.agentserver.responses import ResponseEventStream` |
203+
| _(n/a)_ | `from azure.ai.agentserver.invocations import InvocationAgentServerHost` |
204+
205+
## Multi-protocol composition
206+
207+
A single server can host both Responses and Invocations endpoints using cooperative
208+
mixin inheritance:
209+
210+
```python
211+
from azure.ai.agentserver.invocations import InvocationAgentServerHost
212+
from azure.ai.agentserver.responses import ResponsesAgentServerHost
213+
214+
class MyHost(InvocationAgentServerHost, ResponsesAgentServerHost):
215+
pass
216+
217+
app = MyHost()
218+
219+
@app.response_handler
220+
async def handle_responses(request, context, cancellation_signal):
221+
return TextResponse(context, request, text="Hello from Responses!")
222+
223+
@app.invoke_handler
224+
async def handle_invocations(request):
225+
return JSONResponse({"hello": "from Invocations!"})
226+
227+
app.run()
228+
# Serves both POST /responses and POST /invocations
229+
```
230+
231+
## Additional information
232+
233+
- [azure-ai-agentserver-core README](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/agentserver/azure-ai-agentserver-core/README.md)
234+
- [azure-ai-agentserver-responses README](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/agentserver/azure-ai-agentserver-responses/README.md)
235+
- [azure-ai-agentserver-invocations README](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/agentserver/azure-ai-agentserver-invocations/README.md)
236+
- [Responses samples](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/agentserver/azure-ai-agentserver-responses/samples)
237+
- [Invocations samples](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/agentserver/azure-ai-agentserver-invocations/samples)

sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ async def _lifespan(_app: Starlette) -> AsyncGenerator[None, None]: # noqa: RUF
197197
self._graceful_shutdown_timeout,
198198
)
199199
except Exception: # pylint: disable=broad-exception-caught
200-
logger.exception("Error in on_shutdown")
200+
logger.warning("Error in on_shutdown", exc_info=True)
201201

202202
# Merge routes: subclass routes (if any) + health endpoint
203203
all_routes: list[Any] = list(routes or [])

sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
# ======================================================================
4242

4343

44-
class AgentConfig:
44+
class AgentConfig: # pylint: disable=too-many-instance-attributes
4545
"""Resolved configuration for an agent server host.
4646
4747
All values are populated from environment variables at creation time.

sdk/agentserver/azure-ai-agentserver-core/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ combine-as-imports = true
6868
breaking = false
6969
mypy = true
7070
pyright = true
71-
verifytypes = true
71+
verifytypes = false
72+
latestdependency = false
7273
pylint = true
7374
type_check_samples = false
7475

sdk/agentserver/azure-ai-agentserver-core/tests/test_graceful_shutdown.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ async def receive():
189189
async def send(message):
190190
sent_messages.append(message)
191191

192-
with caplog.at_level(logging.ERROR, logger="azure.ai.agentserver"):
192+
with caplog.at_level(logging.WARNING, logger="azure.ai.agentserver"):
193193
await agent(scope, receive, send)
194194

195195
# The error should be logged

sdk/agentserver/azure-ai-agentserver-githubcopilot/pyproject.toml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ dynamic = ["version", "readme"]
44
description = "GitHub Copilot SDK adapter for Azure AI Agent Server"
55
requires-python = ">=3.11"
66
authors = [
7-
{ name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" },
7+
{ name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" },
88
]
99
license = "MIT"
1010
classifiers = [
@@ -32,13 +32,7 @@ requires = ["setuptools>=69", "wheel"]
3232
build-backend = "setuptools.build_meta"
3333

3434
[tool.setuptools.packages.find]
35-
exclude = [
36-
"tests*",
37-
"samples*",
38-
"doc*",
39-
"azure",
40-
"azure.ai",
41-
]
35+
exclude = ["tests*", "samples*", "doc*", "azure", "azure.ai"]
4236

4337
[tool.setuptools.dynamic]
4438
version = { attr = "azure.ai.agentserver.githubcopilot._version.VERSION" }
@@ -63,6 +57,7 @@ analyze_python_version = "3.11"
6357
breaking = false
6458
pyright = false
6559
verifytypes = false
60+
latestdependency = false
6661
verify_keywords = false
6762
mindependency = false
6863
whl_no_aio = false

sdk/agentserver/azure-ai-agentserver-invocations/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Release History
22

3-
## 1.0.0b1 (Unreleased)
3+
## 1.0.0b1 (2026-04-14)
44

55
### Features Added
66

0 commit comments

Comments
 (0)