Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions sdk/agentserver/azure-ai-agentserver/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Release History

## 1.0.0b1 (Unreleased)

### Features Added

- Initial release of `azure-ai-agentserver`.
- Generic `AgentServer` base class with pluggable protocol heads.
- `/invoke` protocol head with all 4 operations: create, get, cancel, and OpenAPI spec.
- OpenAPI spec-based request/response validation via `jsonschema`.
- Health check endpoints (`/liveness`, `/readiness`).
- Streaming and non-streaming invocation support.
21 changes: 21 additions & 0 deletions sdk/agentserver/azure-ai-agentserver/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Copyright (c) Microsoft Corporation.

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
7 changes: 7 additions & 0 deletions sdk/agentserver/azure-ai-agentserver/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include *.md
include LICENSE
recursive-include tests *.py
recursive-include samples *.py *.md
include azure/__init__.py
include azure/ai/__init__.py
include azure/ai/agentserver/py.typed
197 changes: 197 additions & 0 deletions sdk/agentserver/azure-ai-agentserver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# azure-ai-agentserver

A standalone, **protocol-agnostic agent server** package for Azure AI. Provides a
Starlette-based `AgentServer` base class with pluggable protocol heads, OpenAPI-based
request/response validation, tracing, logging, and health endpoints — with **zero
framework coupling**.

## Overview

`azure-ai-agentserver` is the canonical agent-server package going forward. It supports
multiple protocol heads (`/invoke`, `/responses`, and future protocols) through a pluggable
handler architecture. Phase 1 ships with `/invoke` support; `/responses` and other
protocols will be added in subsequent phases.

**Key properties:**

- **Standalone** — no dependency on `azure-ai-agentserver-core`, `openai`, or any AI
framework library.
- **Starlette + uvicorn** — lightweight ASGI server, same technology used by other Azure
AI packages.
- **Abstract base class** — subclass `AgentServer` and implement handler methods for the
protocol heads you need.
- **Customer-managed adapters** — integration with LangGraph, Agent Framework, Semantic
Kernel, etc. is done in your own code. We provide samples, not separate adapter packages.

## Architecture

```
┌─────────────────────────────────────────────────────────────────────┐
│ Layer 1: Agent Service (Cloud Infrastructure) │
│ Supports: /invoke, /responses, /mcp, /a2a, /activity │
└────────────────────────────┬────────────────────────────────────────┘
┌───────────────────────────────┐
│ azure-ai-agentserver │
│ AgentServer │
│ │
│ Protocol heads: │
│ • /invoke (Phase 1) │
│ • /responses (Phase 2) │
│ │
│ OpenAPI spec validation (all) │
│ Tracing, logging, health │
└───────────────────────────────┘
Customer owns adapters:
┌─────────┼─────────┐
▼ ▼ ▼
LangGraph Agent Semantic
adapter Framework Kernel
(sample) adapter adapter
(sample) (sample)
```

**Single package, multiple protocol heads, no framework coupling.**

## Installation

```bash
pip install azure-ai-agentserver
```

**Requires Python >= 3.10.**

## Quick Start

```python
import json
from azure.ai.agentserver import AgentServer, InvokeRequest


class GreetingAgent(AgentServer):
async def invoke(self, request: InvokeRequest) -> bytes:
data = json.loads(request.body)
greeting = f"Hello, {data['name']}!"
return json.dumps({"greeting": greeting}).encode()


if __name__ == "__main__":
GreetingAgent().run()
```

```bash
# Start the agent
python my_agent.py

# Call it
curl -X POST http://localhost:8088/invocations \
-H "Content-Type: application/json" \
-d '{"name": "World"}'
# → {"greeting": "Hello, World!"}
```

## Subclassing `AgentServer`

| Method | Required | Description |
|--------|----------|-------------|
| `invoke(request)` | **Yes** | Process an invocation. Return `bytes` or `AsyncGenerator[bytes, None]`. |
| `get_invocation(invocation_id)` | No | Retrieve a stored invocation result. Default returns 404. |
| `cancel_invocation(invocation_id, ...)` | No | Cancel a running invocation. Default returns 404. |

### Routes (Phase 1)

| Route | Method | Description |
|-------|--------|-------------|
| `/invocations` | POST | Create and process an invocation |
| `/invocations/{id}` | GET | Retrieve a previous invocation result |
| `/invocations/{id}/cancel` | POST | Cancel a running invocation |
| `/invocations/docs/openapi.json` | GET | Return the registered OpenAPI spec |
| `/liveness` | GET | Health check |
| `/readiness` | GET | Readiness check |

## OpenAPI Validation

Register a spec to validate request and response bodies at runtime:

```python
spec = {
"openapi": "3.0.0",
"paths": {
"/invocations": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {"name": {"type": "string"}},
"required": ["name"],
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {"greeting": {"type": "string"}},
}
}
}
}
},
}
}
},
}

agent = GreetingAgent(openapi_spec=spec)
agent.run()
```

