Skip to main content
The VaultGraph LangChain.js integration provides a callback handler that automatically submits JobReceipts when your chains or agents complete. By default, the handler submits a receipt only for the top-level LangChain run triggered by your outer invoke() or agent execution. Nested prompt, parser, and intermediate runnable callbacks are skipped unless you opt into them with includeNestedRuns: true.

Installation

npm install @vaultgraph/sdk @langchain/core

Quick Start

import { VaultGraphCallbackHandler } from "@vaultgraph/sdk/langchain";

const handler = new VaultGraphCallbackHandler({
  apiKey: process.env.VAULTGRAPH_API_KEY!,
  deploymentId: process.env.VAULTGRAPH_DEPLOYMENT_ID!,
  privateKey: process.env.VAULTGRAPH_PRIVATE_KEY!,
  deriveJobId: (_output, context) => `lc-run-${context.runId}`,
  deriveMetadata: (output, context) => ({
    channel: "chat",
    workflow: "support-escalation",
    event: context.event,
    runId: context.runId,
    preview: String(output.text ?? "").slice(0, 48),
  }),
  deriveResolution: (output, context) => {
    const text = String(output.text ?? "").toLowerCase();
    if (context.event === "chain_error") return "failed";
    if (text.includes("error")) return "failed";
    return text.includes("refund") ? "partial" : "success";
  },
});

// Pass to any LangChain chain or agent
const result = await chain.invoke(
  { question: "What is VaultGraph?" },
  { callbacks: [handler] },
);

Configuration

OptionTypeRequiredDescription
apiKeystringYesVendor API key
deploymentIdstringYesDeployment short ID (dep_...)
privateKeystringYesPEM Ed25519 private key
apiUrlstringNoAPI base URL
deriveJobIdfunctionNoCustom job ID logic
deriveMetadatafunctionNoCustom receipt metadata
deriveResolutionfunctionNoCustom resolution logic
fetchImplfunctionNoCustom fetch implementation
onReceiptSignedfunctionNoSigned receipt callback with exact hashed payload
onErrorfunctionNoError callback

How It Works

The handler listens for these LangChain events:
  • handleChainEnd — defaults to "success" resolution
  • handleChainError — defaults to "failed" resolution
  • handleAgentEnd — defaults to "success" resolution
If you provide deriveResolution, it overrides those defaults. All derive hooks receive the LangChain output plus a context object with the callback event, default resolution, run IDs, tags, optional inputs, and error details when available. Each receipt includes:
  • A job ID for correlation across systems
  • Default metadata including framework source, callback event, and execution hints
  • A context hash derived from the chain output
  • The exact normalized payload and hash used for context_hash via onReceiptSigned
  • An Ed25519 signature
In the default configuration, that means one outer LangChain workflow produces one receipt even when LangChain creates nested child runs internally. By default, the integration generates a unique job ID prefixed with lc-. If you want to align receipts with your own internal jobs, provide deriveJobId.

Custom Resolution Logic

Override the default resolution inference:
const handler = new VaultGraphCallbackHandler({
  apiKey: process.env.VAULTGRAPH_API_KEY!,
  deploymentId: process.env.VAULTGRAPH_DEPLOYMENT_ID!,
  privateKey: process.env.VAULTGRAPH_PRIVATE_KEY!,
  deriveJobId: (_output, context) => `ticket-${context.runId}`,
  deriveResolution: (output, context) => {
    const text = String(output.text ?? "").toLowerCase();
    if (context.event === "chain_error") return "failed";
    if (text.includes("error")) return "failed";
    if (text.includes("retry")) return "partial";
    return "success";
  },
});
deriveResolution receives both the chain or agent output and a context object. On handleChainError, the handler does not have output payload data, so the default "failed" resolution is used unless your hook explicitly returns something else. Your hook should always return one of "success", "partial", or "failed". If you also need receipt correlation with your own systems, use deriveJobId to attach a vendor-controlled job identifier instead of the autogenerated lc-* value. If you need extra structured fields such as ticket IDs, workflow names, or trace references, use deriveMetadata. Derived metadata is merged on top of the default integration metadata. By default, LangChain receipts include:
  • source: "langchain"
  • event
  • runId and parentRunId when available
  • tags when present
  • boolean hints such as hasInputs, hasError, and hasAction
If you need to persist the exact payload that VaultGraph hashed for later verification, use onReceiptSigned:
const handler = new VaultGraphCallbackHandler({
  // ... config
  onReceiptSigned: ({ contextPayload, contextHash }) => {
    console.log("Persist alongside your logs", { contextPayload, contextHash });
  },
});

Hook Context

Each derive hook receives this context shape:
type VaultGraphLangChainReceiptContext = {
  event: "chain_end" | "chain_error" | "agent_end";
  defaultResolution: "success" | "partial" | "failed";
  output: ChainValues;
  error?: unknown;
  runId?: string;
  parentRunId?: string;
  tags?: string[];
  inputs?: Record<string, unknown>;
};
Use runId for vendor-side correlation, event for branching on callback type, and deriveMetadata for secondary identifiers that should travel with the receipt without replacing your primary job ID.

Idempotency and Duplicate Suppression

The autogenerated lc-* job IDs are fine for demos, but they are not idempotent across retries. In production, prefer deriveJobId with a stable identifier such as:
  • context.runId when it represents the same logical job across retries
  • a request or ticket ID passed through chain inputs
  • your own workflow execution ID from the application boundary
Keep retry counters and worker-specific details in deriveMetadata, not in the primary job_id. LangChain can emit multiple callbacks for one user-visible workflow. The VaultGraph handler submits only for the top-level LangChain run by default, so an outer invoke() produces one receipt unless you opt into nested callbacks. If you intentionally want one receipt per callback, set includeNestedRuns: true and encode that explicitly in deriveJobId, for example by including context.event.

Error Handling

The handler never crashes your agent. Submission errors are caught silently. Use the onError callback to log them:
const handler = new VaultGraphCallbackHandler({
  // ... config
  onError: (err) => console.error("VaultGraph:", err.message),
});