Skip to Content
IntegrationsLangChain

LangChain

The langchain-expunct package is drop-in PII redaction middleware for LangChain. Add one line to your chain and automatically strip sensitive data before it reaches any LLM.

Package: langchain-expunct on PyPI

Installation

pip install langchain-expunct

Get your API key at expunct.ai  — free tier includes 1M tokens/month.

Quick Start

Redact text in a chain

from langchain_openai import ChatOpenAI from langchain_expunct import ExpunctPIIRedactor redactor = ExpunctPIIRedactor(api_key="pk_live_...") llm = ChatOpenAI() chain = redactor | llm result = chain.invoke("Summarize this: John Smith (SSN 219-09-9999) applied for a loan.") # The LLM sees: "Summarize this: PERSON_1 (SSN US_SSN_1) applied for a loan."

Redact chat messages

from langchain_core.messages import HumanMessage, SystemMessage from langchain_openai import ChatOpenAI from langchain_expunct import ExpunctChatMessageRedactor redactor = ExpunctChatMessageRedactor(api_key="pk_live_...") chat = ChatOpenAI() chain = redactor | chat result = chain.invoke([ SystemMessage(content="You are a helpful assistant."), HumanMessage(content="My name is Alice Johnson and my email is alice@example.com"), ])

Async usage

import asyncio from langchain_openai import ChatOpenAI from langchain_expunct import ExpunctPIIRedactor redactor = ExpunctPIIRedactor(api_key="pk_live_...") llm = ChatOpenAI() chain = redactor | llm async def main(): result = await chain.ainvoke("Call Bob at 415-555-0100") print(result) asyncio.run(main())

Callback handler — auto-redact LLM inputs

from langchain_openai import ChatOpenAI from langchain_expunct import ExpunctCallbackHandler handler = ExpunctCallbackHandler(api_key="pk_live_...", redact_input=True, log_output=True) llm = ChatOpenAI() result = llm.invoke( "Tell me about John Smith, SSN 219-09-9999", config={"callbacks": [handler]}, )

For async workflows, use AsyncExpunctCallbackHandler:

from langchain_expunct import AsyncExpunctCallbackHandler handler = AsyncExpunctCallbackHandler(api_key="pk_live_...") result = await llm.ainvoke("...", config={"callbacks": [handler]})

Components

ExpunctPIIRedactor

A Runnable[str, str] that redacts PII from plain text. Composable with |.

ParameterTypeDefaultDescription
api_keystrrequiredYour Expunct API key
base_urlstrhttps://api.expunct.aiAPI base URL
policy_idstr | NoneNonePolicy to apply
languagestr"en"Language of input text

ExpunctChatMessageRedactor

A Runnable[list[BaseMessage], list[BaseMessage]] that redacts PII from messages while preserving roles, names, and metadata.

ParameterTypeDefaultDescription
api_keystrrequiredYour Expunct API key
base_urlstrhttps://api.expunct.aiAPI base URL
languagestr"en"Language of input text

ExpunctCallbackHandler / AsyncExpunctCallbackHandler

Hooks into on_llm_start and on_llm_end to redact inputs and log detected PII in outputs.

ParameterTypeDefaultDescription
api_keystrrequiredYour Expunct API key
base_urlstrhttps://api.expunct.aiAPI base URL
languagestr"en"Language of input text
redact_inputboolTrueRedact PII from prompts on LLM start
log_outputboolTrueLog detected PII in LLM responses

RAG Pipeline Example

from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_community.vectorstores import FAISS from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_expunct import ExpunctPIIRedactor vectorstore = FAISS.from_texts(["..."], embedding=OpenAIEmbeddings()) retriever = vectorstore.as_retriever() redactor = ExpunctPIIRedactor(api_key="pk_live_...") prompt = ChatPromptTemplate.from_template( "Answer based on context: {context}\n\nQuestion: {question}" ) llm = ChatOpenAI() rag_chain = ( {"context": retriever, "question": redactor} | prompt | llm | StrOutputParser() ) result = rag_chain.invoke("What did John Smith order? His email is john@acme.com")