Skip to content

Commit 6783a42

Browse files
lmolkovashuningc
authored andcommitted
feat(util-genai): refactor and make API smaller and more user-friendly (open-telemetry#4391)
* Refactor public API on GenAI utils * more lint * review feedback * update tests to use named params * address some of the comments * up * fix failing checks and clean up imports * lint * lint * fix lint * replace @deprecated with docstring info to avoid warnings for users * up * common code for context manager
1 parent dc78bd0 commit 6783a42

File tree

33 files changed

+1579
-1361
lines changed

33 files changed

+1579
-1361
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,8 @@ target
6161

6262
# Benchmark result files
6363
*-benchmark.json
64+
65+
# opentelemetry-admin jobs
66+
opentelemetry-admin-jobs.txt
67+
68+
.claude/settings.local.json

instrumentation-genai/AGENTS.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# GenAI Instrumentation — Agent and Contributor Guidelines
2+
3+
Instrumentation packages here wrap specific libraries (OpenAI, Anthropic, etc.) and bridge
4+
them to the shared telemetry layer in `util/opentelemetry-util-genai`.
5+
6+
## 1. Instrumentation Layer Boundary
7+
8+
Do not call OpenTelemetry APIs (`tracer`, `meter`, `span`, event APIs) directly.
9+
Always go through `TelemetryHandler` and the invocation objects it returns.
10+
11+
This layer is responsible only for:
12+
13+
- Patching the library
14+
- Parsing library-specific input/output into invocation fields
15+
16+
Everything else (span creation, metric recording, event emission, context propagation)
17+
belongs in `util/opentelemetry-util-genai`.
18+
19+
## 2. Invocation Pattern
20+
21+
Use `start_*()` and control span lifetime manually:
22+
23+
```python
24+
invocation = handler.start_inference(provider, request_model, server_address=..., server_port=...)
25+
invocation.temperature = ...
26+
try:
27+
response = client.call(...)
28+
invocation.response_model_name = response.model
29+
invocation.finish_reasons = response.finish_reasons
30+
invocation.stop()
31+
except Exception as exc:
32+
invocation.fail(exc)
33+
raise
34+
```
35+
36+
## 3. Exception Handling
37+
38+
- Do not add `raise {Error}` statements in instrumentation/telemetry code — validation belongs in
39+
tests and callers, not in the instrumentation layer.
40+
- When catching exceptions from the underlying library to record telemetry, always re-raise
41+
the original exception unmodified.

instrumentation-genai/CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/patch.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
gen_ai_attributes as GenAIAttributes,
2727
)
2828
from opentelemetry.util.genai.handler import TelemetryHandler
29-
from opentelemetry.util.genai.types import Error, LLMInvocation
29+
from opentelemetry.util.genai.types import (
30+
Error,
31+
LLMInvocation, # TODO: migrate to InferenceInvocation
32+
)
3033
from opentelemetry.util.genai.utils import (
3134
should_capture_content_on_spans_in_experimental_mode,
3235
)

instrumentation-genai/opentelemetry-instrumentation-anthropic/src/opentelemetry/instrumentation/anthropic/wrappers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from opentelemetry.util.genai.handler import TelemetryHandler
3232
from opentelemetry.util.genai.types import (
3333
Error,
34-
LLMInvocation,
34+
LLMInvocation, # TODO: migrate to InferenceInvocation
3535
)
3636

3737
from .messages_extractors import set_invocation_response_attributes

instrumentation-genai/opentelemetry-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/callback_handler.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from opentelemetry.util.genai.types import (
2929
Error,
3030
InputMessage,
31-
LLMInvocation,
31+
LLMInvocation, # TODO: migrate to InferenceInvocation
3232
MessagePart,
3333
OutputMessage,
3434
Text,
@@ -158,7 +158,7 @@ def on_chat_model_start(
158158
self._invocation_manager.add_invocation_state(
159159
run_id=run_id,
160160
parent_run_id=parent_run_id,
161-
invocation=llm_invocation,
161+
invocation=llm_invocation, # pyright: ignore[reportArgumentType]
162162
)
163163

164164
def on_llm_end(
@@ -171,7 +171,8 @@ def on_llm_end(
171171
) -> None:
172172
llm_invocation = self._invocation_manager.get_invocation(run_id=run_id)
173173
if llm_invocation is None or not isinstance(
174-
llm_invocation, LLMInvocation
174+
llm_invocation,
175+
LLMInvocation,
175176
):
176177
# If the invocation does not exist, we cannot set attributes or end it
177178
return
@@ -262,7 +263,8 @@ def on_llm_error(
262263
) -> None:
263264
llm_invocation = self._invocation_manager.get_invocation(run_id=run_id)
264265
if llm_invocation is None or not isinstance(
265-
llm_invocation, LLMInvocation
266+
llm_invocation,
267+
LLMInvocation,
266268
):
267269
# If the invocation does not exist, we cannot set attributes or end it
268270
return

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ classifiers = [
2525
"Programming Language :: Python :: 3.14",
2626
]
2727
dependencies = [
28-
"opentelemetry-api >= 1.37",
28+
"opentelemetry-api >= 1.39",
2929
"opentelemetry-instrumentation >= 0.58b0",
30-
"opentelemetry-semantic-conventions >= 0.58b0",
31-
"opentelemetry-util-genai"
30+
"opentelemetry-semantic-conventions >= 0.60b0",
31+
"opentelemetry-util-genai >= 0.4b0.dev"
3232
]
3333

3434
[project.optional-dependencies]

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.latest.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@ grpcio>=1.60.0; implementation_name != "pypy"
5050
grpcio<1.60.0; implementation_name == "pypy"
5151

5252
-e opentelemetry-instrumentation
53+
-e util/opentelemetry-util-genai
5354
-e instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/requirements.oldest.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ pytest-asyncio==0.21.0
2626
wrapt==1.16.0
2727
opentelemetry-exporter-otlp-proto-grpc~=1.30
2828
opentelemetry-exporter-otlp-proto-http~=1.30
29-
opentelemetry-api==1.37 # when updating, also update in pyproject.toml
30-
opentelemetry-sdk==1.37 # when updating, also update in pyproject.toml
31-
opentelemetry-semantic-conventions==0.58b0 # when updating, also update in pyproject.toml
29+
opentelemetry-api==1.39 # when updating, also update in pyproject.toml
30+
opentelemetry-sdk==1.39 # when updating, also update in pyproject.toml
31+
opentelemetry-semantic-conventions==0.60b0 # when updating, also update in pyproject.toml
3232
grpcio>=1.60.0; implementation_name != "pypy"
3333
grpcio<1.60.0; implementation_name == "pypy"
3434

35+
-e util/opentelemetry-util-genai
3536
-e instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2

instrumentation-genai/opentelemetry-instrumentation-openai-v2/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ dependencies = [
2828
"opentelemetry-api ~= 1.39",
2929
"opentelemetry-instrumentation ~= 0.60b0",
3030
"opentelemetry-semantic-conventions ~= 0.60b0",
31-
"opentelemetry-util-genai",
31+
"opentelemetry-util-genai >= 0.4b0.dev",
3232
]
3333

3434
[project.optional-dependencies]

0 commit comments

Comments
 (0)