LangSmith
Every Interven scan emits a LangSmith run/span โ see security decisions inline with your agent traces.
If your team uses LangSmith for LangChain / LangGraph observability, Interven plugs straight in. Every scan decision (ALLOW / DENY / SANITIZE / REQUIRE_APPROVAL) becomes a child run under the agent / chain / tool that triggered it. Your compliance reviewer or on-call engineer sees policy decisions in the same trace tree they already use โ no second dashboard.
Install
pip install 'interven-langchain[langsmith]'The [langsmith] extra adds the langsmith package as a dep. The integration
auto-activates whenever langsmith is importable AND your process has the
standard LangSmith env vars set:
export LANGSMITH_API_KEY=ls_...
export LANGSMITH_TRACING=true
export LANGSMITH_PROJECT=your-project # optionalIf those env vars aren't present, the integration silently no-ops โ no LangSmith calls happen. You're not forced into LangSmith just because you installed the extra.
What the spans look like
Each Interven scan appears as a tool run named interven.scan.<tool_name>,
nested under whatever LangChain run triggered it:
agent_executor (root)
โโโ llm_call
โโโ tool: send_slack_message
โ โโโ interven.scan.send_slack_message โ Interven decision lives here
โ โโโ decision: SANITIZE
โ risk_band: MED
โ reason_codes: [PII_EGRESS, SENSITIVE_DATA_EGRESS]
โ interven.console_url: https://app.intervensecurity.com/activity?trace_id=...
โโโ llm_callTags applied to every Interven run
These are searchable in LangSmith filters:
| Tag | Example |
|---|---|
interven.decision.<lower> | interven.decision.deny |
interven.risk.<lower> | interven.risk.high |
interven.runtime.<lower> | interven.runtime.langchain, interven.runtime.langgraph |
interven.reason.<lower> | interven.reason.body_pattern_match, interven.reason.external_principal (up to 5) |
Output shape
{
"decision": "REQUIRE_APPROVAL",
"risk_score": 0.71,
"risk_band": "HIGH",
"reason_codes": ["NEW_DESTINATION", "BODY_PATTERN_MATCH"],
"trace_id": "9f2c-...",
"approval_id": "a4b1-...",
"approval_url": "https://app.intervensecurity.com/approvals/a4b1-..."
}When the decision is DENY or REQUIRE_APPROVAL, the LangSmith run is also marked as errored so you can spot it instantly in the trace UI's red-bordered node count.
Bodies are NOT logged
We deliberately do not include the request body in the LangSmith inputs โ
bodies may carry PII or secrets, and LangSmith stores inputs verbatim. The
full payload lives in Interven's encrypted store and is reachable via the
interven.console_url metadata link from the LangSmith run. This keeps
LangSmith free of regulated content.
Using with LangGraph
The same emission happens whether you're using InterventCallback,
interven_tool_node, guard_state_graph, or scan_tool_call โ every code
path goes through the same emit helper.
from langgraph.prebuilt import create_react_agent
from interven_langchain import InterventCallback
agent = create_react_agent(model, tools=tools)
agent.invoke(
{"messages": [HumanMessage("...")]},
config={"callbacks": [InterventCallback(api_key="iv_live_...")]},
)
# LangSmith now shows interven.scan child runs under each tool call.Why this matters
If your audit team uses LangSmith to investigate an incident, they don't have to learn a second tool to see policy enforcement. Every "what did the agent try, what did the firewall do, why" question gets answered in the same trace they're already looking at. That's the difference between a security tool that ships data and one that integrates with how your team actually works.