Skip to content

Commit 30a60eb

Browse files
wrisaMikeGoldsmith
andauthored
Add LangChain invoke workflow and invoke agent span support (#25)
* Add LangChain invoke workflow and invoke agent span support * Added change log * fixed errors * updated changelog * Addressed lzchen comments * updated uv * addressed * fixed tests instrumentation-langchain-conformance * fixed tests * fixed test * fixed tests * Generate updated CI workflows for langchain instrumentation Add missing py312/py313 test jobs for instrumentation-langchain. Assisted-by: Claude Opus 4.6 --------- Co-authored-by: Mike Goldsmith <goldsmith.mike@gmail.com>
1 parent 171834a commit 30a60eb

27 files changed

Lines changed: 3873 additions & 99 deletions

.github/workflows/test.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,44 @@ jobs:
11141114
- name: Run tests
11151115
run: uvx --with tox-uv tox -e py313-test-instrumentation-claude-agent-sdk-latest -- -ra
11161116

1117+
py312-test-instrumentation-langchain_ubuntu-latest:
1118+
name: instrumentation-langchain 3.12 Ubuntu
1119+
runs-on: ubuntu-latest
1120+
timeout-minutes: 30
1121+
steps:
1122+
- name: Checkout repo @ SHA - ${{ github.sha }}
1123+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1124+
1125+
- name: Set up Python 3.12
1126+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
1127+
with:
1128+
python-version: "3.12"
1129+
1130+
- name: Set up uv
1131+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
1132+
1133+
- name: Run tests
1134+
run: uvx --with tox-uv tox -e py312-test-instrumentation-langchain -- -ra
1135+
1136+
py313-test-instrumentation-langchain_ubuntu-latest:
1137+
name: instrumentation-langchain 3.13 Ubuntu
1138+
runs-on: ubuntu-latest
1139+
timeout-minutes: 30
1140+
steps:
1141+
- name: Checkout repo @ SHA - ${{ github.sha }}
1142+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1143+
1144+
- name: Set up Python 3.13
1145+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
1146+
with:
1147+
python-version: "3.13"
1148+
1149+
- name: Set up uv
1150+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
1151+
1152+
- name: Run tests
1153+
run: uvx --with tox-uv tox -e py313-test-instrumentation-langchain -- -ra
1154+
11171155
py312-test-instrumentation-langchain-conformance_ubuntu-latest:
11181156
name: instrumentation-langchain-conformance 3.12 Ubuntu
11191157
runs-on: ubuntu-latest
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add LangChain workflow and agent span support
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
ReAct agent example built with LangGraph.
6+
7+
Uses LangGraph's prebuilt create_react_agent with simple calculator tools.
8+
OpenTelemetry LangChain instrumentation traces the LLM calls made by the agent.
9+
"""
10+
11+
from uuid import uuid4
12+
13+
from langchain_core.messages import HumanMessage
14+
from langchain_core.tools import tool
15+
from langchain_openai import ChatOpenAI
16+
from langgraph.prebuilt import create_react_agent
17+
18+
from opentelemetry import _logs, metrics, trace
19+
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
20+
OTLPLogExporter,
21+
)
22+
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
23+
OTLPMetricExporter,
24+
)
25+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
26+
OTLPSpanExporter,
27+
)
28+
from opentelemetry.instrumentation.langchain import LangChainInstrumentor
29+
from opentelemetry.sdk._logs import LoggerProvider
30+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
31+
from opentelemetry.sdk.metrics import MeterProvider
32+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
33+
from opentelemetry.sdk.trace import TracerProvider
34+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
35+
36+
# Configure tracing
37+
trace.set_tracer_provider(TracerProvider())
38+
span_processor = BatchSpanProcessor(OTLPSpanExporter())
39+
trace.get_tracer_provider().add_span_processor(span_processor)
40+
41+
# Configure logging
42+
_logs.set_logger_provider(LoggerProvider())
43+
_logs.get_logger_provider().add_log_record_processor(
44+
BatchLogRecordProcessor(OTLPLogExporter())
45+
)
46+
47+
# Configure metrics
48+
metrics.set_meter_provider(
49+
MeterProvider(
50+
metric_readers=[
51+
PeriodicExportingMetricReader(
52+
OTLPMetricExporter(),
53+
),
54+
]
55+
)
56+
)
57+
58+
59+
@tool
60+
def multiply(a: float, b: float) -> float:
61+
"""Multiply two numbers together."""
62+
return a * b
63+
64+
65+
@tool
66+
def add(a: float, b: float) -> float:
67+
"""Add two numbers together."""
68+
return a + b
69+
70+
71+
def main():
72+
LangChainInstrumentor().instrument()
73+
74+
llm = ChatOpenAI(
75+
model="gpt-3.5-turbo",
76+
temperature=0.1,
77+
max_tokens=100,
78+
top_p=0.9,
79+
seed=100,
80+
)
81+
82+
session_id = str(uuid4())
83+
agent = create_react_agent(llm, tools=[multiply, add]).with_config(
84+
{
85+
"metadata": {
86+
"agent_name": "coordinator",
87+
"session_id": session_id,
88+
},
89+
}
90+
)
91+
92+
result = agent.invoke(
93+
{"messages": [HumanMessage(content="What is (3 * 4) + 7?")]}
94+
)
95+
96+
print("Agent output:")
97+
for msg in result["messages"]:
98+
print(f" {type(msg).__name__}: {msg.content}")
99+
100+
LangChainInstrumentor().uninstrument()
101+
102+
103+
if __name__ == "__main__":
104+
main()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
langchain==0.3.21
2+
langchain_openai
3+
langgraph
4+
opentelemetry-sdk>=1.42.0
5+
opentelemetry-exporter-otlp-proto-grpc>=1.42.0
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Single-node agent example built with StateGraph.
6+
7+
A single ReAct agent answers arithmetic questions using calculator tools.
8+
OpenTelemetry LangChain instrumentation traces all LLM calls.
9+
"""
10+
11+
from uuid import uuid4
12+
13+
from langchain_core.messages import HumanMessage
14+
from langchain_core.tools import tool
15+
from langchain_openai import ChatOpenAI
16+
from langgraph.graph import END, START, MessagesState, StateGraph
17+
from langgraph.prebuilt import create_react_agent
18+
19+
from opentelemetry import _logs, metrics, trace
20+
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import (
21+
OTLPLogExporter,
22+
)
23+
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
24+
OTLPMetricExporter,
25+
)
26+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
27+
OTLPSpanExporter,
28+
)
29+
from opentelemetry.instrumentation.langchain import LangChainInstrumentor
30+
from opentelemetry.sdk._logs import LoggerProvider
31+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
32+
from opentelemetry.sdk.metrics import MeterProvider
33+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
34+
from opentelemetry.sdk.trace import TracerProvider
35+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
36+
37+
# Configure tracing
38+
trace.set_tracer_provider(TracerProvider())
39+
trace.get_tracer_provider().add_span_processor(
40+
BatchSpanProcessor(OTLPSpanExporter())
41+
)
42+
43+
# Configure logging
44+
_logs.set_logger_provider(LoggerProvider())
45+
_logs.get_logger_provider().add_log_record_processor(
46+
BatchLogRecordProcessor(OTLPLogExporter())
47+
)
48+
49+
# Configure metrics
50+
metrics.set_meter_provider(
51+
MeterProvider(
52+
metric_readers=[PeriodicExportingMetricReader(OTLPMetricExporter())]
53+
)
54+
)
55+
56+
57+
# --- Tools ----------------------------------------------------------------
58+
59+
60+
@tool
61+
def multiply(a: float, b: float) -> float:
62+
"""Multiply two numbers."""
63+
return a * b
64+
65+
66+
@tool
67+
def add(a: float, b: float) -> float:
68+
"""Add two numbers."""
69+
return a + b
70+
71+
72+
# --- Graph ----------------------------------------------------------------
73+
74+
75+
def build_single_node_graph(llm: ChatOpenAI):
76+
session_id = str(uuid4())
77+
78+
agent = create_react_agent(
79+
llm, tools=[multiply, add], name="math_agent"
80+
).with_config(
81+
{
82+
"metadata": {
83+
"agent_name": "math_agent",
84+
"session_id": session_id,
85+
},
86+
}
87+
)
88+
89+
def run_agent(state: MessagesState) -> dict:
90+
result = agent.invoke({"messages": state["messages"]})
91+
return {"messages": result["messages"]}
92+
93+
builder = StateGraph(MessagesState)
94+
builder.add_node("math_agent", run_agent)
95+
builder.add_edge(START, "math_agent")
96+
builder.add_edge("math_agent", END)
97+
98+
return builder.compile()
99+
100+
101+
def main():
102+
LangChainInstrumentor().instrument()
103+
104+
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.1, seed=100)
105+
graph = build_single_node_graph(llm)
106+
107+
questions = [
108+
"What is 12 multiplied by 7?",
109+
"What is 15 plus 27?",
110+
]
111+
112+
for question in questions:
113+
print(f"\nQuestion: {question}")
114+
result = graph.invoke({"messages": [HumanMessage(content=question)]})
115+
last = result["messages"][-1]
116+
print(f" Answer: {last.content}")
117+
118+
LangChainInstrumentor().uninstrument()
119+
120+
121+
if __name__ == "__main__":
122+
main()

0 commit comments

Comments
 (0)