- Non-conforming **requests** return 400 with details.
- Non-conforming **responses** log warnings but are not blocked.
- `GET /invocations/docs/openapi.json` serves the registered spec (or 404 if none).

## Samples

| Sample | Description |
|--------|-------------|
| `samples/simple_invoke_agent/` | Minimal from-scratch agent |
| `samples/openapi_validated_agent/` | OpenAPI spec with request/response validation |
| `samples/async_invoke_agent/` | Long-running tasks with get & cancel support |
| `samples/human_in_the_loop_agent/` | Synchronous human-in-the-loop interaction |
| `samples/langgraph_invoke_agent/` | Customer-managed LangGraph adapter |
| `samples/agentframework_invoke_agent/` | Customer-managed Agent Framework adapter |

## Vision & Migration Path

### Phase 1 (Current): `/invoke` only

- Ship `azure-ai-agentserver` with the `/invoke` protocol head.
- Existing `agentserver-core` + Layer 3 adapter packages remain as-is for `/responses`
customers.
- Samples show customer-managed framework integration (LangGraph, Agent Framework, Semantic
Kernel, etc.).

### Phase 2 (Future): Add `/responses`

- Add a `/responses` protocol head to `azure-ai-agentserver` as a built-in handler.
- Validation for `/responses` uses the same OpenAPI-based approach (not
`openai.types.responses.*` imports).
- Sample adapters show how to port existing LangGraph / Agent Framework patterns.

### Phase 3 (Future): Deprecate old packages

- Deprecate `azure-ai-agentserver-core` (replaced by this package's `/responses` handler).
- Deprecate `azure-ai-agentserver-agentframework` and `azure-ai-agentserver-langgraph`
(replaced by customer-managed adapter code provided as samples).
- Customers who depend on Layer 3 adapters copy the adapter code into their own projects.

## License

MIT
1 change: 1 addition & 0 deletions sdk/agentserver/azure-ai-agentserver/azure/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------

from ._types import InvokeRequest
from ._version import VERSION
from .server._base import AgentServer

__all__ = ["AgentServer", "InvokeRequest"]
__version__ = VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------


class Constants:
"""Well-known environment variables and defaults for AgentServer."""

AGENT_LOG_LEVEL = "AGENT_LOG_LEVEL"
AGENT_DEBUG_ERRORS = "AGENT_DEBUG_ERRORS"
DEFAULT_AD_PORT = "DEFAULT_AD_PORT"
OTEL_EXPORTER_ENDPOINT = "OTEL_EXPORTER_ENDPOINT"
OTEL_EXPORTER_OTLP_PROTOCOL = "OTEL_EXPORTER_OTLP_PROTOCOL"
ENABLE_APPLICATION_INSIGHTS_LOGGER = "AGENT_APP_INSIGHTS_ENABLED"
APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING"
DEFAULT_PORT = 8088
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------
import logging
import logging.config
import os
from typing import Any, Optional

from ._constants import Constants


def _get_default_log_config() -> dict[str, Any]:
"""Build default logging configuration with level from environment.

:return: A dictionary containing logging configuration.
:rtype: dict[str, Any]
"""
log_level = _get_log_level()
return {
"version": 1,
"disable_existing_loggers": False,
"loggers": {
"azure.ai.agentserver": {
"handlers": ["console"],
"level": log_level,
"propagate": False,
},
},
"handlers": {
"console": {
"formatter": "std_out",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
"level": log_level,
},
},
"formatters": {
"std_out": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
},
},
}


def _get_log_level() -> str:
"""Read log level from environment, defaulting to INFO.

:return: A valid Python log level string.
:rtype: str
"""
log_level = os.getenv(Constants.AGENT_LOG_LEVEL, "INFO").upper()
valid_levels = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
if log_level not in valid_levels:
log_level = "INFO"
return log_level


def configure_logging(log_config: Optional[dict[str, Any]] = None) -> None:
"""Configure logging for the azure.ai.agentserver namespace.

:param log_config: Optional logging configuration dict compatible with logging.config.dictConfig.
:type log_config: Optional[dict[str, Any]]
"""
try:
if log_config is None:
log_config = _get_default_log_config()
logging.config.dictConfig(log_config)
except Exception as exc: # noqa: BLE001
logging.getLogger(__name__).warning("Failed to configure logging: %s", exc)


def get_logger() -> logging.Logger:
"""Return the library-scoped logger.

:return: Configured logger instance for azure.ai.agentserver.
:rtype: logging.Logger
"""
return logging.getLogger("azure.ai.agentserver")
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------
from dataclasses import dataclass


@dataclass
class InvokeRequest:
"""Incoming invoke request.

:param body: Raw request body bytes.
:type body: bytes
:param headers: All HTTP request headers.
:type headers: dict[str, str]
:param invocation_id: Server-generated UUID for this invocation.
:type invocation_id: str
"""

body: bytes
headers: dict[str, str]
invocation_id: str
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------

VERSION = "1.0.0b1"
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------
Loading
Loading