PUNKthe adaptive runtime

//DOCS LangChain

Use Punk with LangChain.js ChatOpenAI through the OpenAI-compatible gateway.

LangChain Integration

Punk works with LangChain.js through ChatOpenAI. Use the LangChain config helper to point LangChain at the Punk gateway and keep the rest of your chains, agents, and LCEL pipelines unchanged.

Use this path when an app already uses @langchain/openai and you want gateway-routed agent traffic to pass through Punk for trace evidence, route explanations, exact caching, governance, and learning.

Install

npm install @langchain/openai @langchain/core @punktechnologies/sdk

Create a Punk tenant API key in the hosted dashboard:

export PUNK_BASE_URL=https://app.punktechnologies.com
export PUNK_API_KEY=pk_...

Provider keys and BYOK credentials live in Punk's hosted control plane, not in this app.

ChatOpenAI Setup

import { ChatOpenAI } from "@langchain/openai";
import { createPunkLangChainConfig } from "@punktechnologies/sdk/langchain";

const model = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
  ...createPunkLangChainConfig({
    app: "langchain-app",
    agent: "research-agent",
    subject: "user-123",
    onRun: ({ runId, route }) => {
      console.log({ runId, route });
    }
  })
});

const reply = await model.invoke([
  ["system", "Classify the ticket: billing, bug, or how-to. Answer with one word."],
  ["human", "How do I add a teammate to my workspace?"]
]);

console.log(reply.content);

LangChain sends the same OpenAI-compatible chat request through Punk. The gateway records a run and returns a provider-compatible response. The helper can read the PUNK_* environment defaults when you do not pass explicit options.

Correlate And Inspect Runs

LangChain does not add raw provider response headers to its message metadata. The optional onRun hook reads Punk's bounded run and route headers at the transport boundary for buffered and streamed calls. It fires once per HTTP response that reached Punk and does not issue another API request:

import { Punk, type PunkLangChainRunEvidence } from "@punktechnologies/sdk";

const punk = new Punk({ app: "langchain-app" });
const runs: PunkLangChainRunEvidence[] = [];
const model = new ChatOpenAI({
  model: "gpt-4o-mini",
  ...punk.langChainConfig({
    onRun: (run) => runs.push(run)
  })
});

await model.invoke("Classify this ticket.");

const exactRun = runs.at(-1);
if (exactRun) {
  console.dir(await punk.explain(exactRun.runId), { depth: null });
}

onRun receives { runId, route?, status }. It also observes governed non-success responses before LangChain applies its retry behavior. Observer errors are contained so evidence collection cannot replace a model result. Pass fetch beside onRun when the app needs a custom transport; the helper wraps that transport instead of globalThis.fetch.

Identity headers matter. X-Punk-App groups runs into app-level patterns. X-Punk-Agent names the agent. X-Punk-Subject scopes cache safety per pseudonymous end user.

Tool Tracing

Gateway traffic shows model calls, but LangChain tools execute in your process. Wrap important tools with the Punk SDK when you need tool-level evidence:

import { Punk } from "@punktechnologies/sdk";

const punk = new Punk({ app: "langchain-app", agent: "research-agent", subject: "user-123" });

const lookupAccount = punk.traceTool({
  name: "crm.lookupAccount",
  sideEffectLevel: 1,
  ttlSeconds: 300,
  execute: async (args: { accountId: string }) => crm.get(args.accountId)
});

Use the onRun hook to retain the latest run id in your invocation-local context, then pass it through punk.withRun(runId, async () => ...) around local tool execution. Explicit { runId } still works. Without a run id or active run context, the tool still executes, but tracing cannot attach it to the model run.

Read next: OpenAI-Compatible AI Gateway, SDK, Agent Observability & Tool Caching.