Edictum
Framework Adapters

Adapter Overview

Edictum ships eight framework adapters.

AI Assistance

Right page if: you need to choose which Edictum adapter fits your AI framework, or compare integration methods across all 8 adapters. Wrong page if: you already know your framework -- go to its specific adapter page (e.g., https://docs.edictum.ai/docs/adapters/langchain). For a deeper tradeoff comparison, see https://docs.edictum.ai/docs/guides/adapter-comparison. 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).

Edictum ships eight framework adapters. 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/deny logic lives in the pipeline, not in the adapters.

This means you get identical enforcement semantics regardless of which framework you use. Switching frameworks requires changing only the adapter wiring, not your contracts.

LangChain
middleware / tool wrapper
CrewAI
global hooks
Agno
tool hook
Semantic Kernel
auto-invocation filter
OpenAI Agents
guardrails
Claude Agent SDK
hook callables
Nanobot
governed registry
Google ADK
plugin / callbacks
Adapter Layer
ToolEnvelope
frozen snapshot — tool_name, args, principal
GovernancePipeline
pre_execute()
→ PreDecision (allow | deny | pending_approval)
post_execute()
→ PostDecision (warnings, redactions)
Audit Event
stdout · file · OTel · server

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 contracts
guard = Edictum.from_yaml("contracts.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

FrameworkAdapter ClassIntegration MethodReturns
Claude Agent SDKClaudeAgentSDKAdapterto_hook_callables()dict with pre_tool_use and post_tool_use async functions
LangChainLangChainAdapteras_tool_wrapper()Wrapper function for ToolNode
CrewAICrewAIAdapterregister()Registers global before/after hooks
AgnoAgnoAdapteras_tool_hook()Wrap-around function
Google ADKGoogleADKAdapteras_plugin() / as_agent_callbacks()Plugin object or callback tuple
Semantic KernelSemanticKernelAdapterregister(kernel)Registers AUTO_FUNCTION_INVOCATION filter on kernel
OpenAI Agents SDKOpenAIAgentsAdapteras_guardrails()(input_guardrail, output_guardrail) tuple
NanobotNanobotAdapterwrap_registry()GovernedToolRegistry drop-in replacement

Capabilities

FrameworkCan Redact Before LLMDeny Mechanism
LangChainYesReturn "DENIED: reason" as ToolMessage
CrewAINo (side-effect only)before_hook returns "DENIED: reason"
AgnoYes (hook wraps execution)Hook returns denial string
Google ADKYes (plugin/callback can replace output)Returns denial payload from plugin/callback path
Semantic KernelYes (filter modifies FunctionResult)Filter sets terminate (configurable) + error
Claude SDKNo (side-effect only)Returns deny dict to SDK
OpenAI AgentsDeny only (reject_content)reject_content(reason)
NanobotYes (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
)
ParameterTypeDefaultDescription
guardEdictumrequiredThe Edictum instance holding contracts, limits, and sinks
session_idstr | Noneauto UUIDGroups related tool calls into a session for limit tracking
principalPrincipal | NoneNoneIdentity context attached to every audit event in this session
principal_resolverCallable | NoneNoneDynamic 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/deny decisions in real time with callbacks on the guard:

guard = Edictum.from_yaml(
    "contracts.yaml",
    on_deny=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", denials are logged as CALL_WOULD_DENY audit events but the tool call is allowed to proceed:

guard = Edictum.from_yaml("contracts.yaml", mode="observe")
adapter = SomeAdapter(guard=guard)

This lets you deploy contracts 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(
    "contracts.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 via as_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_hooks compatible 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 GovernedToolRegistry that wraps every execute() 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.15.0, Apple M-series):

AdapterMedian Overheadp99
guard.run() (core)43.4 us~126 us
Claude Agent SDK42.8 us~107 us
LangChain42.7 us~117 us
OpenAI Agents42.5 us~132 us
Agno42.2 us~77 us
Semantic Kernel41.8 us~129 us
CrewAI41.6 us~119 us
Google ADK41.1 us~105 us

At ~43 us vs 300-2000 ms LLM round-trips, governance adds 0.009% of total latency. The overhead is dominated by YAML contract 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 contract engine (no framework deps)
pip install edictum[all]              # Everything

Google 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

On this page