adapt-agent 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adapt_agent/__init__.py +100 -0
- adapt_agent/__main__.py +8 -0
- adapt_agent/adapters/__init__.py +47 -0
- adapt_agent/adapters/_governed.py +496 -0
- adapt_agent/adapters/base.py +79 -0
- adapt_agent/adapters/claude_agent/__init__.py +49 -0
- adapt_agent/adapters/crewai/__init__.py +71 -0
- adapt_agent/adapters/google_adk/__init__.py +50 -0
- adapt_agent/adapters/langgraph/__init__.py +45 -0
- adapt_agent/adapters/microsoft_agent_framework/__init__.py +47 -0
- adapt_agent/adapters/openai_agents/__init__.py +65 -0
- adapt_agent/adapters/pydantic_ai/__init__.py +45 -0
- adapt_agent/adversarial/__init__.py +254 -0
- adapt_agent/cli/__init__.py +885 -0
- adapt_agent/cli/__main__.py +8 -0
- adapt_agent/core/__init__.py +13 -0
- adapt_agent/core/memory.py +223 -0
- adapt_agent/core/middleware.py +288 -0
- adapt_agent/core/policy.py +404 -0
- adapt_agent/core/trust.py +200 -0
- adapt_agent/core/types.py +82 -0
- adapt_agent/evaluation/__init__.py +209 -0
- adapt_agent/exceptions.py +52 -0
- adapt_agent/observability/__init__.py +287 -0
- adapt_agent/optimization/__init__.py +191 -0
- adapt_agent/optimization/config.py +701 -0
- adapt_agent/optimization/dataset.py +271 -0
- adapt_agent/optimization/evals.py +257 -0
- adapt_agent/optimization/evaluation.py +329 -0
- adapt_agent/optimization/extractors.py +346 -0
- adapt_agent/optimization/introspection/__init__.py +305 -0
- adapt_agent/optimization/introspection/claude_agent.py +150 -0
- adapt_agent/optimization/introspection/crewai.py +190 -0
- adapt_agent/optimization/introspection/google_adk.py +218 -0
- adapt_agent/optimization/introspection/langgraph.py +245 -0
- adapt_agent/optimization/introspection/microsoft_agent_framework.py +177 -0
- adapt_agent/optimization/introspection/openai_agents.py +219 -0
- adapt_agent/optimization/introspection/pydantic_ai.py +238 -0
- adapt_agent/optimization/judge.py +771 -0
- adapt_agent/optimization/judges.py +254 -0
- adapt_agent/optimization/metrics.py +424 -0
- adapt_agent/optimization/optimizers.py +815 -0
- adapt_agent/optimization/parameters.py +307 -0
- adapt_agent/optimization/performance.py +201 -0
- adapt_agent/optimization/proposers.py +464 -0
- adapt_agent/optimization/providers.py +783 -0
- adapt_agent/optimization/runners.py +244 -0
- adapt_agent/optimization/target.py +318 -0
- adapt_agent/patches/__init__.py +114 -0
- adapt_agent/py.typed +0 -0
- adapt_agent/security/__init__.py +11 -0
- adapt_agent/security/firewall.py +366 -0
- adapt_agent/security/taint_tracker.py +346 -0
- adapt_agent/skills/__init__.py +460 -0
- adapt_agent/skills/adapt-agent/SKILL.md +232 -0
- adapt_agent/skills/adapt-agent/references/evals.md +248 -0
- adapt_agent/skills/adapt-agent/references/guardrails.md +313 -0
- adapt_agent/skills/adapt-agent/references/optimization.md +227 -0
- adapt_agent-0.2.0.dist-info/METADATA +401 -0
- adapt_agent-0.2.0.dist-info/RECORD +64 -0
- adapt_agent-0.2.0.dist-info/WHEEL +5 -0
- adapt_agent-0.2.0.dist-info/entry_points.txt +3 -0
- adapt_agent-0.2.0.dist-info/licenses/LICENSE +21 -0
- adapt_agent-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Base adapter interface for framework integrations."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from adapt_agent.core.types import Agent, AgentState
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseAdapter(ABC):
|
|
10
|
+
"""Base class for framework adapters.
|
|
11
|
+
|
|
12
|
+
Adapters provide integration with different LLM agent frameworks,
|
|
13
|
+
converting framework-specific APIs to ADAPT-Agent's unified interface.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, config: dict[str, Any] | None = None):
|
|
17
|
+
"""Initialize the adapter.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
config: Optional configuration dictionary
|
|
21
|
+
"""
|
|
22
|
+
self.config = config or {}
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def wrap_agent(self, agent: Any) -> Agent:
|
|
26
|
+
"""Wrap a framework-specific agent with ADAPT-Agent capabilities.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
agent: Framework-specific agent instance
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Wrapped agent implementing the Agent protocol
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def extract_state(self, agent: Any) -> AgentState:
|
|
38
|
+
"""Extract the current state from a framework-specific agent.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
agent: Framework-specific agent instance
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
AgentState representing the current state
|
|
45
|
+
"""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def inject_middleware(self, agent: Any, middleware: Any) -> Any:
|
|
50
|
+
"""Inject ADAPT-Agent middleware into a framework-specific agent.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
agent: Framework-specific agent instance
|
|
54
|
+
middleware: Middleware to inject
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Modified agent with middleware
|
|
58
|
+
"""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
def validate_agent(self, agent: Any) -> bool:
|
|
62
|
+
"""Validate that an agent is compatible with this adapter.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
agent: Agent to validate
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
True if compatible, False otherwise
|
|
69
|
+
"""
|
|
70
|
+
# Default implementation - should be overridden
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
def get_framework_name(self) -> str:
|
|
74
|
+
"""Get the name of the framework this adapter supports.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Framework name
|
|
78
|
+
"""
|
|
79
|
+
return self.__class__.__name__.replace("Adapter", "")
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Claude Agent SDK adapter for ADAPT-Agent.
|
|
2
|
+
|
|
3
|
+
The `Claude Agent SDK <https://github.com/anthropics/claude-agent-sdk-python>`_
|
|
4
|
+
exposes ``query(prompt=..., options=...)``, an async generator that yields
|
|
5
|
+
message objects (``AssistantMessage`` with ``TextBlock`` content, terminated by
|
|
6
|
+
a ``ResultMessage``).
|
|
7
|
+
|
|
8
|
+
This adapter wraps the ``query`` function (or any callable accepting a
|
|
9
|
+
``prompt`` keyword) and applies ADAPT-Agent's governance pipeline. The async
|
|
10
|
+
stream is drained to a list of messages, so ``execute`` stays synchronous and
|
|
11
|
+
the firewall can scan every text block in the response. Importing this module
|
|
12
|
+
never imports ``claude_agent_sdk``.
|
|
13
|
+
|
|
14
|
+
Example
|
|
15
|
+
-------
|
|
16
|
+
>>> from claude_agent_sdk import query # doctest: +SKIP
|
|
17
|
+
>>> from adapt_agent.adapters import ClaudeAgentSDKAdapter
|
|
18
|
+
>>> from adapt_agent.security import Firewall
|
|
19
|
+
>>> adapter = ClaudeAgentSDKAdapter(firewall=Firewall())
|
|
20
|
+
>>> guarded = adapter.wrap_agent(query) # doctest: +SKIP
|
|
21
|
+
>>> guarded.execute({"messages": [{"role": "user", "content": "hi"}]}) # doctest: +SKIP
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from adapt_agent.adapters._governed import GovernedAdapter, Runner, _extract_prompt
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ClaudeAgentSDKAdapter(GovernedAdapter):
|
|
30
|
+
"""Adapter for integrating ADAPT-Agent with the Claude Agent SDK.
|
|
31
|
+
|
|
32
|
+
Wrap the SDK's ``query`` function (or any callable accepting a ``prompt``
|
|
33
|
+
keyword). The async message stream is drained synchronously. See
|
|
34
|
+
:class:`~adapt_agent.adapters._governed.GovernedAdapter` for the full
|
|
35
|
+
constructor signature.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
framework_name = "Claude Agent SDK"
|
|
39
|
+
run_method_names = ("query", "run")
|
|
40
|
+
operation = "claude_agent.query"
|
|
41
|
+
|
|
42
|
+
def _prepare_input(self, payload: dict[str, Any]) -> Any:
|
|
43
|
+
return _extract_prompt(payload)
|
|
44
|
+
|
|
45
|
+
def _call_runner(self, runner: Runner, payload: dict[str, Any]) -> Any:
|
|
46
|
+
return runner(prompt=self._prepare_input(payload))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
__all__ = ["ClaudeAgentSDKAdapter"]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""CrewAI adapter for ADAPT-Agent.
|
|
2
|
+
|
|
3
|
+
`CrewAI <https://docs.crewai.com>`_ orchestrates a ``Crew`` of agents that you
|
|
4
|
+
run with ``crew.kickoff(inputs=...)`` (sync) or ``crew.kickoff_async(...)``. The
|
|
5
|
+
result is a ``CrewOutput`` whose final text is on ``.raw``.
|
|
6
|
+
|
|
7
|
+
This adapter wraps a ``Crew`` (or anything exposing a callable ``kickoff`` /
|
|
8
|
+
``kickoff_async`` / ``run``) and applies ADAPT-Agent's governance pipeline. The
|
|
9
|
+
payload's ``context`` (its non-``messages`` keys) is forwarded to ``kickoff`` as
|
|
10
|
+
the ``inputs`` mapping of template variables when the runner accepts it.
|
|
11
|
+
Importing this module never imports ``crewai``.
|
|
12
|
+
|
|
13
|
+
Example
|
|
14
|
+
-------
|
|
15
|
+
>>> from crewai import Crew # doctest: +SKIP
|
|
16
|
+
>>> crew = Crew(agents=[...], tasks=[...]) # doctest: +SKIP
|
|
17
|
+
>>> from adapt_agent.adapters import CrewAIAdapter
|
|
18
|
+
>>> from adapt_agent.security import Firewall
|
|
19
|
+
>>> adapter = CrewAIAdapter(firewall=Firewall())
|
|
20
|
+
>>> guarded = adapter.wrap_agent(crew) # doctest: +SKIP
|
|
21
|
+
>>> guarded.execute({"messages": [{"role": "user", "content": "hi"}], "topic": "AI"}) # doctest: +SKIP
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import inspect
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from adapt_agent.adapters._governed import GovernedAdapter, Runner
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CrewAIAdapter(GovernedAdapter):
|
|
31
|
+
"""Adapter for integrating ADAPT-Agent with CrewAI crews.
|
|
32
|
+
|
|
33
|
+
Wrap a ``Crew`` (or any object exposing a callable ``kickoff`` /
|
|
34
|
+
``kickoff_async`` / ``run``). See
|
|
35
|
+
:class:`~adapt_agent.adapters._governed.GovernedAdapter` for the full
|
|
36
|
+
constructor signature.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
framework_name = "CrewAI"
|
|
40
|
+
run_method_names = ("kickoff", "kickoff_async", "run")
|
|
41
|
+
operation = "crewai.kickoff"
|
|
42
|
+
|
|
43
|
+
def _prepare_input(self, payload: dict[str, Any]) -> Any:
|
|
44
|
+
# CrewAI's ``inputs`` are template variables, not a chat transcript.
|
|
45
|
+
return {k: v for k, v in payload.items() if k != "messages"}
|
|
46
|
+
|
|
47
|
+
def _call_runner(self, runner: Runner, payload: dict[str, Any]) -> Any:
|
|
48
|
+
inputs = self._prepare_input(payload)
|
|
49
|
+
# Prefer the idiomatic ``kickoff(inputs=...)`` call when supported.
|
|
50
|
+
if self._accepts_kwarg(runner, "inputs"):
|
|
51
|
+
return runner(inputs=inputs)
|
|
52
|
+
return runner(inputs)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def _accepts_kwarg(runner: Runner, name: str) -> bool:
|
|
56
|
+
try:
|
|
57
|
+
signature = inspect.signature(runner)
|
|
58
|
+
except (TypeError, ValueError):
|
|
59
|
+
return False
|
|
60
|
+
for param in signature.parameters.values():
|
|
61
|
+
if param.kind is inspect.Parameter.VAR_KEYWORD:
|
|
62
|
+
return True
|
|
63
|
+
if param.name == name and param.kind in (
|
|
64
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
65
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
66
|
+
):
|
|
67
|
+
return True
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
__all__ = ["CrewAIAdapter"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Google ADK adapter for ADAPT-Agent.
|
|
2
|
+
|
|
3
|
+
The `Google Agent Development Kit <https://google.github.io/adk-docs>`_ runs an
|
|
4
|
+
agent through a ``Runner``: ``runner.run_async(user_id=..., session_id=...,
|
|
5
|
+
new_message=...)`` (an async generator of ``Event`` objects) or the sync
|
|
6
|
+
``runner.run(...)``. There is no single result object -- you iterate events and
|
|
7
|
+
take the one where ``event.is_final_response()`` is true.
|
|
8
|
+
|
|
9
|
+
Because an ADK run needs a session plus ``user_id`` / ``session_id`` /
|
|
10
|
+
``new_message`` arguments, the idiomatic wrap target here is a **callable** you
|
|
11
|
+
provide that performs the run and returns (or yields) the events. The adapter
|
|
12
|
+
drains async/sync generators automatically and screens the text inside each
|
|
13
|
+
event's ``content.parts``. Importing this module never imports ``google.adk``.
|
|
14
|
+
|
|
15
|
+
Example
|
|
16
|
+
-------
|
|
17
|
+
>>> from google.adk.runners import InMemoryRunner # doctest: +SKIP
|
|
18
|
+
>>> from google.genai import types # doctest: +SKIP
|
|
19
|
+
>>> runner = InMemoryRunner(agent=my_agent) # doctest: +SKIP
|
|
20
|
+
>>> def run(payload): # doctest: +SKIP
|
|
21
|
+
... msg = types.Content(role="user", parts=[types.Part(text=payload["prompt"])])
|
|
22
|
+
... return runner.run(user_id="u", session_id="s", new_message=msg)
|
|
23
|
+
>>> from adapt_agent.adapters import GoogleADKAdapter
|
|
24
|
+
>>> from adapt_agent.security import Firewall
|
|
25
|
+
>>> adapter = GoogleADKAdapter(firewall=Firewall())
|
|
26
|
+
>>> guarded = adapter.wrap_agent(run) # doctest: +SKIP
|
|
27
|
+
>>> guarded.execute({"prompt": "hi"}) # doctest: +SKIP
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from adapt_agent.adapters._governed import GovernedAdapter
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GoogleADKAdapter(GovernedAdapter):
|
|
34
|
+
"""Adapter for integrating ADAPT-Agent with Google ADK agents.
|
|
35
|
+
|
|
36
|
+
Wrap a callable ``run(payload) -> events`` that drives an ADK ``Runner`` (the
|
|
37
|
+
events may be a sync generator, an async generator, or a list). The full
|
|
38
|
+
payload dict is passed through to your callable. See
|
|
39
|
+
:class:`~adapt_agent.adapters._governed.GovernedAdapter` for the full
|
|
40
|
+
constructor signature.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
framework_name = "Google ADK"
|
|
44
|
+
# An ADK run requires session/user arguments, so binding to a bare
|
|
45
|
+
# ``runner.run`` would mismatch its signature; wrap a callable instead.
|
|
46
|
+
run_method_names = ()
|
|
47
|
+
operation = "google_adk.run"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = ["GoogleADKAdapter"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""LangGraph adapter for ADAPT-Agent.
|
|
2
|
+
|
|
3
|
+
This adapter wraps a compiled LangGraph graph (or any object exposing an
|
|
4
|
+
``invoke`` method) with ADAPT-Agent's security and governance primitives:
|
|
5
|
+
a :class:`~adapt_agent.security.Firewall`, an
|
|
6
|
+
:class:`~adapt_agent.adversarial.AdversarialDefense`, a
|
|
7
|
+
:class:`~adapt_agent.core.PolicyEnforcer`, an
|
|
8
|
+
:class:`~adapt_agent.observability.AgentObserver` and a
|
|
9
|
+
:class:`~adapt_agent.core.Middleware` pipeline.
|
|
10
|
+
|
|
11
|
+
The adapter is intentionally *structural*: it integrates with anything that
|
|
12
|
+
looks like a compiled LangGraph graph (a callable ``invoke(state) -> state``)
|
|
13
|
+
rather than importing LangGraph at module load time. The optional ``langgraph``
|
|
14
|
+
dependency is only needed at runtime to build the graph you pass in; importing
|
|
15
|
+
this module never imports ``langgraph``.
|
|
16
|
+
|
|
17
|
+
Example
|
|
18
|
+
-------
|
|
19
|
+
>>> from adapt_agent.adapters import LangGraphAdapter
|
|
20
|
+
>>> from adapt_agent.security import Firewall
|
|
21
|
+
>>> fw = Firewall(max_content_length=10_000)
|
|
22
|
+
>>> fw.add_blocked_pattern(r"(?i)ignore previous instructions")
|
|
23
|
+
>>> adapter = LangGraphAdapter(firewall=fw)
|
|
24
|
+
>>> guarded = adapter.wrap_agent(compiled_graph) # doctest: +SKIP
|
|
25
|
+
>>> guarded.execute({"messages": [{"role": "user", "content": "hi"}]}) # doctest: +SKIP
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from adapt_agent.adapters._governed import GovernedAdapter, _extract_texts
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LangGraphAdapter(GovernedAdapter):
|
|
32
|
+
"""Adapter for integrating ADAPT-Agent with LangGraph agents.
|
|
33
|
+
|
|
34
|
+
Wrap a **compiled** LangGraph graph -- or any object exposing a callable
|
|
35
|
+
``invoke(state) -> state``. See :class:`~adapt_agent.adapters._governed.GovernedAdapter`
|
|
36
|
+
for the full constructor signature; every security control is optional and
|
|
37
|
+
keyword-only.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
framework_name = "LangGraph"
|
|
41
|
+
run_method_names = ("invoke",)
|
|
42
|
+
operation = "langgraph.invoke"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = ["LangGraphAdapter", "_extract_texts"]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Microsoft Agent Framework adapter for ADAPT-Agent.
|
|
2
|
+
|
|
3
|
+
`Microsoft Agent Framework <https://github.com/microsoft/agent-framework>`_ is
|
|
4
|
+
Microsoft's unified successor to Semantic Kernel and AutoGen. Its primary
|
|
5
|
+
runnable object is a ``ChatAgent`` (also exported as ``Agent``), whose
|
|
6
|
+
``run(prompt)`` coroutine returns an ``AgentRunResponse`` carrying the final
|
|
7
|
+
text on ``.text``.
|
|
8
|
+
|
|
9
|
+
This adapter wraps a ``ChatAgent`` (or anything exposing a callable ``run`` /
|
|
10
|
+
``run_sync``) and applies ADAPT-Agent's governance pipeline. ``run`` is async;
|
|
11
|
+
the wrapper resolves the coroutine transparently, so ``execute`` stays
|
|
12
|
+
synchronous. Importing this module never imports ``agent_framework``.
|
|
13
|
+
|
|
14
|
+
Example
|
|
15
|
+
-------
|
|
16
|
+
>>> from agent_framework.openai import OpenAIChatClient # doctest: +SKIP
|
|
17
|
+
>>> agent = OpenAIChatClient().create_agent(instructions="...") # doctest: +SKIP
|
|
18
|
+
>>> from adapt_agent.adapters import MicrosoftAgentFrameworkAdapter
|
|
19
|
+
>>> from adapt_agent.security import Firewall
|
|
20
|
+
>>> adapter = MicrosoftAgentFrameworkAdapter(firewall=Firewall())
|
|
21
|
+
>>> guarded = adapter.wrap_agent(agent) # doctest: +SKIP
|
|
22
|
+
>>> guarded.execute({"messages": [{"role": "user", "content": "hi"}]}) # doctest: +SKIP
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from adapt_agent.adapters._governed import GovernedAdapter, _extract_prompt
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MicrosoftAgentFrameworkAdapter(GovernedAdapter):
|
|
31
|
+
"""Adapter for integrating ADAPT-Agent with Microsoft Agent Framework agents.
|
|
32
|
+
|
|
33
|
+
Wrap a ``ChatAgent`` (or any object exposing a callable ``run`` / ``run_sync``).
|
|
34
|
+
The agent is run from a prompt string derived from the payload's latest user
|
|
35
|
+
message. See :class:`~adapt_agent.adapters._governed.GovernedAdapter` for the
|
|
36
|
+
full constructor signature.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
framework_name = "Microsoft Agent Framework"
|
|
40
|
+
run_method_names = ("run", "run_sync")
|
|
41
|
+
operation = "agent_framework.run"
|
|
42
|
+
|
|
43
|
+
def _prepare_input(self, payload: dict[str, Any]) -> Any:
|
|
44
|
+
return _extract_prompt(payload)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
__all__ = ["MicrosoftAgentFrameworkAdapter"]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""OpenAI Agents SDK adapter for ADAPT-Agent.
|
|
2
|
+
|
|
3
|
+
The `OpenAI Agents SDK <https://openai.github.io/openai-agents-python>`_ runs an
|
|
4
|
+
``Agent`` through the ``Runner`` class: ``Runner.run_sync(agent, input)`` (sync)
|
|
5
|
+
or ``await Runner.run(agent, input)`` (async). The result is a ``RunResult``
|
|
6
|
+
whose final text is on ``.final_output``.
|
|
7
|
+
|
|
8
|
+
You can wrap an OpenAI ``Agent`` directly -- the adapter lazily imports
|
|
9
|
+
``agents.Runner`` at execution time and drives it for you -- or wrap any plain
|
|
10
|
+
callable ``run(input) -> result`` if you want full control over the run
|
|
11
|
+
configuration. Importing this module never imports ``agents``.
|
|
12
|
+
|
|
13
|
+
Example
|
|
14
|
+
-------
|
|
15
|
+
>>> from agents import Agent # doctest: +SKIP
|
|
16
|
+
>>> agent = Agent(name="Assistant", instructions="...") # doctest: +SKIP
|
|
17
|
+
>>> from adapt_agent.adapters import OpenAIAgentsAdapter
|
|
18
|
+
>>> from adapt_agent.security import Firewall
|
|
19
|
+
>>> adapter = OpenAIAgentsAdapter(firewall=Firewall())
|
|
20
|
+
>>> guarded = adapter.wrap_agent(agent) # doctest: +SKIP
|
|
21
|
+
>>> guarded.execute({"messages": [{"role": "user", "content": "hi"}]}) # doctest: +SKIP
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Any, Optional
|
|
25
|
+
|
|
26
|
+
from adapt_agent.adapters._governed import GovernedAdapter, Runner, _extract_prompt
|
|
27
|
+
from adapt_agent.exceptions import MissingDependencyError
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OpenAIAgentsAdapter(GovernedAdapter):
|
|
31
|
+
"""Adapter for integrating ADAPT-Agent with the OpenAI Agents SDK.
|
|
32
|
+
|
|
33
|
+
Wrap an OpenAI ``Agent`` (driven via a lazily-imported ``Runner``) or any
|
|
34
|
+
plain callable ``run(input) -> result``. See
|
|
35
|
+
:class:`~adapt_agent.adapters._governed.GovernedAdapter` for the full
|
|
36
|
+
constructor signature.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
framework_name = "OpenAI Agents"
|
|
40
|
+
run_method_names = ("run_sync", "run")
|
|
41
|
+
operation = "openai_agents.run"
|
|
42
|
+
|
|
43
|
+
def _resolve_runner(self, agent: Any) -> Runner | None:
|
|
44
|
+
# An object exposing run_sync/run (e.g. a custom runner) or a plain
|
|
45
|
+
# callable is used as-is.
|
|
46
|
+
runner = super()._resolve_runner(agent)
|
|
47
|
+
if runner is not None:
|
|
48
|
+
return runner
|
|
49
|
+
# Otherwise treat ``agent`` as an OpenAI Agent and drive it through the
|
|
50
|
+
# SDK's Runner, imported lazily so importing this module stays cheap.
|
|
51
|
+
return lambda prompt: self._run_with_sdk(agent, prompt)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def _run_with_sdk(agent: Any, prompt: Any) -> Any:
|
|
55
|
+
try:
|
|
56
|
+
from agents import Runner
|
|
57
|
+
except ImportError as exc: # pragma: no cover - exercised only without the dep
|
|
58
|
+
raise MissingDependencyError("openai-agents", "openai-agents") from exc
|
|
59
|
+
return Runner.run_sync(agent, prompt)
|
|
60
|
+
|
|
61
|
+
def _prepare_input(self, payload: dict[str, Any]) -> Any:
|
|
62
|
+
return _extract_prompt(payload)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__all__ = ["OpenAIAgentsAdapter"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Pydantic AI adapter for ADAPT-Agent.
|
|
2
|
+
|
|
3
|
+
`Pydantic AI <https://ai.pydantic.dev>`_ centres on an ``Agent`` whose
|
|
4
|
+
``run_sync(prompt)`` (sync) and ``run(prompt)`` (async) methods return an
|
|
5
|
+
``AgentRunResult`` carrying the final output on ``.output``.
|
|
6
|
+
|
|
7
|
+
This adapter wraps a Pydantic AI ``Agent`` (or anything exposing a callable
|
|
8
|
+
``run_sync`` / ``run``) and applies ADAPT-Agent's governance pipeline. Importing
|
|
9
|
+
this module never imports ``pydantic_ai``.
|
|
10
|
+
|
|
11
|
+
Example
|
|
12
|
+
-------
|
|
13
|
+
>>> from pydantic_ai import Agent # doctest: +SKIP
|
|
14
|
+
>>> agent = Agent("openai:gpt-4o", system_prompt="...") # doctest: +SKIP
|
|
15
|
+
>>> from adapt_agent.adapters import PydanticAIAdapter
|
|
16
|
+
>>> from adapt_agent.security import Firewall
|
|
17
|
+
>>> adapter = PydanticAIAdapter(firewall=Firewall())
|
|
18
|
+
>>> guarded = adapter.wrap_agent(agent) # doctest: +SKIP
|
|
19
|
+
>>> guarded.execute({"messages": [{"role": "user", "content": "hi"}]}) # doctest: +SKIP
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from adapt_agent.adapters._governed import GovernedAdapter, _extract_prompt
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PydanticAIAdapter(GovernedAdapter):
|
|
28
|
+
"""Adapter for integrating ADAPT-Agent with Pydantic AI agents.
|
|
29
|
+
|
|
30
|
+
Wrap a Pydantic AI ``Agent`` (or any object exposing a callable
|
|
31
|
+
``run_sync`` / ``run``). The agent is run from a prompt string derived from
|
|
32
|
+
the payload's latest user message. See
|
|
33
|
+
:class:`~adapt_agent.adapters._governed.GovernedAdapter` for the full
|
|
34
|
+
constructor signature.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
framework_name = "Pydantic AI"
|
|
38
|
+
run_method_names = ("run_sync", "run")
|
|
39
|
+
operation = "pydantic_ai.run"
|
|
40
|
+
|
|
41
|
+
def _prepare_input(self, payload: dict[str, Any]) -> Any:
|
|
42
|
+
return _extract_prompt(payload)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
__all__ = ["PydanticAIAdapter"]
|