Skip to content
Closed
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
912b784
Add workflow and refactor LLM for langchain
wrisa Apr 16, 2026
9d4876f
Merge branch 'open-telemetry:main' into langchain-workflow-type
wrisa Apr 20, 2026
1ce4cf5
add workflow support and genai dependancy
wrisa Apr 16, 2026
e9fff12
fixed errors
wrisa Apr 20, 2026
6e34620
fixed changelog
wrisa Apr 20, 2026
c17607d
fixed error
wrisa Apr 20, 2026
a63f271
fixed type error
wrisa Apr 20, 2026
e8be641
removed optional
wrisa Apr 21, 2026
a9dc188
fixed error
wrisa Apr 21, 2026
3605631
ignore
wrisa Apr 21, 2026
4b7808f
fixed requests
wrisa Apr 21, 2026
e6d9f03
Merge branch 'main' into langchain-workflow-type
wrisa Apr 22, 2026
a503105
Merge branch 'main' into langchain-workflow-type
wrisa Apr 23, 2026
dc068fb
Merge branch 'main' into langchain-workflow-type
wrisa Apr 23, 2026
2ee7792
Merge branch 'main' into langchain-workflow-type
wrisa Apr 24, 2026
8ed66a4
Merge branch 'main' into langchain-workflow-type
wrisa Apr 27, 2026
1b5f2a3
Added delete and removed optional
wrisa Apr 24, 2026
93e806b
removed non workflow
wrisa Apr 24, 2026
16ee93a
removed line
wrisa Apr 27, 2026
eee467a
added new line
wrisa Apr 27, 2026
3f668b1
Merge branch 'main' into langchain-workflow-type
wrisa Apr 30, 2026
af5deaf
Merge branch 'main' into langchain-workflow-type
wrisa May 1, 2026
3df798a
Merge branch 'open-telemetry:main' into langchain-workflow-type
wrisa May 4, 2026
86d3a64
classify operation
wrisa May 1, 2026
06e438a
add invoke agent
wrisa May 4, 2026
5594a72
fixed errors and added tests
wrisa May 4, 2026
53dbd7b
fixed precommit error
wrisa May 4, 2026
0cd6b28
fixed error
wrisa May 4, 2026
5ede64d
fixed test and example
wrisa May 4, 2026
3844025
removed unused
wrisa May 5, 2026
546eab8
fixed test
wrisa May 5, 2026
e6b9b68
updated changelog
wrisa May 5, 2026
b5d0b3b
Merge branch 'main' into langchain-workflow-type
wrisa May 7, 2026
785f463
Merge branch 'main' into langchain-workflow-type
wrisa May 7, 2026
4f37393
Merge branch 'main' into langchain-workflow-type
wrisa May 7, 2026
8ae8b98
Merge branch 'open-telemetry:main' into langchain-workflow-type
wrisa May 7, 2026
c547813
added SPDX license
wrisa May 7, 2026
02c0f37
Merge branch 'main' into langchain-workflow-type
wrisa May 8, 2026
79f6814
Merge branch 'main' into langchain-workflow-type
wrisa May 11, 2026
3067e5f
input output messages and updated example
wrisa May 12, 2026
0f3dce2
fixed typecheck
wrisa May 12, 2026
debafbe
add cast
wrisa May 12, 2026
6652b43
Merge branch 'main' into langchain-workflow-type
wrisa May 12, 2026
2242323
Merge branch 'main' into langchain-workflow-type
wrisa May 12, 2026
cd9f32e
Merge branch 'main' into langchain-workflow-type
wrisa May 12, 2026
aa15e92
Merge branch 'main' into langchain-workflow-type
wrisa May 12, 2026
1553f2e
Merge branch 'main' into langchain-workflow-type
wrisa May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Add LangChain workflow and agent span support. Also refactor LLM invocation.
([#4449](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4449))
- Fix compatibility with wrapt 2.x by using positional arguments in `wrap_function_wrapper()` calls
([#4445](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4445))
- Added span support for genAI langchain llm invocation.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

"""
ReAct agent example built with LangGraph.

Uses LangGraph's prebuilt create_react_agent with simple calculator tools.
Comment thread
wrisa marked this conversation as resolved.
OpenTelemetry LangChain instrumentation traces the LLM calls made by the agent.
"""

from uuid import uuid4

from langchain.agents import create_agent
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

from opentelemetry import _logs, metrics, trace
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
OTLPLogExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
OTLPMetricExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.instrumentation.langchain import LangChainInstrumentor
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Configure tracing
trace.set_tracer_provider(TracerProvider())
span_processor = BatchSpanProcessor(OTLPSpanExporter())
trace.get_tracer_provider().add_span_processor(span_processor)

# Configure logging
_logs.set_logger_provider(LoggerProvider())
_logs.get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)

# Configure metrics
metrics.set_meter_provider(
MeterProvider(
metric_readers=[
PeriodicExportingMetricReader(
OTLPMetricExporter(),
),
]
)
)


@tool
def multiply(a: float, b: float) -> float:
"""Multiply two numbers together."""
return a * b


@tool
def add(a: float, b: float) -> float:
"""Add two numbers together."""
return a + b


def main():
LangChainInstrumentor().instrument()

llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.1,
max_tokens=100,
top_p=0.9,
seed=100,
)

session_id = str(uuid4())
agent = create_agent(
llm, tools=[multiply, add], name="coordinator"
).with_config(
{
"metadata": {
"agent_name": "coordinator",
"session_id": session_id,
},
}
)

result = agent.invoke(
{"messages": [HumanMessage(content="What is (3 * 4) + 7?")]}
)

print("Agent output:")
for msg in result["messages"]:
print(f" {type(msg).__name__}: {msg.content}")

LangChainInstrumentor().uninstrument()


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
langchain==0.3.21
langchain_openai
langgraph
opentelemetry-sdk>=1.39.0
opentelemetry-exporter-otlp-proto-grpc>=1.39.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

"""
Single-node agent example built with StateGraph.

A single ReAct agent answers arithmetic questions using calculator tools.
OpenTelemetry LangChain instrumentation traces all LLM calls.
"""

from uuid import uuid4

from langchain.agents import create_agent
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, MessagesState, StateGraph

from opentelemetry import _logs, metrics, trace
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
OTLPLogExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
OTLPMetricExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.instrumentation.langchain import LangChainInstrumentor
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Configure tracing
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(OTLPSpanExporter())
)

# Configure logging
_logs.set_logger_provider(LoggerProvider())
_logs.get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)

