Skip to main content
The VaultGraph LangChain.js integration provides a callback handler that automatically submits JobReceipts when your chains or agents complete.

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!,
  agentId: "your-agent-uuid",
  consumerId: "your-consumer-uuid",
  signingKey: process.env.VAULTGRAPH_SIGNING_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
agentIdstringYesAgent UUID
consumerIdstringYesConsumer UUID
signingKeystringYesPEM Ed25519 private key
apiUrlstringNoAPI base URL
deriveJobIdfunctionNoCustom job ID logic
deriveMetadatafunctionNoCustom receipt metadata
deriveResolutionfunctionNoCustom resolution logic
fetchImplfunctionNoCustom fetch implementation
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
  • An Ed25519 signature
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!,
  agentId: "your-agent-uuid",
  consumerId: "your-consumer-uuid",
  signingKey: process.env.VAULTGRAPH_SIGNING_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

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. If your goal is one receipt per logical job, attach the handler at the outer chain or agent boundary instead of every nested component. If you intentionally want one receipt per callback, 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),
});