> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agnost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# LangChain

> Capture traces from LangChain / LangGraph via LangSmith's OTel mode

LangChain emits OTel through LangSmith. Register the OTel exporter pointing at Agnost before building chains or agents, enable LangSmith's OTel mode, then pass `session_id` / `user_id` per call.

<Note>
  LangChain/LangSmith traces can include prompts, completions, tool inputs, and tool outputs. Review [Data Governance](/data-governance) before enabling production traffic.
</Note>

## 1. Install

**Already have LangSmith and an OTLP exporter installed?** Skip.

**No setup yet?**

<CodeGroup>
  ```bash Python theme={null}
  pip install "langsmith[otel]" langchain \
              opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
  ```

  ```bash TypeScript theme={null}
  npm install langsmith @opentelemetry/api @opentelemetry/context-async-hooks \
              @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-proto
  ```
</CodeGroup>

## 2. Wire LangSmith OTel pointing at Agnost

The provider must be registered before importing or constructing LangChain objects.

**Already have an OTel TracerProvider?** Append Agnost as an additional span processor:

```python Python theme={null}
import os
from opentelemetry import trace
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(
            endpoint="https://otel.agnost.ai/v1/traces",
            headers={"X-Agnost-Org-ID": os.environ["AGNOST_ORG_ID"]},
        )
    )
)

os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_OTEL_ONLY"] = "true"
```

**No OTel yet?** Full setup:

<CodeGroup>
  ```python Python theme={null}
  import os
  from opentelemetry import trace
  from opentelemetry.sdk.trace import TracerProvider
  from opentelemetry.sdk.trace.export import BatchSpanProcessor
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

  provider = TracerProvider()
  provider.add_span_processor(
      BatchSpanProcessor(
          OTLPSpanExporter(
              endpoint="https://otel.agnost.ai/v1/traces",
              headers={"X-Agnost-Org-ID": os.environ["AGNOST_ORG_ID"]},
          )
      )
  )
  trace.set_tracer_provider(provider)

  os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
  os.environ["LANGSMITH_TRACING"] = "true"
  os.environ["LANGSMITH_OTEL_ONLY"] = "true"

  # Import LangChain AFTER the provider is registered.
  from langchain_openai import ChatOpenAI
  ```

  ```typescript TypeScript theme={null}
  import { trace, SpanStatusCode } from '@opentelemetry/api';

  process.env.LANGSMITH_TRACING = 'true';
  process.env.LANGCHAIN_TRACING_V2 = 'true';
  process.env.LANGSMITH_TRACING_MODE = 'otel';

  const { initializeOTEL } = await import('langsmith/experimental/otel/setup');

  initializeOTEL({
    exporterConfig: {
      url: 'https://otel.agnost.ai/v1/traces',
      headers: { 'X-Agnost-Org-ID': process.env.AGNOST_ORG_ID! },
    },
  });
  ```
</CodeGroup>

For TypeScript, `langsmith/experimental/vercel` is the Vercel AI SDK 7 adapter. LangChain JS/TS should use `langsmith/experimental/otel/setup`.

For LangChain JS/TS, keep one active OTel span around the actual invoke. The
LangSmith OTel translator attaches LangChain run data to that active span, and
the GenAI attributes below give Agnost exact Chat View input/output text.

## 3. Pass user\_id / session\_id per call

<CodeGroup>
  ```python Python theme={null}
  result = agent.invoke(
      {"messages": [{"role": "user", "content": "Hello"}]},
      config={
          "metadata": {
              "session_id": "conv-abc123",
              "user_id": "user-42",
          },
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const tracer = trace.getTracer('langchain-ts');
  const userMessage = 'Hello';

  const result = await tracer.startActiveSpan('langchain.invoke', {
    attributes: {
      'langsmith.traceable': 'true',
      'langsmith.metadata.session_id': 'conv-abc123',
      'langsmith.metadata.user_id': 'user-42',
      'gen_ai.operation.name': 'chat',
      'gen_ai.system': 'langchain',
      'gen_ai.input.messages': JSON.stringify([{ role: 'user', content: userMessage }]),
    },
  }, async (span) => {
    try {
      const result = await chain.invoke(
        { messages: [{ role: 'user', content: userMessage }] },
        {
          metadata: {
            session_id: 'conv-abc123',
            user_id: 'user-42',
          },
          tags: ['agnost', 'langchain-ts'],
        },
      );
      const answer = typeof result.content === 'string' ? result.content : JSON.stringify(result);
      span.setAttribute('gen_ai.output.messages', JSON.stringify([{ role: 'assistant', content: answer }]));
      span.setStatus({ code: SpanStatusCode.OK });
      return result;
    } catch (error) {
      span.recordException(error as Error);
      span.setStatus({ code: SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error) });
      throw error;
    } finally {
      span.end();
    }
  });
  ```
</CodeGroup>

LangChain prefixes metadata as `langsmith.metadata.*`: Agnost reads `langsmith.metadata.session_id` and `langsmith.metadata.user_id` natively for user / session grouping.

For Chat View, keep the assistant answer in the normal LangChain output:
message content, `generations`, or a chain result wrapper such as `answer`,
`output`, or `result`. Agnost stores the raw OTel payload, then collapses those
common output shapes to readable text for the dashboard.

## What appears in Agnost

* **Conversations** grouped by `session_id`.
* **User-level analytics** grouped by `user_id`.
* **Raw logs** for LangSmith OTel spans.

## Verify

Run one `agent.invoke`, then open **Raw logs** and confirm `langsmith.metadata.session_id` and `langsmith.metadata.user_id` exist.

## Troubleshooting

* Register the `TracerProvider` before importing LangChain.
* Set `LANGSMITH_OTEL_ENABLED=true` and `LANGSMITH_TRACING=true`.
* Set `LANGSMITH_OTEL_ONLY=true` if you do not want to also ship to LangSmith.

## References

* [Enable OpenTelemetry export](https://docs.langchain.com/langsmith/trace-with-opentelemetry)
* [Add custom metadata](https://docs.langchain.com/langsmith/add-metadata-tags)
