Skip to main content

Drop Stryda governance into a LangChain agent — wrap a BaseTool, or attach a callback to an AgentExecutor.

Project description

stryda-langchain

Drop Stryda governance into a LangChain agent with ~3 lines of wiring.

This package is the LangChain-native surface on top of stryda-sdk. It ships two adapters — a tool wrapper and a callback handler — so you can pick whichever matches your existing agent structure. Both paths funnel every tool call through Stryda's stryda.check_action → execute → stryda.record_outcome pipeline.

Install

Not published to PyPI yet. Install from this monorepo:

pip install -e ./packages/stryda-sdk-python
pip install -e ./packages/stryda-langchain

Requires Python 3.10+ and langchain-core >= 0.3.

Option A — StrydaToolWrapper (recommended)

Wrap any BaseTool. The LLM sees the same tool (same name, description, args_schema), but every invocation goes through Stryda first. On deny/escalate the agent sees a readable policy reason as the tool output and can course-correct.

from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain_core.tools import tool
from stryda_sdk import StrydaClient
from stryda_langchain import StrydaToolWrapper

stryda = StrydaClient(api_key=os.environ["STRYDA_API_KEY"])

@tool
def refund_charge(charge_id: str, amount_cents: int) -> dict:
    """Refund a Stripe charge."""
    return stripe.Refund.create(charge=charge_id, amount=amount_cents)

# One-liner — the rest of your agent setup is unchanged.
governed_refund = StrydaToolWrapper(
    tool=refund_charge,
    action_type="payments.refund",
    stryda_client=stryda,
)

executor = AgentExecutor(
    agent=agent,
    tools=[governed_refund, other_tools...],
)

What happens on each invocation:

Stryda decision Wrapped tool runs? Agent sees
allow Yes Real tool result; record_outcome=success
deny No "[stryda:denied] refund_charge was not run. Reason: …"
escalate No "[stryda:pending_approval] … (escalation_id=esc_xyz)"
Stryda 5xx No StrydaError raised — fail closed by design
Tool raises record_outcome=error, exception re-raised

Option B — StrydaCallback

If you can't (or don't want to) rewrite your tool objects, attach the callback at the AgentExecutor level. It governs every tool call in the agent loop.

from stryda_langchain import StrydaCallback

executor = AgentExecutor(
    agent=agent,
    tools=tools,                        # unchanged — still BYO-key
    callbacks=[StrydaCallback(
        stryda_client=stryda,
        action_type_map={
            "refund_charge":  "payments.refund",
            "send_email":     "comms.email_send",
            "create_ticket":  "crm.ticket_create",
        },
    )],
)

The callback has raise_error = True, so a DeniedError raised from on_tool_start propagates through LangChain's callback manager and the tool is never executed. Unmapped tool names fall back to the tool's lowercased name coerced into Stryda's action_type regex.

Why only one class? Earlier versions of this package shipped a separate AsyncStrydaCallback subclassing AsyncCallbackHandler. It had a silent correctness bug — when an agent used ainvoke on a sync tool, LangChain's dispatcher sometimes routed async handlers through _run_coros, which swallows exceptions unconditionally. A denied decision would then log-and-allow. The single sync-methods class avoids that path entirely. The import AsyncStrydaCallback is still exported as an alias of StrydaCallback for back-compat.

Offline attestation verification

Every authorized tool call produces a signed Ed25519 JWT. To verify stored attestations without hitting Stryda, use stryda-sdk's verifier:

from stryda_sdk import fetch_jwks, verify_attestation

jwks = fetch_jwks("https://api.stryda.ai")   # cache per-process
claims = verify_attestation(jwt_token, jwks, audience="mcp-governance")

BYO-key stays BYO-key

Stryda never holds your Stripe / Gmail / Salesforce credentials. StrydaToolWrapper calls your original tool's _run with the original args; StrydaCallback lets LangChain execute your tool as it normally would. Stryda only authorizes and records. If your Stryda endpoint is unreachable, the wrapper fails closed (StrydaError) — it will never silently bypass governance.

Troubleshooting

Callback raised DeniedError but LangChain warned and ran the tool anyway. You probably attached a handler that subclasses AsyncCallbackHandler with async def methods on a sync tool. Use StrydaCallback — it works in both sync and async paths.

Tool ran but no attestation in Stryda's ledger. Check the check_id in your logs. record_outcome is best-effort (the action already happened); transient Stryda 5xx errors on record do NOT roll back the tool call. Your ledger-verify endpoint (GET /api/ledger/verify) will surface the gap if it drifted.

Wrapper is blocking LLM tool calling. Make sure you passed tool= (not tool_cls=) a concrete instance of BaseTool, not a class. LangChain introspects args_schema on the instance.

Related

  • stryda-sdk-python — the underlying HTTP client + governed context manager
  • stryda-langgraph — policy node for LangGraph state machines
  • Mission + architecture: MISSION.md, docs/system-architecture.md

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stryda_langchain-0.1.0.tar.gz (10.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stryda_langchain-0.1.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file stryda_langchain-0.1.0.tar.gz.

File metadata

  • Download URL: stryda_langchain-0.1.0.tar.gz
  • Upload date:
  • Size: 10.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for stryda_langchain-0.1.0.tar.gz
Algorithm Hash digest
SHA256 494b18b11d9695a04cdb19861e2112c785478af5246d9343b3b5e9ba628d1613
MD5 6b4b12c5876b34fa3d80604f61b2d7b5
BLAKE2b-256 f6e954bcdb51745d9b221430f12c4a36fd21fad3051d1e2f6431a693b3bba755

See more details on using hashes here.

File details

Details for the file stryda_langchain-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for stryda_langchain-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 17263da219d344c9adef56582aba7ab4ba9665b06ea1a77cfcf38b222b980503
MD5 1c276eaa3b5bb6dce10b4db499b12621
BLAKE2b-256 5314a03b2569bd8b4dbc1ef851f000ed2a2e385139efab2fc231719b90c5893e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page