Metadata-Version: 2.5
Name: opentelemetry-instrumentation-genai-langchain
Version: 1.2b0
Summary: OpenTelemetry Official Langchain instrumentation
Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-genai/tree/main/instrumentation/opentelemetry-instrumentation-genai-langchain
Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python-genai
Author-email: OpenTelemetry Authors <cncf-opentelemetry-contributors@lists.cncf.io>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.10
Requires-Dist: opentelemetry-instrumentation<1,>=0.64b0
Requires-Dist: opentelemetry-util-genai<2,>=1.2b0
Provides-Extra: instruments
Requires-Dist: langchain<2,>=0.3.21; extra == 'instruments'
Description-Content-Type: text/x-rst

OpenTelemetry LangChain Instrumentation
=======================================

|pypi|

.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-genai-langchain.svg
   :target: https://pypi.org/project/opentelemetry-instrumentation-genai-langchain/

This library traces `LangChain <https://pypi.org/project/langchain/>`_ and
`LangGraph`_ applications. It hooks into
LangChain's callback manager to emit spans that mirror the structure of your
application:

* **Workflow spans** for a graph or chain run — for example an invocation of a
  LangGraph ``StateGraph`` — capturing the overall input and output of the run.
* **Agent spans** for agent invocations nested inside a workflow, including the
  agent name, id, description, and conversation/session id when available.
* **Tool spans** for tool calls made during a run.
* **Retrieval spans** for retriever invocations, capturing the query and retrieved
  document IDs and relevance scores when available.

The spans nest to reflect the graph, so a single graph invocation produces a
workflow span with the agent, tool, and model calls it triggered as children.

Installation
------------

::

    pip install opentelemetry-instrumentation-genai-langchain

See the `examples <examples>`_ directory for runnable ``workflow``, ``agent``,
``tools``, and ``zero-code`` scenarios.

Usage
-----

Call ``LangChainInstrumentor().instrument()`` once during startup, then build
and invoke your graph as usual. The example below traces a simple two-node
LangGraph ``StateGraph`` (``START → researcher → summariser → END``); the
``graph.invoke(...)`` call is recorded as a workflow span with the node model
calls nested underneath.

.. code-block:: python

    from typing import Annotated, TypedDict

    from langchain_core.messages import HumanMessage, SystemMessage
    from langchain_openai import ChatOpenAI
    from langgraph.graph import END, START, StateGraph
    from langgraph.graph.message import add_messages

    from opentelemetry.instrumentation.genai.langchain import LangChainInstrumentor

    LangChainInstrumentor().instrument()

    llm = ChatOpenAI(model="<your-model>", temperature=0)


    class State(TypedDict):
        messages: Annotated[list, add_messages]
        research: str


    def researcher(state: State) -> dict:
        response = llm.invoke(
            [
                SystemMessage(content="Provide 2-3 factual sentences."),
                HumanMessage(content=state["messages"][-1].content),
            ]
        )
        return {"research": response.content, "messages": [response]}


    def summariser(state: State) -> dict:
        response = llm.invoke(
            [
                SystemMessage(content="Condense the text into one sentence."),
                HumanMessage(content=state["research"]),
            ]
        )
        return {"messages": [response]}


    builder = StateGraph(State)
    builder.add_node("researcher", researcher)
    builder.add_node("summariser", summariser)
    builder.add_edge(START, "researcher")
    builder.add_edge("researcher", "summariser")
    builder.add_edge("summariser", END)
    graph = builder.compile()

    # Recorded as a workflow span with the two node LLM calls nested underneath.
    graph.invoke(
        {
            "messages": [HumanMessage(content="What is the capital of France?")],
            "research": "",
        }
    )

Retrieval Spans and Document Scores
-----------------------------------

When invoking LangChain retrievers (e.g., vectorstores, knowledge bases, or contextual compression retrievers),
retrieval spans are recorded with the query and retrieved document IDs and scores.

When message content capture is enabled (``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=SPAN_ONLY``
or ``SPAN_AND_EVENT``), the retrieved documents are serialized into the
``gen_ai.retrieval.documents`` span attribute using the shared ``RetrievalDocument``
model, as a JSON array of objects with only ``id`` and ``score``.
Document text (previously recorded as ``content``) and metadata are not captured.
Query text capture is unchanged.

When available, relevance and similarity scores are captured in each document object under ``score``:

* **Direct retrieval scores**: extracted from ``metadata["score"]`` (populated by retrievers such as
  ``AmazonKnowledgeBasesRetriever``, ``TavilySearchAPIRetriever``, and vectorstore score-threshold searches).
* **Reranking scores**: extracted from ``metadata["relevance_score"]`` (populated when retrievers are wrapped
  with rerankers via ``ContextualCompressionRetriever``, such as ``CohereRerank``).
* **Duck-typed / custom documents**: extracted from a top-level ``score`` attribute or mapping key.

If a document has no score, or if the score is non-numeric or non-finite (``NaN``, ``Infinity``),
``score`` is recorded as JSON ``null`` to ensure RFC 8259 JSON compliance.
Missing document IDs are also recorded as ``null``.

Configuration
-------------

By default, prompts and completions are not captured. To capture message content, set the
environment variable ``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`` to one of
``NO_CONTENT``, ``SPAN_ONLY``, ``EVENT_ONLY``, or ``SPAN_AND_EVENT``.

Prompts and completions can instead be uploaded to external storage via a completion hook: set
``OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK=upload`` with
``OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH`` (install the ``upload`` extra:
``pip install opentelemetry-util-genai[upload]``), or pass a custom ``CompletionHook``
programmatically, which takes precedence over the environment variable::

    LangChainInstrumentor().instrument(completion_hook=my_hook)

Known Limitations
-----------------

Context propagation to nested calls (such as auto-instrumented HTTP clients
or database queries within tools) is not supported when using LangChain async API.

References
----------

* `OpenTelemetry Project <https://opentelemetry.io/>`_
* `LangGraph <https://langchain-ai.github.io/langgraph/>`_
* `OpenTelemetry Python Examples <https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples>`_
