> ## 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

## Language support

| Setup option                    | Python | TypeScript |
| ------------------------------- | :----: | :--------: |
| Agnost AI skill                 |    ✓   |      ✓     |
| Agnost AI SDK                   |    ✓   |      ✓     |
| OpenTelemetry through LangSmith |    ✓   |      ✓     |

## Choose a setup method

| Method              | Use it when                                                                    |
| ------------------- | ------------------------------------------------------------------------------ |
| **Agnost AI skill** | You want your coding agent to inspect the app, make the change, and verify it. |
| **Agnost AI SDK**   | You want explicit control over the interaction boundaries and fields.          |
| **OpenTelemetry**   | The framework already emits useful spans or you operate an OTel pipeline.      |

Start with one method for a call path. Combining SDK tracking with framework
OpenTelemetry on the same call can create duplicate interactions.

## Recommended: Agnost AI skill

<Note>
  **Recommended.** Choose this path when you want your coding agent to inspect the
  project, select a supported transport, make the smallest instrumentation change,
  send a test event, and verify that it reached Agnost AI.
</Note>

Install the skill once in your coding-agent environment:

```bash theme={null}
npx skills add AgnostAI/skills --skill agnost-ai
```

Run the framework-specific prompt below from the application root. Review the
resulting diff before deploying it.

```text theme={null}
Use the agnost-ai skill to add Agnost AI analytics to this LangChain or LangGraph application.
Org ID: your-org-id
Instrument the real chain, graph, or agent invocation and verify one fresh interaction.
```

## Manual setup: Agnost AI SDK

<CodeGroup>
  ```bash Python theme={null}
  pip install agnost
  ```

  ```bash TypeScript theme={null}
  npm install agnostai
  ```
</CodeGroup>

<CodeGroup>
  ```python Python theme={null}
  import agnost

  agnost.init("your-org-id")
  interaction = agnost.begin(user_id="u-42", agent_name="langchain-support", input=prompt)
  try:
      result = chain.invoke({"input": prompt})
      interaction.end(output=str(result))
  except Exception as exc:
      interaction.end(output=str(exc), success=False)
      raise
  finally:
      agnost.shutdown()
  ```

  ```typescript TypeScript theme={null}
  import * as agnost from 'agnostai';

  agnost.init('your-org-id');
  const interaction = agnost.begin({ userId: 'u-42', agentName: 'langchain-support', input: prompt });
  try {
    const result = await chain.invoke({ input: prompt });
    interaction.end(JSON.stringify(result));
  } catch (error) {
    interaction.end(String(error), false);
    throw error;
  } finally {
    await agnost.shutdown();
  }
  ```
</CodeGroup>

## Manual setup: OpenTelemetry

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

### 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 AI

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

**Already have an OTel TracerProvider?** Append Agnost AI 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 AI 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 AI 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 AI stores the raw OTel payload, then collapses those
common output shapes to readable text for the dashboard.

### What appears in Agnost AI

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

### Verify

Run one `agent.invoke`, then open **Events** 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)

## Next steps

* [Conversations](/using-conversations): review the complete chain or graph execution.
* [Events](/using-events): inspect LangChain and LangGraph spans.
* [Intents](/using-intents): organize production conversations by what users wanted.
