agentrust-py 0.0.5__tar.gz → 0.0.7__tar.gz
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.
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/CHANGELOG.md +16 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/PKG-INFO +5 -2
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/__init__.py +9 -2
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/adapters/_core.py +10 -6
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/adapters/autogen.py +129 -41
- agentrust_py-0.0.7/agentrust_sdk/adapters/crewai.py +237 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/config.py +1 -1
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/decorator.py +12 -1
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/manifest.py +1 -1
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/pyproject.toml +5 -2
- agentrust_py-0.0.7/tests/adapters/_ag2.py +57 -0
- agentrust_py-0.0.7/tests/adapters/_crews.py +29 -0
- agentrust_py-0.0.7/tests/adapters/test_ag2_governed_function.py +232 -0
- agentrust_py-0.0.7/tests/adapters/test_ag2_reply_hook.py +157 -0
- agentrust_py-0.0.7/tests/adapters/test_ag2_research_team_example.py +122 -0
- agentrust_py-0.0.7/tests/adapters/test_crewai_baseline.py +28 -0
- agentrust_py-0.0.7/tests/adapters/test_crewai_failure_modes.py +107 -0
- agentrust_py-0.0.7/tests/adapters/test_crewai_governed_tools.py +215 -0
- agentrust_py-0.0.7/tests/adapters/test_crewai_holds_and_chain.py +177 -0
- agentrust_py-0.0.7/tests/adapters/test_crewai_quickstart_example.py +116 -0
- agentrust_py-0.0.7/tests/adapters/test_crewai_task_checks.py +170 -0
- agentrust_py-0.0.7/tests/adapters/test_langgraph_quickstart_example.py +133 -0
- agentrust_py-0.0.7/tests/conformance/bindings.py +193 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/conformance/test_c1_c10.py +4 -5
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/conftest.py +31 -0
- agentrust_py-0.0.7/tests/test_adapter_tier_gate.py +46 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_adapters.py +1 -2
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_async_advanced.py +3 -6
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_callback_url.py +1 -1
- agentrust_py-0.0.5/agentrust_sdk/adapters/crewai.py +0 -104
- agentrust_py-0.0.5/tests/conformance/bindings.py +0 -100
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/.gitignore +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/LICENSE +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/README.md +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/_evaluations.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/_real_validator.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/adapters/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/adapters/claude_agents.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/adapters/langgraph.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/adapters/mcp.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/adapters/openai_agents.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/audit/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/audit/logger.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/auth.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/auto.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/autoload.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/classifier/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/classifier/engine.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/classifier/profiles.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/classifier/system_prompt.md +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/cli.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/cli_certify.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/client.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/embedded.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/golden_suite.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/governance/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/governance/hitl.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/governance/kernel.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/governance/kill_switch.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/governance/modes.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/hooks.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/models.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/notifications/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/notifications/discord.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/notifications/dispatcher.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/notifications/slack.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/policy/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/policy/backends/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/policy/backends/native.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/policy/backends/opa.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/policy/engine.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/policy/loader.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/py.typed +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/queue_replay.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/tiers.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/tool_tracking.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/version_negotiation.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/agentrust_sdk/webhooks.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/policy/base.yaml +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/policy/default-policy.json +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/policy/financial.yaml +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/policy/medical.yaml +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/policy/profiles/advisory.json +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/policy/profiles/balanced.json +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/policy/profiles/strict.json +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/_cfg.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/adapters/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/adapters/_graphs.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/adapters/test_langgraph_baseline.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/adapters/test_langgraph_failure_modes.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/adapters/test_langgraph_governed_tools.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/adapters/test_langgraph_holds_and_chain.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/adapters/test_langgraph_node.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/conformance/__init__.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_chaos.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_cli_certify.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_client.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_client_adapter_contract.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_config.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_decorator.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_embedded.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_embedded_adapter_contract.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_golden_suite.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_governor.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_hooks.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_manifest.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_policy_packaging.py +0 -0
- {agentrust_py-0.0.5 → agentrust_py-0.0.7}/tests/test_webhooks.py +0 -0
|
@@ -7,6 +7,22 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.0.6] - 2026-10-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **CrewAI tool calls are checked before they run.** `governed_tools(tools, agent_id=...)` (`agentrust_sdk.adapters.crewai`) wraps the tools given to a CrewAI `Agent`: each call is pre-checked (tool trust, kill switch, trust chain), held for a person when the decision is `escalate`, then post-checked. A refused call reaches the agent as the tool's result (`Blocked by AgentTrust: ...`), so the crew finishes. Proven against real CrewAI by the same C1–C10 conformance suite as LangGraph.
|
|
14
|
+
- `guardrail(agent_id=...)` for `Task(guardrail=...)`: a blocked task output returns `(False, "Blocked by AgentTrust: ...")`, so CrewAI asks the agent for a new answer instead of ending the crew.
|
|
15
|
+
- `examples/crewai_quickstart.py`, the calculator crew governed by AgentTrust.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **`agentrust-py[crewai]` now needs CrewAI 1.x** (`crewai>=1.0,<2.0`). CrewAI 0.x is no longer supported.
|
|
19
|
+
- **`AgentTrustCallback`:** the gateway URL defaults to `AGENTRUST_GATEWAY_URL` instead of a hardcoded `http://localhost:8000`; with `AGENTRUST_FAILURE_MODE=closed` an unreachable gateway now raises `GatewayUnavailableError` instead of passing the output through. Its constructor arguments and its behaviour in open mode are unchanged.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **Console agent keys (`at_live_...`) were refused by the SDK's client-side tier check** with `<framework> adapter requires Team tier`, a paying Team customer's included, so a connection needed `AGENTRUST_ENV=development`. Only a JWT key states its tier, so only a JWT key is checked on the client; for any other key the gateway, which knows the owner's plan, decides. Affects `governed_tools` for LangGraph and the CrewAI adapter.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
10
26
|
## [0.0.5] - 2026-10-26
|
|
11
27
|
|
|
12
28
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentrust-py
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: AgentTrust Edge — Full Governance Platform. Policy Engine + MAD Classifier + HITL + Kill Switch.
|
|
5
5
|
Project-URL: Homepage, https://agentrust.io
|
|
6
6
|
Project-URL: Documentation, https://docs.agentrust.io
|
|
@@ -23,10 +23,13 @@ Requires-Python: >=3.10
|
|
|
23
23
|
Requires-Dist: httpx<1.0.0,>=0.27.0
|
|
24
24
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
25
25
|
Requires-Dist: pyyaml<7.0,>=6.0.3
|
|
26
|
+
Provides-Extra: ag2
|
|
27
|
+
Requires-Dist: ag2<1.0,>=0.9; extra == 'ag2'
|
|
26
28
|
Provides-Extra: autoload
|
|
27
29
|
Provides-Extra: crewai
|
|
28
|
-
Requires-Dist: crewai<
|
|
30
|
+
Requires-Dist: crewai<2.0,>=1.0; extra == 'crewai'
|
|
29
31
|
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: crewai<2.0,>=1.0; extra == 'dev'
|
|
30
33
|
Requires-Dist: fastapi>=0.137.1; extra == 'dev'
|
|
31
34
|
Requires-Dist: httpx<1.0.0,>=0.27.0; extra == 'dev'
|
|
32
35
|
Requires-Dist: langgraph<2.0.0,>=0.2.0; extra == 'dev'
|
|
@@ -38,7 +38,12 @@ from .client import (
|
|
|
38
38
|
from .config import SDK_CONFIG
|
|
39
39
|
from .hooks import auto_instrument, auto_wrap
|
|
40
40
|
from .adapters.mcp import mcp_tool_guard, MCPServerGuard
|
|
41
|
-
from .adapters.autogen import
|
|
41
|
+
from .adapters.autogen import (
|
|
42
|
+
AgentTrustGroupChatMonitor,
|
|
43
|
+
AgentTrustReplyHook,
|
|
44
|
+
governed_function,
|
|
45
|
+
governed_functions,
|
|
46
|
+
)
|
|
42
47
|
from .adapters.claude_agents import ClaudeAgentGuard
|
|
43
48
|
from .adapters.openai_agents import agent_guard as openai_agent_guard, OpenAIAgentsGuard, SwarmGuard
|
|
44
49
|
from .adapters.crewai import AgentTrustCallback
|
|
@@ -87,6 +92,8 @@ from .webhooks import (
|
|
|
87
92
|
__all__ = [
|
|
88
93
|
# Primary API
|
|
89
94
|
"harness",
|
|
95
|
+
"governed_function",
|
|
96
|
+
"governed_functions",
|
|
90
97
|
# Embedded gateway (zero-dep plug-and-play)
|
|
91
98
|
"embed_gateway",
|
|
92
99
|
"EmbeddedGateway",
|
|
@@ -147,4 +154,4 @@ __all__ = [
|
|
|
147
154
|
"validate",
|
|
148
155
|
]
|
|
149
156
|
|
|
150
|
-
__version__ = "0.0.
|
|
157
|
+
__version__ = "0.0.7"
|
|
@@ -28,7 +28,7 @@ import logging
|
|
|
28
28
|
import os
|
|
29
29
|
import time
|
|
30
30
|
from contextvars import ContextVar
|
|
31
|
-
from dataclasses import dataclass
|
|
31
|
+
from dataclasses import dataclass, replace
|
|
32
32
|
from typing import Any, Awaitable, Callable, Literal
|
|
33
33
|
|
|
34
34
|
from .. import client as _client
|
|
@@ -71,6 +71,8 @@ class Refused:
|
|
|
71
71
|
envelope_id: str | None = None
|
|
72
72
|
reason_code: str | None = None
|
|
73
73
|
outcome: str = "block"
|
|
74
|
+
# The gateway answer behind a refused output check, when there is one.
|
|
75
|
+
response: ValidateResponse | None = None
|
|
74
76
|
|
|
75
77
|
|
|
76
78
|
def _real_envelope(envelope_id: str | None) -> str | None:
|
|
@@ -80,12 +82,14 @@ def _real_envelope(envelope_id: str | None) -> str | None:
|
|
|
80
82
|
def check_adapter_tier(capability: Capability, api_key: str | None = None) -> None:
|
|
81
83
|
"""Raise RuntimeError when *api_key*'s tier does not include *capability*.
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
Only a JWT key states its tier, so only a JWT key is checked here. An
|
|
86
|
+
opaque key, such as a console agent key (``at_live_...``), carries no
|
|
87
|
+
tier: the gateway knows the owner's plan and decides. Also skipped in
|
|
88
|
+
development/demo/test environments and without a key.
|
|
85
89
|
"""
|
|
86
90
|
env = os.environ.get("AGENTRUST_ENV", "production").lower()
|
|
87
91
|
key = api_key or SDK_CONFIG.api_key
|
|
88
|
-
if env in ("development", "dev", "demo", "test") or not key or
|
|
92
|
+
if env in ("development", "dev", "demo", "test") or not key or key.count(".") != 2:
|
|
89
93
|
return
|
|
90
94
|
from ..auth import resolve_key
|
|
91
95
|
|
|
@@ -281,10 +285,10 @@ class Governor:
|
|
|
281
285
|
|
|
282
286
|
def _output_verdict(self, resp: ValidateResponse) -> Allowed | Refused:
|
|
283
287
|
if resp.decision.outcome == "block":
|
|
284
|
-
return self._refused(
|
|
288
|
+
return replace(self._refused(
|
|
285
289
|
resp.decision.reason, _real_envelope(resp.envelope_id), resp.decision.reason_code,
|
|
286
290
|
outcome="block", raise_blocked=False,
|
|
287
|
-
)
|
|
291
|
+
), response=resp)
|
|
288
292
|
return Allowed(resp, _real_envelope(resp.envelope_id))
|
|
289
293
|
|
|
290
294
|
def _hold(self, client: Any, pre: ValidateResponse) -> Refused | None:
|
|
@@ -5,6 +5,11 @@ Architecture doc (two-pager): "Connects to … AutoGen agents."
|
|
|
5
5
|
|
|
6
6
|
Supports both AutoGen v0.2 (pyautogen) and AutoGen v0.4 (autogen-agentchat):
|
|
7
7
|
|
|
8
|
+
Pattern 0 — governed_function (AG2 / pyautogen 0.2 tools):
|
|
9
|
+
Wrap each tool before register_function so every call is pre-checked,
|
|
10
|
+
held for approval when needed, run, post-checked and audited. A refusal
|
|
11
|
+
is returned as the tool result, so the chat keeps going.
|
|
12
|
+
|
|
8
13
|
Pattern 1 — AgentTrustReplyHook (v0.2 + v0.4):
|
|
9
14
|
Attach to any ConversableAgent / AssistantAgent to validate every reply
|
|
10
15
|
before it is sent.
|
|
@@ -25,19 +30,111 @@ Requires Team tier.
|
|
|
25
30
|
"""
|
|
26
31
|
from __future__ import annotations
|
|
27
32
|
|
|
33
|
+
import functools
|
|
34
|
+
import inspect
|
|
28
35
|
import logging
|
|
29
36
|
import time
|
|
30
|
-
from typing import Any, Callable
|
|
37
|
+
from typing import Any, Callable, Sequence
|
|
38
|
+
|
|
39
|
+
from ..tiers import Capability
|
|
40
|
+
from ._core import Allowed, Governor, OnBlock, Refused, check_adapter_tier
|
|
31
41
|
|
|
32
42
|
logger = logging.getLogger(__name__)
|
|
33
43
|
|
|
44
|
+
_FRAMEWORK = "ag2"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def governed_function(
|
|
48
|
+
fn: Callable[..., Any],
|
|
49
|
+
*,
|
|
50
|
+
agent_id: str,
|
|
51
|
+
name: str | None = None,
|
|
52
|
+
on_block: OnBlock = "refuse",
|
|
53
|
+
review_timeout_s: float = 300.0,
|
|
54
|
+
user: str = "ag2",
|
|
55
|
+
base_url: str | None = None,
|
|
56
|
+
api_key: str | None = None,
|
|
57
|
+
governor: Governor | None = None,
|
|
58
|
+
) -> Callable[..., Any]:
|
|
59
|
+
"""Wrap an AG2 tool function so AgentTrust governs every call.
|
|
60
|
+
|
|
61
|
+
The result keeps the name, docstring and signature of *fn*, so AG2 builds
|
|
62
|
+
the same tool schema from it. Pass it to ``register_function`` or
|
|
63
|
+
``register_for_llm``. Async functions stay async.
|
|
64
|
+
"""
|
|
65
|
+
check_adapter_tier(Capability.AUTOGEN_ADAPTER, api_key)
|
|
66
|
+
gov = governor or Governor(
|
|
67
|
+
agent_id,
|
|
68
|
+
framework=_FRAMEWORK,
|
|
69
|
+
on_block=on_block,
|
|
70
|
+
review_timeout_s=review_timeout_s,
|
|
71
|
+
user=user,
|
|
72
|
+
base_url=base_url,
|
|
73
|
+
api_key=api_key,
|
|
74
|
+
)
|
|
75
|
+
tool_name = name or fn.__name__
|
|
76
|
+
signature = inspect.signature(fn)
|
|
77
|
+
|
|
78
|
+
def call_args(args: tuple, kwargs: dict) -> dict[str, Any]:
|
|
79
|
+
bound = signature.bind(*args, **kwargs)
|
|
80
|
+
bound.apply_defaults()
|
|
81
|
+
return dict(bound.arguments)
|
|
82
|
+
|
|
83
|
+
def unwrap(outcome: Allowed | Refused) -> Any:
|
|
84
|
+
# The refusal text is the tool result the agent reads next.
|
|
85
|
+
return outcome.message if isinstance(outcome, Refused) else outcome.result
|
|
86
|
+
|
|
87
|
+
if inspect.iscoroutinefunction(fn):
|
|
88
|
+
|
|
89
|
+
@functools.wraps(fn)
|
|
90
|
+
async def governed(*args: Any, **kwargs: Any) -> Any:
|
|
91
|
+
params = call_args(args, kwargs)
|
|
92
|
+
outcome = await gov.aguard_tool(
|
|
93
|
+
tool_name, params, lambda: fn(*args, **kwargs), input=_describe_call(tool_name, params),
|
|
94
|
+
)
|
|
95
|
+
return unwrap(outcome)
|
|
96
|
+
|
|
97
|
+
else:
|
|
34
98
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
99
|
+
@functools.wraps(fn)
|
|
100
|
+
def governed(*args: Any, **kwargs: Any) -> Any:
|
|
101
|
+
params = call_args(args, kwargs)
|
|
102
|
+
outcome = gov.guard_tool(
|
|
103
|
+
tool_name, params, lambda: fn(*args, **kwargs), input=_describe_call(tool_name, params),
|
|
104
|
+
)
|
|
105
|
+
return unwrap(outcome)
|
|
106
|
+
|
|
107
|
+
return governed
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def governed_functions(
|
|
111
|
+
fns: Sequence[Callable[..., Any]],
|
|
112
|
+
*,
|
|
113
|
+
agent_id: str,
|
|
114
|
+
on_block: OnBlock = "refuse",
|
|
115
|
+
review_timeout_s: float = 300.0,
|
|
116
|
+
user: str = "ag2",
|
|
117
|
+
base_url: str | None = None,
|
|
118
|
+
api_key: str | None = None,
|
|
119
|
+
governor: Governor | None = None,
|
|
120
|
+
) -> list[Callable[..., Any]]:
|
|
121
|
+
"""Wrap each function with :func:`governed_function`, sharing one Governor."""
|
|
122
|
+
check_adapter_tier(Capability.AUTOGEN_ADAPTER, api_key)
|
|
123
|
+
gov = governor or Governor(
|
|
124
|
+
agent_id,
|
|
125
|
+
framework=_FRAMEWORK,
|
|
126
|
+
on_block=on_block,
|
|
127
|
+
review_timeout_s=review_timeout_s,
|
|
128
|
+
user=user,
|
|
129
|
+
base_url=base_url,
|
|
130
|
+
api_key=api_key,
|
|
131
|
+
)
|
|
132
|
+
return [governed_function(fn, agent_id=agent_id, governor=gov) for fn in fns]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _describe_call(name: str, args: dict[str, Any]) -> str:
|
|
136
|
+
"""The proposed call as the pre-check's request input."""
|
|
137
|
+
return f"tool call {name}({', '.join(f'{k}={v!r}' for k, v in args.items())})"
|
|
41
138
|
|
|
42
139
|
|
|
43
140
|
def _validate_sync(
|
|
@@ -50,7 +147,8 @@ def _validate_sync(
|
|
|
50
147
|
framework: str,
|
|
51
148
|
block_on_block: bool,
|
|
52
149
|
) -> None:
|
|
53
|
-
from agentrust_sdk.client import AgentTrustClient
|
|
150
|
+
from agentrust_sdk.client import AgentTrustClient, GatewayUnavailableError
|
|
151
|
+
from agentrust_sdk.config import SDK_CONFIG
|
|
54
152
|
from agentrust_sdk.decorator import _check_decision, BlockedError
|
|
55
153
|
|
|
56
154
|
try:
|
|
@@ -63,9 +161,11 @@ def _validate_sync(
|
|
|
63
161
|
framework=framework,
|
|
64
162
|
)
|
|
65
163
|
_check_decision(resp, block_on_block, False)
|
|
66
|
-
except BlockedError:
|
|
164
|
+
except (BlockedError, GatewayUnavailableError):
|
|
67
165
|
raise
|
|
68
166
|
except Exception as exc:
|
|
167
|
+
if SDK_CONFIG.failure_mode == "closed":
|
|
168
|
+
raise
|
|
69
169
|
logger.warning("[AgentTrust] AutoGen validate failed (non-fatal): %s", exc)
|
|
70
170
|
|
|
71
171
|
|
|
@@ -99,7 +199,7 @@ class AgentTrustReplyHook:
|
|
|
99
199
|
block_on_block: bool = True,
|
|
100
200
|
framework: str = "AutoGen",
|
|
101
201
|
) -> None:
|
|
102
|
-
|
|
202
|
+
check_adapter_tier(Capability.AUTOGEN_ADAPTER, api_key)
|
|
103
203
|
self._agent_id = agent_id
|
|
104
204
|
self._base_url = base_url
|
|
105
205
|
self._api_key = api_key
|
|
@@ -111,47 +211,35 @@ class AgentTrustReplyHook:
|
|
|
111
211
|
# AutoGen v0.2
|
|
112
212
|
# ------------------------------------------------------------------
|
|
113
213
|
|
|
114
|
-
def
|
|
115
|
-
self,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"""AutoGen v0.2 reply function — registered via register_reply()."""
|
|
122
|
-
if not messages:
|
|
123
|
-
return False, None
|
|
124
|
-
|
|
125
|
-
last = messages[-1]
|
|
126
|
-
content = last.get("content", "") or ""
|
|
127
|
-
role = last.get("role", "assistant")
|
|
128
|
-
|
|
129
|
-
if role not in ("assistant", "user"):
|
|
130
|
-
return False, None
|
|
214
|
+
def _check_outgoing(
|
|
215
|
+
self, sender: Any = None, message: Any = None, recipient: Any = None, silent: bool = False,
|
|
216
|
+
) -> Any:
|
|
217
|
+
"""AG2 ``process_message_before_send`` hook: check the agent's own reply."""
|
|
218
|
+
content = message.get("content") if isinstance(message, dict) else message
|
|
219
|
+
if not isinstance(content, str) or not content:
|
|
220
|
+
return message
|
|
131
221
|
|
|
132
222
|
t0 = time.perf_counter()
|
|
133
|
-
output = {"content": content[:4000], "role": role}
|
|
134
223
|
_validate_sync(
|
|
135
|
-
self._agent_id, self._base_url, self._api_key,
|
|
136
|
-
|
|
224
|
+
self._agent_id, self._base_url, self._api_key, self._user,
|
|
225
|
+
content[:500], {"content": content[:4000], "role": "assistant"},
|
|
226
|
+
self._framework, self._block_on_block,
|
|
137
227
|
)
|
|
138
228
|
logger.debug(
|
|
139
229
|
"[AgentTrust] AutoGen reply validated in %.1f ms", (time.perf_counter() - t0) * 1000
|
|
140
230
|
)
|
|
141
|
-
return
|
|
231
|
+
return message
|
|
142
232
|
|
|
143
233
|
def attach(self, agent: Any) -> None:
|
|
144
|
-
"""Attach to an AutoGen v0.2 ConversableAgent
|
|
145
|
-
|
|
234
|
+
"""Attach to an AG2 / AutoGen v0.2 ConversableAgent.
|
|
235
|
+
|
|
236
|
+
Checks every message the agent sends, before it leaves the agent.
|
|
237
|
+
"""
|
|
238
|
+
if not hasattr(agent, "register_hook"):
|
|
146
239
|
raise TypeError(
|
|
147
|
-
f"Expected a ConversableAgent with
|
|
240
|
+
f"Expected a ConversableAgent with register_hook(), got {type(agent).__name__}"
|
|
148
241
|
)
|
|
149
|
-
agent.
|
|
150
|
-
trigger=[],
|
|
151
|
-
reply_func=self._reply_func,
|
|
152
|
-
position=0,
|
|
153
|
-
name="agentrust_reply_hook",
|
|
154
|
-
)
|
|
242
|
+
agent.register_hook("process_message_before_send", self._check_outgoing)
|
|
155
243
|
logger.info(
|
|
156
244
|
"[AgentTrust] AutoGen reply hook attached to agent '%s'", getattr(agent, "name", "?")
|
|
157
245
|
)
|
|
@@ -209,7 +297,7 @@ class AgentTrustGroupChatMonitor:
|
|
|
209
297
|
block_on_block: bool = False, # Default False — group chats tolerate review
|
|
210
298
|
framework: str = "AutoGen",
|
|
211
299
|
) -> None:
|
|
212
|
-
|
|
300
|
+
check_adapter_tier(Capability.AUTOGEN_ADAPTER, api_key)
|
|
213
301
|
self._agent_id = agent_id
|
|
214
302
|
self._base_url = base_url
|
|
215
303
|
self._api_key = api_key
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CrewAI adapter.
|
|
3
|
+
|
|
4
|
+
- ``governed_tools(tools, agent_id=...)`` wraps each tool so every call is
|
|
5
|
+
pre-checked before it runs (tool trust, kill switch, review holds,
|
|
6
|
+
fail-closed) and its result post-checked. A refused call reaches the agent
|
|
7
|
+
as the tool's result text, so the crew keeps running.
|
|
8
|
+
- ``AgentTrustCallback`` validates a task's output after the task finished.
|
|
9
|
+
|
|
10
|
+
Requires Team tier. Gated at construction time.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import inspect
|
|
16
|
+
import logging
|
|
17
|
+
from functools import lru_cache
|
|
18
|
+
from typing import Any, Callable, Sequence
|
|
19
|
+
|
|
20
|
+
from ._core import Allowed, Governor, OnBlock, Refused, check_adapter_tier
|
|
21
|
+
from ..client import GatewayUnavailableError
|
|
22
|
+
from ..tiers import Capability
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
_FRAMEWORK = "CrewAI"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def governed_tools(
|
|
30
|
+
tools: Sequence[Any],
|
|
31
|
+
*,
|
|
32
|
+
agent_id: str,
|
|
33
|
+
on_block: OnBlock = "refuse",
|
|
34
|
+
review_timeout_s: float = 300.0,
|
|
35
|
+
user: str = "crewai",
|
|
36
|
+
base_url: str | None = None,
|
|
37
|
+
api_key: str | None = None,
|
|
38
|
+
governor: Governor | None = None,
|
|
39
|
+
) -> list[Any]:
|
|
40
|
+
"""Wrap CrewAI tools so AgentTrust governs every call.
|
|
41
|
+
|
|
42
|
+
Accepts ``BaseTool`` instances (subclasses and the ``@tool`` decorator)
|
|
43
|
+
and plain functions with a docstring. Each wrapped tool keeps its name,
|
|
44
|
+
description and argument schema, so the agent sees the same definition as
|
|
45
|
+
before. Pass the result as the ``Agent``'s ``tools=``.
|
|
46
|
+
"""
|
|
47
|
+
check_adapter_tier(Capability.CREWAI_ADAPTER, api_key)
|
|
48
|
+
gov = governor or Governor(
|
|
49
|
+
agent_id,
|
|
50
|
+
framework=_FRAMEWORK,
|
|
51
|
+
on_block=on_block,
|
|
52
|
+
review_timeout_s=review_timeout_s,
|
|
53
|
+
user=user,
|
|
54
|
+
base_url=base_url,
|
|
55
|
+
api_key=api_key,
|
|
56
|
+
)
|
|
57
|
+
from crewai.tools import BaseTool, tool as as_tool
|
|
58
|
+
|
|
59
|
+
wrapper = _governed_tool_class()
|
|
60
|
+
return [
|
|
61
|
+
wrapper.wrap(t if isinstance(t, BaseTool) else as_tool(t), gov)
|
|
62
|
+
for t in tools
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _describe_call(name: str, args: dict[str, Any]) -> str:
|
|
67
|
+
"""The proposed call as the pre-check's request input."""
|
|
68
|
+
return f"tool call {name}({', '.join(f'{k}={v!r}' for k, v in args.items())})"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _never_cache(*_: Any, **__: Any) -> bool:
|
|
72
|
+
# A cached call skips ``_run``, so it would skip the pre-check too.
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _runs_async(tool: Any) -> bool:
|
|
77
|
+
"""True when the tool has a real ``_arun`` rather than the base stub."""
|
|
78
|
+
func = getattr(tool, "func", None)
|
|
79
|
+
if func is not None:
|
|
80
|
+
return inspect.iscoroutinefunction(func)
|
|
81
|
+
from crewai.tools import BaseTool
|
|
82
|
+
|
|
83
|
+
return type(tool)._arun is not BaseTool._arun
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@lru_cache(maxsize=None)
|
|
87
|
+
def _governed_tool_class() -> type:
|
|
88
|
+
"""Build GovernedTool on first use, so crewai stays optional."""
|
|
89
|
+
from crewai.tools import BaseTool
|
|
90
|
+
|
|
91
|
+
class GovernedTool(BaseTool):
|
|
92
|
+
"""A tool whose every call goes through an AgentTrust Governor."""
|
|
93
|
+
|
|
94
|
+
inner: Any
|
|
95
|
+
governor: Any
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def wrap(cls, inner: BaseTool, governor: Governor) -> "GovernedTool":
|
|
99
|
+
return cls(
|
|
100
|
+
name=inner.name,
|
|
101
|
+
description=inner.description,
|
|
102
|
+
args_schema=inner.args_schema,
|
|
103
|
+
result_as_answer=inner.result_as_answer,
|
|
104
|
+
cache_function=_never_cache,
|
|
105
|
+
inner=inner,
|
|
106
|
+
governor=governor,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def _run(self, *args: Any, **kwargs: Any) -> Any:
|
|
110
|
+
outcome = self.governor.guard_tool(
|
|
111
|
+
self.name, kwargs,
|
|
112
|
+
lambda: self.inner.run(**kwargs),
|
|
113
|
+
input=_describe_call(self.name, kwargs),
|
|
114
|
+
)
|
|
115
|
+
return self._unwrap(outcome)
|
|
116
|
+
|
|
117
|
+
async def _arun(self, *args: Any, **kwargs: Any) -> Any:
|
|
118
|
+
async def call() -> Any:
|
|
119
|
+
if _runs_async(self.inner):
|
|
120
|
+
return await self.inner.arun(**kwargs)
|
|
121
|
+
return await asyncio.to_thread(lambda: self.inner.run(**kwargs))
|
|
122
|
+
|
|
123
|
+
outcome = await self.governor.aguard_tool(
|
|
124
|
+
self.name, kwargs, call, input=_describe_call(self.name, kwargs),
|
|
125
|
+
)
|
|
126
|
+
return self._unwrap(outcome)
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def _unwrap(outcome: Allowed | Refused) -> Any:
|
|
130
|
+
# The refusal text is the observation the agent reads next.
|
|
131
|
+
return outcome.message if isinstance(outcome, Refused) else outcome.result
|
|
132
|
+
|
|
133
|
+
return GovernedTool
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _task_payload(output: Any) -> dict[str, Any]:
|
|
137
|
+
"""A task's output as the dict /validate expects."""
|
|
138
|
+
raw = getattr(output, "raw", output)
|
|
139
|
+
return raw if isinstance(raw, dict) else {"result": str(raw)}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def guardrail(
|
|
143
|
+
*,
|
|
144
|
+
agent_id: str,
|
|
145
|
+
user: str = "crewai",
|
|
146
|
+
base_url: str | None = None,
|
|
147
|
+
api_key: str | None = None,
|
|
148
|
+
governor: Governor | None = None,
|
|
149
|
+
) -> Callable[[Any], tuple[bool, Any]]:
|
|
150
|
+
"""A ``Task(guardrail=...)`` that checks the task's output with AgentTrust.
|
|
151
|
+
|
|
152
|
+
Approved output passes through. A blocked output returns
|
|
153
|
+
``(False, "Blocked by AgentTrust: ...")``, so CrewAI hands the refusal to
|
|
154
|
+
the agent and asks it to try again. In closed mode a gateway outage raises
|
|
155
|
+
``GatewayUnavailableError``.
|
|
156
|
+
"""
|
|
157
|
+
check_adapter_tier(Capability.CREWAI_ADAPTER, api_key)
|
|
158
|
+
gov = governor or Governor(
|
|
159
|
+
agent_id, framework=_FRAMEWORK, user=user, base_url=base_url, api_key=api_key,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# No return annotation: CrewAI checks it at runtime and rejects the string
|
|
163
|
+
# form that ``from __future__ import annotations`` produces.
|
|
164
|
+
def check(output):
|
|
165
|
+
verdict = gov.check_output(input="crewai-task", output=_task_payload(output))
|
|
166
|
+
if isinstance(verdict, Refused):
|
|
167
|
+
return False, verdict.message
|
|
168
|
+
return True, output
|
|
169
|
+
|
|
170
|
+
return check
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class AgentTrustCallback:
|
|
174
|
+
"""
|
|
175
|
+
CrewAI task callback that validates output via AgentTrust.
|
|
176
|
+
|
|
177
|
+
Requires Team tier ($149/mo). Prefer ``guardrail()`` when a blocked output
|
|
178
|
+
should send the agent back to try again rather than raise.
|
|
179
|
+
|
|
180
|
+
Usage::
|
|
181
|
+
|
|
182
|
+
from crewai import Task
|
|
183
|
+
from agentrust_sdk.adapters.crewai import AgentTrustCallback
|
|
184
|
+
|
|
185
|
+
cb = AgentTrustCallback(agent_id="research-crew")
|
|
186
|
+
task = Task(description="...", callback=cb.on_task_complete)
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
def __init__(
|
|
190
|
+
self,
|
|
191
|
+
agent_id: str,
|
|
192
|
+
*,
|
|
193
|
+
base_url: str | None = None,
|
|
194
|
+
api_key: str | None = None,
|
|
195
|
+
user: str = "crewai",
|
|
196
|
+
block_on_block: bool = True,
|
|
197
|
+
block_on_review: bool = False,
|
|
198
|
+
framework: str = _FRAMEWORK,
|
|
199
|
+
governor: Governor | None = None,
|
|
200
|
+
) -> None:
|
|
201
|
+
check_adapter_tier(Capability.CREWAI_ADAPTER, api_key)
|
|
202
|
+
self._agent_id = agent_id
|
|
203
|
+
self._block_on_block = block_on_block
|
|
204
|
+
self._block_on_review = block_on_review
|
|
205
|
+
self._governor = governor or Governor(
|
|
206
|
+
agent_id, framework=framework, user=user, base_url=base_url, api_key=api_key,
|
|
207
|
+
)
|
|
208
|
+
self._last_result: Any = None
|
|
209
|
+
|
|
210
|
+
def on_task_complete(self, output: Any) -> Any:
|
|
211
|
+
from agentrust_sdk.decorator import _check_decision
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
verdict = self._governor.check_output(input="crewai-task", output=_task_payload(output))
|
|
215
|
+
except GatewayUnavailableError:
|
|
216
|
+
raise # closed mode: the caller must stop
|
|
217
|
+
except Exception as exc: # kept from before: an unexpected client error is not fatal
|
|
218
|
+
logger.warning("[AgentTrust] CrewAI callback failed (non-fatal): %s", exc)
|
|
219
|
+
return output
|
|
220
|
+
resp = verdict.response if isinstance(verdict, Refused) else verdict.result
|
|
221
|
+
self._last_result = resp
|
|
222
|
+
_check_decision(resp, self._block_on_block, self._block_on_review)
|
|
223
|
+
return output
|
|
224
|
+
|
|
225
|
+
def task_callback(self, fn: Callable) -> Callable:
|
|
226
|
+
import functools
|
|
227
|
+
|
|
228
|
+
@functools.wraps(fn)
|
|
229
|
+
def wrapper(output: Any) -> Any:
|
|
230
|
+
self.on_task_complete(output)
|
|
231
|
+
return fn(output)
|
|
232
|
+
|
|
233
|
+
return wrapper
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def last_result(self) -> Any:
|
|
237
|
+
return self._last_result
|
|
@@ -329,6 +329,17 @@ def _check_decision(resp: Any, block_on_block: bool, block_on_review: bool) -> N
|
|
|
329
329
|
raise BlockedError(outcome, resp.decision.reason, resp.envelope_id)
|
|
330
330
|
|
|
331
331
|
|
|
332
|
+
def _ag2_installed() -> bool:
|
|
333
|
+
"""AG2 installs the ``autogen`` module under the distribution name ``ag2``."""
|
|
334
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
335
|
+
|
|
336
|
+
try:
|
|
337
|
+
version("ag2")
|
|
338
|
+
except PackageNotFoundError:
|
|
339
|
+
return False
|
|
340
|
+
return True
|
|
341
|
+
|
|
342
|
+
|
|
332
343
|
def _detect_framework() -> str:
|
|
333
344
|
"""Best-effort framework detection from installed packages."""
|
|
334
345
|
try:
|
|
@@ -341,7 +352,7 @@ def _detect_framework() -> str:
|
|
|
341
352
|
("llama_index", "LlamaIndex"),
|
|
342
353
|
]:
|
|
343
354
|
if importlib.util.find_spec(name):
|
|
344
|
-
return label
|
|
355
|
+
return "AG2" if name == "autogen" and _ag2_installed() else label
|
|
345
356
|
except Exception:
|
|
346
357
|
pass
|
|
347
358
|
return "REST"
|
|
@@ -50,7 +50,7 @@ class CertifyManifest(BaseModel):
|
|
|
50
50
|
display_name: str = ""
|
|
51
51
|
framework: str = Field(
|
|
52
52
|
default="rest",
|
|
53
|
-
pattern=r"^(langchain|langgraph|crewai|autogen|openai-agents|mcp|rest)$",
|
|
53
|
+
pattern=r"^(langchain|langgraph|crewai|autogen|ag2|openai-agents|mcp|rest)$",
|
|
54
54
|
)
|
|
55
55
|
language: str = Field(default="python", pattern=r"^(python|typescript|rest)$")
|
|
56
56
|
model: ModelDeclaration = Field(default_factory=ModelDeclaration)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentrust-py"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.7"
|
|
8
8
|
description = "AgentTrust Edge — Full Governance Platform. Policy Engine + MAD Classifier + HITL + Kill Switch."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -40,7 +40,9 @@ llm = [
|
|
|
40
40
|
]
|
|
41
41
|
# Framework adapters (optional, tier-gated)
|
|
42
42
|
langgraph = ["langgraph>=0.2.0,<2.0.0"]
|
|
43
|
-
crewai = ["crewai>=
|
|
43
|
+
crewai = ["crewai>=1.0,<2.0"]
|
|
44
|
+
# AG2 0.x keeps the ConversableAgent API (module `autogen`); 1.x is a new API
|
|
45
|
+
ag2 = ["ag2>=0.9,<1.0"]
|
|
44
46
|
openai = ["openai>=2.41.1,<3.0.0"]
|
|
45
47
|
langchain = ["langchain>=0.2.0,<1.0.0"]
|
|
46
48
|
# Retry/backoff (recommended for production)
|
|
@@ -68,6 +70,7 @@ dev = [
|
|
|
68
70
|
"uvicorn>=0.29.0",
|
|
69
71
|
"httpx>=0.27.0,<1.0.0",
|
|
70
72
|
"langgraph>=0.2.0,<2.0.0",
|
|
73
|
+
"crewai>=1.0,<2.0",
|
|
71
74
|
]
|
|
72
75
|
|
|
73
76
|
[project.scripts]
|