Skip to content
Merged
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Python SDK for **AI Agent Assembly** — a governance-native runtime for AI agen

## Why use it

- **Framework adapters** for LangChain, LangGraph, CrewAI, OpenAI Agents, Pydantic AI, Google ADK, Haystack, Smolagents, Agno, LlamaIndex, and MCP servers — drop in, no SDK rewrites required.
- **Framework adapters** for LangChain, LangGraph, CrewAI, OpenAI Agents, Pydantic AI, Google ADK, Haystack, Smolagents, Agno, LlamaIndex, Microsoft Agent Framework, and MCP servers — drop in, no SDK rewrites required.
- **Pre-execution policy enforcement** via the `FrameworkAdapter` ABC — block disallowed tool calls before they hit the LLM.
- **Audit trail** — every tool call, prompt, and policy decision is emitted to the gateway with full agent lineage (parent / root / team).
- **Native PyO3 fast path** (optional) — drop into a Rust runtime client when you need sub-millisecond policy checks.
Expand Down Expand Up @@ -43,6 +43,7 @@ are expected to work but are not continuously tested.
| OpenAI Agents | `agents` | `>=0.1.0` | `0.17.x` |
| Smolagents | `smolagents` | `>=1.0.0,<2.0.0` | `1.26.x` |
| Agno | `agno` | `>=2.0.0` | `2.6.x` |
| Microsoft Agent Framework | `agent_framework` | `>=1.0.0,<2.0` | `1.9.x` |

Python framework compatibility is documented **authoritatively in the SDK docs** —
[Framework compatibility](./docs/compatibility/frameworks.md) — because the Python
Expand Down
21 changes: 21 additions & 0 deletions agent_assembly/adapters/microsoft_agent_framework/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Microsoft Agent Framework adapter package.

Governs tool/function execution for Microsoft's unified Agent Framework
(PyPI ``agent-framework``, importable module ``agent_framework``). The single
interception point is ``agent_framework.FunctionTool.invoke`` — the async method
through which every function tool (``@tool``-decorated callables and
``FunctionTool(...)`` instances) executes — so governance applies regardless of
whether the user wires any framework middleware.

See :mod:`agent_assembly.adapters.microsoft_agent_framework.patch` for the hook
mechanics and ADR-0001 for the broader hook architecture.
"""

from agent_assembly.adapters.microsoft_agent_framework.adapter import (
MicrosoftAgentFrameworkAdapter,
)
from agent_assembly.adapters.microsoft_agent_framework.patch import (
MicrosoftAgentFrameworkPatch,
)

__all__ = ["MicrosoftAgentFrameworkAdapter", "MicrosoftAgentFrameworkPatch"]
62 changes: 62 additions & 0 deletions agent_assembly/adapters/microsoft_agent_framework/adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Microsoft Agent Framework adapter."""

from __future__ import annotations

import importlib.util

from agent_assembly.adapters.base import FrameworkAdapter, GovernanceInterceptor
from agent_assembly.adapters.microsoft_agent_framework.patch import (
MicrosoftAgentFrameworkPatch,
)


class MicrosoftAgentFrameworkAdapter(FrameworkAdapter):
"""Adapter for Microsoft Agent Framework governance hook installation."""

def __init__(self, *, process_agent_id: str | None = None) -> None:
self._process_agent_id = process_agent_id
self._patch: MicrosoftAgentFrameworkPatch | None = None

@property
def process_agent_id(self) -> str | None:
return self._process_agent_id

@process_agent_id.setter
def process_agent_id(self, value: str | None) -> None:
self._process_agent_id = value

def get_framework_name(self) -> str:
return "microsoft_agent_framework"

def get_supported_versions(self) -> list[str]:
# Microsoft Agent Framework reached its first stable line at 1.x
# (``agent-framework`` / ``agent-framework-core`` 1.9.0 at time of
# writing). Pin to the 1.x major; the 2.x surface is unverified.
return [">=1.0.0,<2.0"]

def is_available(self) -> bool:
"""Detect the importable ``agent_framework`` package.

WHY override (AAASM-3528 lesson): the framework name
(``microsoft_agent_framework``) deliberately differs from the importable
module (``agent_framework``). The base ``is_available`` imports
``get_framework_name()``, which would never resolve here and make the
adapter silently report unavailable. Detect the real top-level module so
the hook genuinely activates when the framework is installed.
"""
try:
return importlib.util.find_spec("agent_framework") is not None
except (ImportError, ValueError):
return False

def register_hooks(self, interceptor: GovernanceInterceptor) -> None:
self._patch = MicrosoftAgentFrameworkPatch(
callback_handler=interceptor,
process_agent_id=self._process_agent_id,
)
self._patch.apply()

def unregister_hooks(self) -> None:
if self._patch is not None:
self._patch.revert()
self._patch = None
Loading