Skip to content

Add LangSmith tracing plugin for Temporal workflows#1369

Draft
xumaple wants to merge 9 commits intomainfrom
maplexu/langsmith-plugin
Draft

Add LangSmith tracing plugin for Temporal workflows#1369
xumaple wants to merge 9 commits intomainfrom
maplexu/langsmith-plugin

Conversation

@xumaple
Copy link

@xumaple xumaple commented Mar 17, 2026

Summary

  • Adds temporalio.contrib.langsmith plugin that creates LangSmith trace hierarchies for Temporal operations (workflows, activities, signals, queries, updates, child workflows, Nexus)
  • Supports ambient @traceable context propagation through Temporal headers, replay-safe tracing, and an add_temporal_runs toggle for lightweight context-only mode
  • 48 tests covering unit, integration, and comprehensive end-to-end scenarios

Test plan

  • uv run pytest tests/contrib/langsmith/ -v — 48/48 pass
  • uv run pyright temporalio/contrib/langsmith/ — 0 errors
  • uv run ruff check temporalio/contrib/langsmith/ tests/contrib/langsmith/ — all checks passed
  • Review still in progress — additional changes may follow

🤖 Generated with Claude Code

@CLAassistant
Copy link

CLAassistant commented Mar 17, 2026

CLA assistant check
All committers have signed the CLA.

xumaple and others added 4 commits March 16, 2026 21:44
Implements a LangSmith contrib plugin that creates trace hierarchies
for Temporal operations (workflows, activities, signals, queries,
updates, child workflows, Nexus). Supports ambient @Traceable context
propagation, replay-safe tracing, and an add_temporal_runs toggle for
lightweight context-only mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…late

- Add ReplaySafeRunTree wrapper that handles replay skipping and sandbox
  safety (post/end/patch no-op during replay, sandbox_unrestricted in
  workflow context), inspired by OTel plugin's _ReplaySafeSpan pattern
- Add config.maybe_run() to eliminate repeated config kwargs at every
  call site
- Add _traced_call (client outbound) and _traced_outbound (workflow
  outbound) helpers to reduce interceptor methods to one-liners
- Fold _extract_context into _workflow_maybe_run for workflow inbound
- Remove _safe_post, _safe_patch helpers (internalized in wrapper)
- Remove in_workflow parameter from _maybe_run (wrapper detects it)
- Establish consistent wrapping invariant: all run references are
  ReplaySafeRunTree, unwrapping is unconditional ._run at RunTree
  constructor boundary
- Parametrize redundant unit tests (client outbound, workflow
  inbound/outbound) and remove duplicate test
- Remove _make_interceptor test helper, use LangSmithInterceptor directly
- Collapse plugin constructor tests into one, add comprehensive plugin
  integration test, remove redundant sandbox tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix ruff I001 import sorting violations in _interceptor.py and
test_integration.py. Extract _get_current_run_safe() helper for
reading ambient LangSmith context with replay safety.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@xumaple xumaple force-pushed the maplexu/langsmith-plugin branch from b6a7751 to 80d981e Compare March 17, 2026 01:44
xumaple and others added 5 commits March 17, 2026 11:09
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@xumaple xumaple force-pushed the maplexu/langsmith-plugin branch from 2803b95 to 768ac70 Compare March 17, 2026 17:22
def __init__(
self,
*,
client: Any | None = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be any

*,
client: Any | None = None,
project_name: str | None = None,
add_temporal_runs: bool = True,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer default to false

return runner

super().__init__(
"LangSmithPlugin",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're adopting organization.PluginName

# Constants
# ---------------------------------------------------------------------------

HEADER_KEY = "_langsmith-context"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefix with _temporal or something, check what others use


def _inject_context(
headers: Mapping[str, Payload],
run_tree: Any,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Any?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants