Adapter Overview
19 framework adapters across Python, TypeScript, and Go.
Right page if: you need to choose which Edictum adapter fits your AI framework, or compare integration methods across all 19 adapters in Python, TypeScript, and Go. Wrong page if: you already know your framework -- go to its specific adapter page (e.g., https://docs.edictum.ai/docs/adapters/langchain). For TypeScript adapters, see https://docs.edictum.ai/docs/typescript. For Go adapters, see https://docs.edictum.ai/docs/go. Gotcha: if your framework is not listed, use Edictum.run() directly -- no adapter needed. All adapters share the same constructor signature (guard, session_id, principal, principal_resolver). The same rules.yaml works across all three languages.
Edictum ships 19 framework adapters across Python, TypeScript, and Go. Adapters are thin translation layers -- they convert framework-specific hook events into Edictum envelopes and translate enforcement decisions back into the format each framework expects. All allow/block logic lives in the pipeline, not in the adapters.
This means you get identical enforcement semantics regardless of which framework
or language you use. Switching frameworks requires changing only the adapter wiring, not
your rulesets. The same rules.yaml works across all three SDKs.
Multi-Language Coverage
| Language | Adapters | Frameworks |
|---|---|---|
| Python | 8 | LangChain, OpenAI Agents, CrewAI, Agno, Google ADK, Semantic Kernel, Claude Agent SDK, Nanobot |
| TypeScript | 6 | Claude SDK, LangChain, OpenAI Agents, OpenClaw, Vercel AI, Core |
| Go | 5 | ADK Go, Anthropic, Eino, Genkit, LangChain Go |
Using TypeScript or Go? See the TypeScript SDK or Go SDK for language-specific adapter setup and API details.
The Common Pattern
Every adapter follows the same three-step setup:
from edictum import Edictum, Principal
from edictum.adapters.langchain import LangChainAdapter # or any adapter
# 1. Load rulesets
guard = Edictum.from_yaml("rules.yaml")
# 2. Create the adapter
adapter = LangChainAdapter(
guard=guard,
session_id="my-session-123", # optional -- auto UUID if omitted
principal=Principal(user_id="alice", role="analyst"), # optional
)
# 3. Get the framework-specific hook and wire it in
wrapper = adapter.as_tool_wrapper()Quick Comparison
| Framework | Adapter Class | Integration Method | Returns |
|---|---|---|---|
| Claude Agent SDK | ClaudeAgentSDKAdapter | to_hook_callables() | dict with pre_tool_use and post_tool_use async functions |
| LangChain | LangChainAdapter | as_tool_wrapper() | Wrapper function for ToolNode |
| CrewAI | CrewAIAdapter | register() | Registers global before/after hooks |
| Agno | AgnoAdapter | as_tool_hook() | Wrap-around function |
| Google ADK | GoogleADKAdapter | as_plugin() / as_agent_callbacks() | Plugin object or callback tuple |
| Semantic Kernel | SemanticKernelAdapter | register(kernel) | Registers AUTO_FUNCTION_INVOCATION filter on kernel |
| OpenAI Agents SDK | OpenAIAgentsAdapter | as_guardrails() | (input_guardrail, output_guardrail) tuple |
| Nanobot | NanobotAdapter | wrap_registry() | GovernedToolRegistry drop-in replacement |
Capabilities
| Framework | Can Redact Before LLM | Block Mechanism |
|---|---|---|
| LangChain | Yes | Return "DENIED: reason" as ToolMessage |
| CrewAI | No (side-effect only) | before_hook returns "DENIED: reason" |
| Agno | Yes (hook wraps execution) | Hook returns block string |
| Google ADK | Yes (plugin/callback can replace output) | Returns block payload from plugin/callback path |
| Semantic Kernel | Yes (filter modifies FunctionResult) | Filter sets terminate (configurable) + error |
| Claude SDK | No (side-effect only) | Returns block dict to SDK |
| OpenAI Agents | Block only (reject_content) | reject_content(reason) |
| Nanobot | Yes (wraps execute) | Returns "[DENIED] reason" string |
For regulated environments requiring PII interception (not just detection), use LangChain, Agno, Google ADK, or Semantic Kernel.
Common Constructor
All adapters share the same core constructor signature:
adapter = SomeAdapter(
guard=guard, # required -- the Edictum instance
session_id="my-session-123", # optional -- auto-generated UUID if omitted
principal=principal, # optional -- identity context for audit
)| Parameter | Type | Default | Description |
|---|---|---|---|
guard | Edictum | required | The Edictum instance holding rulesets, limits, and sinks |
session_id | str | None | auto UUID | Groups related tool calls into a session for limit tracking |
principal | Principal | None | None | Identity context attached to every audit event in this session |
principal_resolver | Callable | None | None | Dynamic principal resolution. Called before each tool call to resolve the current principal. Overrides the static principal when set. See mutable principal guide. |
All adapters also expose set_principal(principal) to update the static principal mid-session without replacing the adapter.
Some adapters accept additional parameters. See individual adapter docs for details (e.g., terminate_on_deny for Semantic Kernel).
Common Features
Lifecycle Callbacks
React to allow/block decisions in real time with callbacks on the guard:
guard = Edictum.from_yaml(
"rules.yaml",
on_block=lambda env, reason, cid: print(f"DENIED {env.tool_name}: {reason}"),
on_allow=lambda env: metrics.increment("allowed", tool=env.tool_name),
)These fire in all 8 adapters. See Lifecycle Callbacks for use cases and full API reference.
Observe Mode
Every adapter supports observe mode. When the guard is created with
mode="observe", blocks are logged as CALL_WOULD_DENY audit events but the
tool call is allowed to proceed:
guard = Edictum.from_yaml("rules.yaml", mode="observe")
adapter = SomeAdapter(guard=guard)This lets you deploy rulesets in production to validate enforcement behavior
before switching to mode="enforce".
Audit Sinks
Route audit events to a file with automatic redaction:
from edictum.audit import FileAuditSink, RedactionPolicy
redaction = RedactionPolicy()
sink = FileAuditSink("audit.jsonl", redaction=redaction)
guard = Edictum.from_yaml(
"rules.yaml",
audit_sink=sink,
redaction=redaction,
)Principal Context
Attach identity information to every audit event:
from edictum import Principal
principal = Principal(
user_id="alice",
role="sre",
ticket_ref="JIRA-1234",
claims={"department": "platform"},
)
adapter = SomeAdapter(guard=guard, principal=principal)Choosing an Adapter
Pick the adapter that matches your agent framework:
- Claude Agent SDK -- Building with Anthropic's agent SDK. Hook-based
enforcement via
pre_tool_use/post_tool_use. - LangChain -- Using LangChain agents with
ToolNode. Wrap tool calls viaas_tool_wrapper(). - CrewAI -- Using CrewAI crews. Global before/after hooks applied to every tool call across all agents in the crew.
- Agno -- Using the Agno framework. A
tool_hookscompatible function that wraps tool execution. - Google ADK -- Using Google's ADK runtime. Supports runner plugin wiring and per-agent callback wiring.
- Semantic Kernel -- Using Microsoft Semantic Kernel. Registers an auto-function-invocation filter on the kernel.
- OpenAI Agents SDK -- Using the OpenAI Agents SDK. Per-tool guardrails via
tool_input_guardrails/tool_output_guardrails. - Nanobot -- Using the nanobot multi-channel agent framework. Drop-in
GovernedToolRegistrythat wraps everyexecute()call.
Not sure which adapter? If your framework is in the table above, use that adapter. If it is not listed, use Edictum.run() directly — you get the same pipeline without any adapter. All adapters share the same constructor signature and enforcement semantics.
If your framework is not listed, use Edictum.run() directly -- it provides
the same pipeline without any adapter:
result = await guard.run(
tool_name="read_file",
args={"path": "/etc/passwd"},
tool_callable=my_read_file_fn,
)Performance
All adapters converge to the same overhead -- the adapter layer adds zero measurable cost on top of the core pipeline. Benchmarks from edictum-demo (v0.16.0, Apple M-series):
| Adapter | Median Overhead | p99 |
|---|---|---|
| guard.run() (core) | 43.4 us | ~126 us |
| Claude Agent SDK | 42.8 us | ~107 us |
| LangChain | 42.7 us | ~117 us |
| OpenAI Agents | 42.5 us | ~132 us |
| Agno | 42.2 us | ~77 us |
| Semantic Kernel | 41.8 us | ~129 us |
| CrewAI | 41.6 us | ~119 us |
| Google ADK | 41.1 us | ~105 us |
At ~43 us vs 300-2000 ms LLM round-trips, enforcement adds 0.009% of total latency. The overhead is dominated by YAML rule evaluation, not by adapter translation.
Installation Extras
Each adapter has an optional dependency group:
pip install edictum[langchain] # LangChain adapter
pip install edictum[crewai] # CrewAI adapter
pip install edictum[agno] # Agno adapter
pip install edictum[semantic-kernel] # Semantic Kernel adapter
pip install edictum[openai-agents] # OpenAI Agents SDK adapter
pip install edictum[yaml] # YAML rule engine (no framework deps)
pip install edictum[all] # EverythingGoogle ADK requires google-adk (pip install edictum google-adk).
The Claude Agent SDK and Nanobot adapters have no extra dependencies beyond edictum[yaml].
Last updated on