Framework Adapter Comparison
Edictum ships eight framework adapters.
Right page if: you are comparing adapters to understand capability differences -- especially PII redaction support, deny mechanisms, and postcondition effect limitations. Wrong page if: you already know your framework and need setup instructions -- go to its adapter page (e.g., https://docs.edictum.ai/docs/adapters/langchain). For a quick table of all adapters, see https://docs.edictum.ai/docs/adapters/overview. Gotcha: only LangChain, Agno, Semantic Kernel, Nanobot, and Google ADK can intercept tool results before the LLM sees them. CrewAI, Claude SDK, and OpenAI Agents are side-effect only for postconditions.
Edictum ships eight framework adapters. This guide helps you choose the right one and understand the tradeoffs.
Quick Comparison
| Framework | Integration Method | Can Redact Before LLM | Deny Mechanism | Cost (same task) |
|---|---|---|---|---|
| LangChain | as_tool_wrapper() | Yes | Return "DENIED: reason" as ToolMessage | $0.025 |
| OpenAI Agents | as_guardrails() | Deny only (reject_content) | reject_content(reason) | $0.018 |
| CrewAI | register() | No (side-effect only) | before_hook returns "DENIED: reason" | $0.040 |
| Agno | as_tool_hook() | Yes (hook wraps execution) | Hook returns denial string | N/A |
| Google ADK | as_plugin() / as_agent_callbacks() | Yes | Plugin/callback returns denial payload | N/A |
| Semantic Kernel | register(kernel) | Yes (filter modifies FunctionResult) | Filter sets terminate (configurable) + error | $0.008 |
| Claude SDK | to_hook_callables() | No (side-effect only) | Returns deny dict to SDK | N/A |
| Nanobot | wrap_registry() | Yes (wraps execute) | Returns "[DENIED] reason" string | N/A |
Cost column reflects benchmarks from edictum-demo using each framework's default model. N/A indicates no published benchmark data.
Which Adapter Should I Use?
- Need full PII interception? -- Use LangChain, Agno, Semantic Kernel, Nanobot, or Google ADK. These adapters can replace the tool result before the LLM sees it.
- Cheapest per-task cost? -- Semantic Kernel ($0.008 per task in benchmarks).
- Simplest integration? -- Claude SDK or Agno. Both require minimal wiring.
- Need ADK plugin/callback governance? -- Use Google ADK adapter.
- Using CrewAI? -- CrewAI adapter is the only option. Note that CrewAI hooks are global (applied to every tool across all agents in the crew).
Per-Adapter Snippets
LangChain
from edictum import Edictum, Principal
from edictum.adapters.langchain import LangChainAdapter
from langgraph.prebuilt import ToolNode
guard = Edictum.from_yaml("contracts.yaml")
adapter = LangChainAdapter(guard=guard, principal=Principal(role="analyst"))
wrapper = adapter.as_tool_wrapper()
# Pass to: ToolNode(tools=tools, wrap_tool_call=wrapper)OpenAI Agents
from edictum import Edictum, Principal
from edictum.adapters.openai_agents import OpenAIAgentsAdapter
from agents import function_tool
guard = Edictum.from_yaml("contracts.yaml")
adapter = OpenAIAgentsAdapter(guard=guard, principal=Principal(role="assistant"))
input_gr, output_gr = adapter.as_guardrails()
# Pass to: @function_tool(tool_input_guardrails=[input_gr], tool_output_guardrails=[output_gr])CrewAI
from edictum import Edictum, Principal
from edictum.adapters.crewai import CrewAIAdapter
guard = Edictum.from_yaml("contracts.yaml")
adapter = CrewAIAdapter(guard=guard, principal=Principal(role="researcher"))
adapter.register()
# Hooks are now globally registered for all CrewAI tool callsAgno
from edictum import Edictum, Principal
from edictum.adapters.agno import AgnoAdapter
guard = Edictum.from_yaml("contracts.yaml")
adapter = AgnoAdapter(guard=guard, principal=Principal(role="assistant"))
hook = adapter.as_tool_hook()
# Pass to: Agent(tool_hooks=[hook])Semantic Kernel
from edictum import Edictum, Principal
from edictum.adapters.semantic_kernel import SemanticKernelAdapter
guard = Edictum.from_yaml("contracts.yaml")
adapter = SemanticKernelAdapter(guard=guard, principal=Principal(role="analyst"))
adapter.register(kernel)
# Filter is now registered on the kernel instanceGoogle ADK
from edictum import Edictum, Principal
from edictum.adapters.google_adk import GoogleADKAdapter
guard = Edictum.from_yaml("contracts.yaml")
adapter = GoogleADKAdapter(guard=guard, principal=Principal(role="analyst"))
plugin = adapter.as_plugin()
# Pass to your ADK Runner: plugins=[plugin]Claude SDK
from edictum import Edictum, Principal
from edictum.adapters.claude_agent_sdk import ClaudeAgentSDKAdapter
guard = Edictum.from_yaml("contracts.yaml")
adapter = ClaudeAgentSDKAdapter(guard=guard, principal=Principal(role="sre"))
hooks = adapter.to_hook_callables()
# Use in your agent loop — see bridge recipe in Claude SDK adapter docsNanobot
from edictum import Edictum, Principal
from edictum.adapters.nanobot import NanobotAdapter
guard = Edictum.from_yaml("contracts.yaml")
adapter = NanobotAdapter(guard=guard, principal=Principal(role="user"))
governed_registry = adapter.wrap_registry(agent.tool_registry)
# Replace: agent.tool_registry = governed_registryKnown Limitations
LangChain
as_middleware()is sync-only. If an asyncio event loop is already running (Jupyter, FastAPI), useas_tool_wrapper()(handles nested loops via ThreadPoolExecutor) oras_async_tool_wrapper(). See LangChain adapter docs.
OpenAI Agents
- Guardrails are per-tool via
@function_tool(), not per-agent. Input and output guardrails are separate functions; the adapter correlates them using insertion-order (FIFO), which assumes sequential tool execution. See OpenAI Agents adapter docs.
CrewAI
- Hooks are global -- they apply to every tool across all agents in the crew. There is no per-agent hook scoping. See CrewAI adapter docs.
- Side-effect only --
on_postcondition_warncallbacks fire for side effects (logging, alerting) but cannot replace the tool result. Postconditionredact/denyeffects setPostCallResult.resultfor wrapper consumers but cannot modify what CrewAI passes to the model.
Agno
- Tool callables must accept keyword arguments (the adapter spreads the args dict with
**arguments). See Agno adapter docs.
Google ADK
- Use plugin path for runner-wide governance and callback path for live/streaming mode. See Google ADK adapter docs.
Semantic Kernel
- By default,
context.terminate = Trueon deny stops all auto-invocations in the current turn, not just the denied tool. Setterminate_on_deny=Falseto allow remaining tool calls to proceed. See Semantic Kernel adapter docs.
Claude SDK
- Side-effect only -- the hook callables (
to_hook_callables()) cannot replace the tool result. PII detection is logged but not intercepted before reaching the model. Postconditionredact/denyeffects setPostCallResult.resultfor wrapper consumers but cannot modify the SDK's result flow. A warning is logged at adapter construction when postconditions declare these effects. See Claude SDK adapter docs.
Nanobot
GovernedToolRegistry.execute()always returns a string (matching nanobot'sToolRegistrycontract). Non-string results from the inner registry are converted viastr(). See Nanobot adapter docs.
OpenAI Agents (postcondition enforcement)
- The output guardrail can
.allow()or.reject_content()but cannot substitute the tool result. Postconditioneffect: denyon pure/read tools returns.reject_content(), fully enforcing the denial. Postconditioneffect: redactis not supported because native guardrails cannot transform tool results. A warning is logged at adapter construction when postconditions declareeffect: redact.
Last updated on