Docs / Integrations / LangChain
LangChain
Mesedi ships as a LangChain.js callback handler. Wrap your entry function with mesedi.wrap() and attach a MesediLangChainCallbackHandlerinstance to LangChain's existing callbacks: slot. Every LLM invocation and tool call inside the wrap flows into Mesedi as an llm_call or tool_call event.
Install
npm install mesedi@^0.7 @langchain/coreMesedi's adapter treats @langchain/core as an optional peer dependency. If you never import mesedi/integrations/langchain, the library is not loaded and there is no install cost for non-LangChain customers. Peer range is >=0.3.0 <0.5.0.
Wire it up
import { configure, wrap } from "mesedi";
import { MesediLangChainCallbackHandler } from "mesedi/integrations/langchain";
import { ChatOpenAI } from "@langchain/openai";
// Configure Mesedi once (usually at app startup).
configure({ apiKey: process.env.MESEDI_API_KEY });
export const runAgent = wrap(
{ agentName: "support-triage" },
async (question: string) => {
const llm = new ChatOpenAI({ model: "gpt-4o" });
const result = await llm.invoke(
[{ role: "user", content: question }],
{ callbacks: [new MesediLangChainCallbackHandler()] },
);
return String(result.content);
},
);wrap() owns the execution boundary (started / completed / crash signature). The callback handler owns the intra-execution events. Cross-cutting Mesedi features (per-project detector thresholds, custom security patterns, hard-halt budgets) all keep working through LangChain runs without any additional wiring.
Where to attach the handler
The callbacks:option exists on every LangChain runnable's invoke(), stream(), and batch() methods, and on the RunnableConfig passed to agent executors. Attach one MesediLangChainCallbackHandler per top-level call; LangChain propagates it to every nested runnable automatically.
Callback-to-event mapping
LangChain fires typed callbacks for every LLM invocation and tool call. The handler translates each pair (start + end, or start + error) into the corresponding Mesedi event on the end/error callback.
| LangChain callback | Mesedi event | Notes |
|---|---|---|
handleLLMStart + handleLLMEnd | llm_call (status ok) | Plain (non-chat) LLM. Model, prompt, response, tokens. |
handleChatModelStart + handleLLMEnd | llm_call (status ok) | Chat model. Extracts last user + system message from the conversation. |
handleLLMError | llm_call (status failed) | Response text and token counts are omitted on the failure path. |
handleToolStart + handleToolEnd | tool_call (status ok) | Tool name, input, output. Payload shape matches @mesedi.tool. |
handleToolError | tool_call (status failed) | Carries exception_type + exception_message. |
Detector coverage
All 20 Mesedi failure-class detectors fire when driven through this adapter. Ten are native: the callback-to-event mapping above feeds them directly, as long as you attach MesediLangChainCallbackHandler at the callbacks: slot on the runnable. Ten are hybrid: you call an SDK emit helper inside your wrap() and the detector picks up the shape it needs. The LangChain integration harness exercises all 20 end-to-end against a real backend.
| Detector | Mode | Fires via |
|---|---|---|
loops | Native | handleLLMStart + handleToolStart callbacks (identical_call, similar_call, step_count) |
tool_failures | Native | handleToolError callback |
drift | Native | handleLLMStart user_message divergence |
cost_velocity | Native | handleLLMEnd token counts + provider pricing |
prompt_injection | Native | handleLLMStart user_message pattern match |
data_leakage | Native | handleLLMStart user_message DLP match |
context_overflow | Native | handleLLMStart prompt-size vs model window |
token_waste | Native | handleLLMStart shared prefix across 3+ user_messages |
tool_schema_drift | Native | handleToolEnd structured return_value fingerprint (attach callbacks on tool.invoke()) |
sandbox_escape | Native | handleToolStart arguments match the dangerous-call regex |
crashes | Hybrid | throw inside wrap() — same pattern every SDK language uses |
semantic_loop | Hybrid | Call checkpoint(name, metadata) at each phase — LangChain has no chain-boundary equivalent |
validator_failures | Hybrid | Call validatorResult() after your schema check |
infrastructure_throttled | Hybrid | Call emitInfrastructureEvent() when you observe a rate-limit / retry |
grounding_failure | Hybrid | Call emitEvalScore() with your RAG / faithfulness score |
cascading_failure | Hybrid | Call emitAgentHandoff() to a child execution |
coordination_deadlock | Hybrid | Call emitAgentHandoff()— Tarjan's SCC catches the cycle |
provider_incident | Hybrid | Emit a failed llm_call event with error_class="timeout" / "rate_limited" when your provider client throws |
hitl_timeout | Hybrid | requestHumanIntervention() + completeHumanIntervention() with timeout kind |
hitl_rejection_spike | Hybrid | Repeated validatorResult() with human_verdict=rejected over a window |
Hybrid rows use exactly the same SDK helpers you'd call from any Mesedi integration (or from plain wrap()-only code). The callback handler and the helpers coexist in one execution — no double-counting, no setup order dependency.
Halt-safe boundaries
Every emitted llm_call and tool_call is a halt-safe boundary. If you pass a budget to wrap(), the handler checks it at each callback, increments the step counter, and on LLM calls adds any observed input/output tokens toward the per-execution cap. Behavior matches @mesedi.tool and the Anthropic patch.
Current limitations
- Streaming (
handleLLMNewToken) is not currently consumed. Mesedi receives onellm_callevent with the assembled final response onhandleLLMEnd. Per-token attribution needs an event-payload schema change and lands in a later release. - Per-chain depth tracking is not exposed.
handleChainStartandhandleChainEndfire for every nested runnable but the handler ignores them;wrap()already owns the execution boundary. A chain-as-execution mode (nowrap()required) is a later iteration. - Multi-modal content blocks in chat messages are consumed for their text parts only. Image / audio blocks are ignored on the input side; the wire format doesn't yet carry them.
- Outside a wrap, all callbacks silently no-op. Attach the handler inside a
wrap()-decorated function for events to reach Mesedi.
Related
- TypeScript SDK reference —
configure(),wrap(),tool(), and the other framework adapters. - Mastra integration — sibling adapter for the Mastra framework.
- LangChain.js callbacks docs — how the
callbacks:option propagates through LangChain runnables.