OGuardAI
Integrations

LangChain

Protect LangChain chains with OGuardAI so PII is tokenized before the model and restored in the output

The OGuardAI SDKs ship an optional LangChain integration that wraps any LangChain Runnable so PII in the input is tokenized before the chain runs and restored in the chain output afterward. The wrapped runnable and the model only ever see the tokenized safe_text; the sealed session never enters the chain, its callbacks, or its memory. Each integration is a thin seam over the SDK guarded_call / guardedCall primitive.

Install

The integration is an optional extra, so LangChain is only pulled in when you use it.

Python

pip install "oguardai-sdk[langchain]"

TypeScript

pnpm add @oguardai/sdk @langchain/core

Two entry points

Both give the same core guarantee: the wrapped runnable and the model see only tokenized text. They differ in how they interact with LangChain callbacks and tracers.

  • guarded_invoke / guardedInvoke runs the transform and rehydrate outside LangChain. No LangChain callback or tracer ever observes the raw input or the restored output. Prefer this when a PII-exporting tracer (for example LangSmith) is attached to your chain.
  • protect / protectRunnable returns a composable Runnable you can drop into a larger chain. Its own input and output boundary (raw in, restored out) is visible to callbacks and tracers attached at or above that node, so use it when the outer chain is not exported to a PII-sensitive destination.

Python

from guardai_sdk import OGuardAIClient
from guardai_sdk.integrations.langchain import protect, guarded_invoke

client = OGuardAIClient(base_url="http://localhost:3000")

# Boundary-safe: nothing raw or restored reaches a LangChain callback or tracer.
answer = guarded_invoke(chain, client, "Email Julia Schneider at julia@firma.example")

# Composable Runnable (see the tracer note above).
guarded = protect(chain, client)
answer = guarded.invoke("Email Julia Schneider at julia@firma.example")

TypeScript

import { OGuardAIClient } from "@oguardai/sdk";
import { protectRunnable, guardedInvoke } from "@oguardai/sdk/integrations/langchain";

const client = new OGuardAIClient({ baseUrl: "http://localhost:3000" });

// Boundary-safe: nothing raw or restored reaches a LangChain callback or tracer.
const answer = await guardedInvoke(chain, client, "Email Julia Schneider at julia@firma.example");

// Composable Runnable (see the tracer note above).
const guarded = protectRunnable(chain, client);
const composed = await guarded.invoke("Email Julia Schneider at julia@firma.example");

In both cases the chain sees only Email {{person:p_001:ad4f97591c16}} at {{email:e_001:1bcaef1a4aff}}, and the returned answer has the real values restored.

Structured input

String input is safe by construction: only the tokenized text reaches the chain. For a structured input (a dict of prompt variables, or a message object), pass an extractor to pull the single text to protect and a builder to rebuild the chain input. Structured input without an extractor is rejected (fail loud) so a raw value can never be sent by accident.

Python

guarded = protect(
    chain,
    client,
    input_extractor=lambda payload: payload["question"],
    safe_input_builder=lambda payload, safe: {"question": safe},
    output_key="answer",
)

TypeScript

const guarded = protectRunnable(chain, client, {
  inputExtractor: (payload) => payload.question,
  safeInputBuilder: (_payload, safe) => ({ question: safe }),
  outputKey: "answer",
});

The builder receives the raw original input, so it is your responsibility to pass back only the fields your chain needs and never a raw PII field. When several fields need protecting, use the structured transform API directly instead of this single-text integration.

How it works

  1. The integration calls /v1/transform on the input text, replacing PII with semantic tokens.
  2. The wrapped runnable runs on the tokenized safe_text and returns its output.
  3. The integration calls /v1/rehydrate on that output using the sealed session, restoring the real values per the output channel.

If the transform fails, the wrapped runnable is never invoked (fail closed).