npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@temporalio/langsmith

v1.20.3

Published

Temporal LangSmith observability integration package

Readme

@temporalio/langsmith — LangSmith tracing for Temporal

This plugin makes LangSmith observability work inside Temporal Workflows and Activities without changing your existing instrumentation. Code you already trace with LangSmith's native traceable keeps working when you move it into a Workflow or Activity body — you only add the plugin to your Client and Worker.

This plugin is built on Temporal's Plugin API, which is experimental; its APIs may change in a future release.

It handles the parts that are otherwise hard:

  • Replay safety. Workflows replay history; a naive tracer re-emits every run on every replay and floods your project with duplicates. This plugin emits runs with deterministic IDs out-of-isolate via a Temporal Sink that does not fire during replay.
  • Run parenting across boundaries. A trace started on the client threads through workflow → activity → child-workflow → Nexus so the runs nest the way you expect, instead of fragmenting into disconnected roots.

Install

npm install @temporalio/langsmith langsmith

langsmith is a peer dependency — install the version your project already uses.

Enable tracing

Tracing is off by default, matching the langsmith library: the plugin emits nothing unless LangSmith tracing is enabled in the worker/client process environment. Enable it with LangSmith's standard environment variables:

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="<your key>"

The plugin reads the same flags LangSmith itself uses (LANGSMITH_TRACING / LANGSMITH_TRACING_V2 and their LANGCHAIN_ aliases); with none set to true, tracing stays off and the plugin is a no-op.

Hello world

Your existing LangSmith instrumentation does not change. Here a traceable runs inside an Activity:

// activities.ts
import { traceable } from 'langsmith/traceable';

const callModel = traceable(
  async (prompt: string) => {
    // ... your real model call ...
    return `answer to: ${prompt}`;
  },
  { name: 'inner_llm_call' }
);

export async function answer(prompt: string): Promise<string> {
  return callModel(prompt);
}
// workflows.ts
import { proxyActivities } from '@temporalio/workflow';
import type * as activities from './activities';

const { answer } = proxyActivities<typeof activities>({
  startToCloseTimeout: '1 minute',
});

export async function GreetingWorkflow(prompt: string): Promise<string> {
  return answer(prompt);
}

The only new code is the plugin registration on the Client and the Worker:

// worker.ts
import { Worker } from '@temporalio/worker';
import { Client } from '@temporalio/client';
import { Client as LangSmithClient } from 'langsmith';
import { LangSmithPlugin } from '@temporalio/langsmith';
import * as activities from './activities';

const langsmith = new LangSmithClient(); // reads LANGSMITH_API_KEY from the env

// In TypeScript the Client and Worker are configured independently, so add the
// plugin to each. (Construct one plugin instance and share it.)
const plugin = new LangSmithPlugin({ client: langsmith, addTemporalRuns: true });

const client = new Client({ plugins: [plugin] });

const worker = await Worker.create({
  taskQueue: 'greeting',
  workflowsPath: require.resolve('./workflows'),
  activities,
  plugins: [plugin],
});

await worker.run();
// starter.ts — start the workflow from inside your own trace
import { traceable } from 'langsmith/traceable';

const pipeline = traceable(
  async () => {
    return client.workflow.execute(GreetingWorkflow, {
      taskQueue: 'greeting',
      workflowId: 'greeting-1',
      args: ['hello'],
    });
  },
  { name: 'user_pipeline' }
);

await pipeline();

With addTemporalRuns: true, the resulting trace nests like this:

user_pipeline
  StartWorkflow:GreetingWorkflow
  RunWorkflow:GreetingWorkflow
    StartActivity:answer
    RunActivity:answer
      inner_llm_call

Set addTemporalRuns: false (the default) to emit only your own traceable runs — the trace context still propagates across boundaries, so they nest correctly, but no StartWorkflow: / RunActivity: scaffolding is added:

user_pipeline
  inner_llm_call

Options

| Option | Default | Meaning | | ----------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | client | new Client() | The LangSmith client runs are emitted to. Lives only in the worker/client process — it is never serialized or sent across a Temporal boundary. | | addTemporalRuns | false | Emit first-class runs for Temporal operations (StartWorkflow:, RunActivity:, HandleSignal:, …) in addition to your traceable runs. | | projectName | LangSmith default | Target LangSmith project for emitted runs. | | tags | — | Tags attached to every run the plugin emits. | | metadata | — | Metadata merged into every run the plugin emits. Credential-looking keys are scrubbed before emission. |

The LangSmith API key is never accepted as a plugin option and never crosses a Temporal boundary. Supply a pre-constructed Client (which reads LANGSMITH_API_KEY from the process environment) or let the plugin build a default client from the environment.

Where traceable works

traceable from langsmith/traceable works unchanged in every position below. Each row is exercised by the plugin's test suite.

| Position | Works? | Notes | | ------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Client side, before client.workflow.start(...) | ✅ | The trace propagates into the workflow; RunWorkflow: nests under your run. | | Inside an Activity body | ✅ | Runs in the real worker process. Nests under RunActivity: (or under the propagated parent when addTemporalRuns: false). | | Inside a Workflow body | ✅ | Replay-safe: deterministic IDs, emitted out-of-isolate, suppressed on replay. Works whether or not a parent trace is propagated in. | | Inside signal / query / update handlers | ✅ | Handler-body traceable runs nest under the handler's run (workflow-body semantics). Temporal-internal queries (__temporal*, __stack_trace) are never traced. |

Concurrency caveat (workflow body only). Inside a Workflow, parent resolution uses a synchronous context stack rather than node:async_hooks (which is unavailable in the workflow isolate). Sequential await inner(...) nesting is exact. Under Promise.all(...) fan-out, or for traceable calls made after an await in the same scope, parenting falls back to the workflow run. This affects only the visual shape of the trace, never workflow history or control flow. Activity-body and client-side traceable use LangSmith's real async context and are unaffected.

Process-wide effects to be aware of

  • Workflow context provider. Loading the plugin's workflow interceptor module installs a global LangSmith async-context provider inside the workflow isolate (via AsyncLocalStorageProviderSingleton.initializeGlobalInstance). This is what lets unchanged traceable calls find their parent inside a Workflow. It is scoped to the workflow bundle and does not affect your client/worker process context.

Composing with other plugins

Register observability first (outermost) so it observes everything beneath it, then governance, then agent-framework plugins:

const worker = await Worker.create({
  taskQueue: 'tq',
  workflowsPath: require.resolve('./workflows'),
  activities,
  plugins: [
    new LangSmithPlugin({ client: langsmith }), // observability — first
    // new GovernancePlugin(...),
    // new AgentFrameworkPlugin(...),
  ],
});

The plugin is safe to register on both the Client and the Worker; it de-duplicates its own interceptors and sinks, so a worker built from a plugin-configured client will not double-instrument.

License

MIT