# Configure metrics
metrics.set_meter_provider(
MeterProvider(
metric_readers=[PeriodicExportingMetricReader(OTLPMetricExporter())]
)
)


# --- Tools ----------------------------------------------------------------


@tool
def multiply(a: float, b: float) -> float:
"""Multiply two numbers."""
return a * b


@tool
def add(a: float, b: float) -> float:
"""Add two numbers."""
return a + b


# --- Graph ----------------------------------------------------------------


def build_single_node_graph(llm: ChatOpenAI):
session_id = str(uuid4())

agent = create_agent(
llm, tools=[multiply, add], name="math_agent"
).with_config(
{
"metadata": {
"agent_name": "math_agent",
"session_id": session_id,
},
}
)

def run_agent(state: MessagesState) -> dict:
result = agent.invoke({"messages": state["messages"]})
return {"messages": result["messages"]}

builder = StateGraph(MessagesState)
builder.add_node("math_agent", run_agent)
builder.add_edge(START, "math_agent")
builder.add_edge("math_agent", END)

return builder.compile()


def main():
LangChainInstrumentor().instrument()

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1, seed=100)
graph = build_single_node_graph(llm)

questions = [
"What is 12 multiplied by 7?",
"What is 15 plus 27?",
]

for question in questions:
print(f"\nQuestion: {question}")
result = graph.invoke({"messages": [HumanMessage(content=question)]})
last = result["messages"][-1]
print(f" Answer: {last.content}")

LangChainInstrumentor().uninstrument()


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

"""
LangGraph StateGraph example with an LLM node.

Similar to the manual example (../manual/main.py) but uses LangGraph's StateGraph
with a node that calls ChatOpenAI. OpenTelemetry LangChain instrumentation traces
the LLM calls made from within the graph node.
"""

from typing import Annotated

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 typing_extensions import TypedDict

from opentelemetry import _logs, metrics, trace
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
OTLPLogExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
OTLPMetricExporter,
)
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.instrumentation.langchain import LangChainInstrumentor
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Configure tracing
trace.set_tracer_provider(TracerProvider())
span_processor = BatchSpanProcessor(OTLPSpanExporter())
trace.get_tracer_provider().add_span_processor(span_processor)

# Configure logging
_logs.set_logger_provider(LoggerProvider())
_logs.get_logger_provider().add_log_record_processor(
BatchLogRecordProcessor(OTLPLogExporter())
)

# Configure metrics
metrics.set_meter_provider(
MeterProvider(
metric_readers=[
PeriodicExportingMetricReader(
OTLPMetricExporter(),
),
]
)
)


class GraphState(TypedDict):
"""State for the graph; messages are accumulated with add_messages."""

messages: Annotated[list, add_messages]


def build_graph(llm: ChatOpenAI):
"""Build a StateGraph with a single LLM node."""

def llm_node(state: GraphState) -> dict:
"""Node that invokes the LLM with the current messages."""
response = llm.invoke(state["messages"])
return {"messages": [response]}

builder = StateGraph(GraphState)
builder.add_node("llm", llm_node)
builder.add_edge(START, "llm")
builder.add_edge("llm", END)
return builder.compile()


def main():
# Set up instrumentation (traces LLM calls from within graph nodes)
LangChainInstrumentor().instrument()

# ChatOpenAI setup
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.1,
max_tokens=100,
top_p=0.9,
frequency_penalty=0.5,
presence_penalty=0.5,
stop=["\n", "Human:", "AI:"],
seed=100,
)

graph = build_graph(llm)
Comment thread
wrisa marked this conversation as resolved.

initial_messages = [
SystemMessage(content="You are a helpful assistant!"),
HumanMessage(content="What is the capital of France?"),
]

result = graph.invoke({"messages": initial_messages})

print("LangGraph output (messages):")
for msg in result.get("messages", []):
print(f" {type(msg).__name__}: {msg.content}")

# Un-instrument after use
LangChainInstrumentor().uninstrument()


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
langchain==0.3.21
langchain_openai
langgraph
opentelemetry-sdk>=1.39.0
opentelemetry-exporter-otlp-proto-grpc>=1.39.0
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ classifiers = [
"Programming Language :: Python :: 3.14",
]
dependencies = [
"opentelemetry-instrumentation ~= 0.57b0",
"opentelemetry-instrumentation ~= 0.60b0",
"opentelemetry-util-genai >= 0.4b0.dev",
Comment thread
keith-decker marked this conversation as resolved.
Outdated
Comment thread
wrisa marked this conversation as resolved.
Outdated
]

[project.optional-dependencies]
Expand Down
Loading
Loading