Faramesh Docs
SDKs

Python SDK

GovernedToolSet, ToolDeniedException, and the transport configuration for Faramesh's Python SDK.

The Python SDK is the in-process integration point for native frameworks. LangGraph, LangChain, CrewAI, OpenAI Agents, Google ADK, Anthropic SDK. Wrap your tool list with GovernedToolSet; every tool call is evaluated by the daemon before execution.

Install

Terminal
pip install faramesh-sdk

Supports Python 3.10+. Pure Python, no native extensions.

GovernedToolSet

agent.py
from faramesh import GovernedToolSet

tools = GovernedToolSet(
    [search_docs, send_email, charge_card],
    agent_id="payments-bot",
)

Accepts:

  • LangChain BaseTool subclasses
  • LangChain @tool decorated functions
  • OpenAI Agents function_tool decorated functions
  • CrewAI BaseTool subclasses
  • Plain callables, they become tools named after the function

The wrapped collection exposes the same interface as your framework expects. For LangChain it's a list of BaseTool; for OpenAI Agents it's the SDK's tool registry; for CrewAI it's a list of tool instances.

Constructor parameters

agent.py
GovernedToolSet(
    tools,                       # iterable of tools
    agent_id="my-agent",         # required, must match an agent in governance.fms
    transport=None,              # auto-selected (see Transport)
    on_defer="raise",            # "raise" | "block" | callback
    on_deny="raise",
    timeout_seconds=10.0,
    fail_open=False,             # only ever True for explicit dev paths
)
ParameterDescription
agent_idRequired. Matches agent "<id>" { ... } in governance.fms. Can also be set via FARAMESH_AGENT_ID.
on_deferraise (default) raises ToolDeniedException. block blocks the call and polls for approval. A callback gets (approval_id, denial) for custom handling.
on_denySame shape; block is invalid here (denials never resolve).
timeout_secondsCap on the daemon round-trip. Default 10 s.
fail_openIf True, daemon-unreachable produces a permit with a warning DPR. Never enable in production.

ToolDeniedException

agent.py
from faramesh import ToolDeniedException

try:
    result = tool.invoke(args)
except ToolDeniedException as denial:
    denial.code           # "POLICY_DENY", "POLICY_DEFER", "RATE_EXCEEDED", ...
    denial.human_message  # free-form message
    denial.rule_ref       # "governance.fms:12"
    denial.resolution     # structured hint (retry_after, pending_approval, ...)
    denial.approval_id    # convenience for POLICY_DEFER
    denial.action_id      # the DPR id for this denial

denial.resolution mirrors the denial code JSON shape. The fields you'll handle most:

Resolution typeFields
pending_approvalapproval_id
retry_afterretry_after_seconds
budget_resetresets_at
rule_blockrule_id

Async tools

GovernedToolSet dispatches async tools transparently. For a tool that defines both arun and run, the wrapper picks the matching method on invocation:

agent.py
async def my_tool(query: str) -> str:
    ...

graph = create_react_agent(model, GovernedToolSet([my_tool], agent_id="..."))
await graph.ainvoke({...})

Transport selection

The SDK auto-selects how it talks to the daemon. Priority:

OrderVariableMechanism
1FARAMESH_REMOTE_URLHTTPS POST /v1/evaluate. Use for serverless agents (Lambda, Cloud Run) where there is no local daemon.
2FARAMESH_SOCKETUnix socket JSON-RPC. The default for local stacks. Set by faramesh apply and faramesh dev.
3FARAMESH_BASE_URLHTTPS fallback for local stacks listening on a port instead of a socket.

Optional:

VariableDescription
FARAMESH_TOKENBearer token, required for remote and authenticated local.
FARAMESH_AGENT_IDDefault agent id for processes that wrap tools dynamically.
FARAMESH_PRINCIPAL_TOKENPass-through identity claim (JWT) when the daemon trusts an upstream IDP.

You can also pass a Transport instance directly:

agent.py
from faramesh.transport import HttpTransport, SocketTransport

tools = GovernedToolSet(
    [...],
    agent_id="payments-bot",
    transport=HttpTransport("https://eval.internal", token="..."),
)

Logging and observability

The SDK ships its own structured logger. To wire it into your application's logger:

agent.py
import logging
logging.getLogger("faramesh").setLevel(logging.INFO)

It logs:

  • Decision id and effect for every call
  • Latency at the SDK boundary
  • Daemon connectivity changes
  • Transport fallbacks

The DPR itself is recorded by the daemon, not the SDK.

Agent governance metadata

Some frameworks let you attach metadata to a tool call (LangChain RunnableConfig, OpenAI Agents context). The SDK forwards a known set of keys to the daemon as condition variables:

KeyBecomes
principalprincipal.* claim set
session_idSession identifier for budget pooling
request_idCorrelation id surfaced in DPR
tagsFree-form tag list available in conditions as tags

Anything else is dropped at the SDK boundary so policy can't accidentally branch on uncontrolled fields.

Testing your wiring

agent.py
from faramesh.testing import StubTransport

tools = GovernedToolSet(
    [search_docs],
    agent_id="my-agent",
    transport=StubTransport({
        "search_docs": "permit",
    }),
)

# Now run your agent against the stub

StubTransport accepts a mapping from tool name to effect or a callable that takes the action payload and returns a decision. Use it in unit tests to assert your agent handles structured denials correctly.

What's next

On this page