traceable-agent 2.0.0__tar.gz → 2.1.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/PKG-INFO +46 -7
- traceable_agent-2.1.0b1/pyproject.toml +15 -0
- traceable_agent-2.1.0b1/setup.py +113 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/__init__.py +119 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/messages_extractors.py +251 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/package.py +15 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/patch.py +131 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/utils.py +243 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/version.py +15 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/wrappers.py +435 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/__init__.py +168 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/instruments.py +52 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/package.py +18 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/patch.py +952 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/response_extractors.py +419 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/response_wrappers.py +467 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/utils.py +502 -0
- traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/version.py +15 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/__init__.py +13 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_embedding_invocation.py +123 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_inference_invocation.py +358 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_invocation.py +140 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_tool_invocation.py +96 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_upload/__init__.py +90 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_upload/completion_hook.py +404 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_workflow_invocation.py +115 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/completion_hook.py +126 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/environment_variables.py +71 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/handler.py +335 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/instruments.py +54 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/invocation.py +47 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/metrics.py +54 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/types.py +255 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/utils.py +143 -0
- traceable_agent-2.1.0b1/src/opentelemetry/util/genai/version.py +15 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceable_agent.egg-info/PKG-INFO +46 -7
- traceable_agent-2.1.0b1/src/traceable_agent.egg-info/SOURCES.txt +90 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceable_agent.egg-info/entry_points.txt +0 -1
- traceable_agent-2.1.0b1/src/traceable_agent.egg-info/requires.txt +39 -0
- traceable_agent-2.1.0b1/src/traceable_agent.egg-info/top_level.txt +2 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/agent.py +31 -6
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/agent_init.py +60 -15
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/config/config.py +15 -5
- traceable_agent-2.1.0b1/src/traceableai/config/config_pb2.py +112 -0
- traceable_agent-2.1.0b1/src/traceableai/config/default.py +77 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/config/environment.py +90 -15
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/constants.py +1 -1
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/custom_logger.py +3 -1
- traceable_agent-2.1.0b1/src/traceableai/excluded_by_attribute_span_processor.py +40 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/filter/__init__.py +7 -12
- traceable_agent-2.1.0b1/src/traceableai/filter/libtraceable_common.py +31 -0
- traceable_agent-2.1.0b1/src/traceableai/filter/registry.py +51 -0
- traceable_agent-2.1.0b1/src/traceableai/filter/traceable.py +356 -0
- traceable_agent-2.1.0b1/src/traceableai/gen_ai/__init__.py +7 -0
- traceable_agent-2.1.0b1/src/traceableai/gen_ai/exceptions.py +17 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/__init__.py +21 -62
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/aiohttp/__init__.py +84 -48
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/anthropic/__init__.py +379 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/aws_lambda/__init__.py +2 -1
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/django/__init__.py +22 -9
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/fast_api/__init__.py +47 -21
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/flask/__init__.py +16 -12
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/genai_env.py +71 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/grpc/__init__.py +6 -5
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/httpx/__init__.py +73 -0
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/httpx/utils.py +160 -0
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/instrumentation_definitions.py +171 -0
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/litellm/__init__.py +401 -0
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/mcp/__init__.py +40 -0
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/mcp/gen_ai_mirror.py +204 -0
- traceable_agent-2.1.0b1/src/traceableai/instrumentation/openai/__init__.py +340 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/requests/__init__.py +7 -1
- traceable_agent-2.1.0b1/src/traceableai/otlp_reporting.py +75 -0
- traceable_agent-2.1.0b1/src/traceableai/sampling_span_processor.py +63 -0
- traceable_agent-2.1.0b1/src/traceableai/span_attributes_processor.py +19 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/version.py +1 -1
- traceable-agent-2.0.0/pyproject.toml +0 -8
- traceable-agent-2.0.0/setup.py +0 -67
- traceable-agent-2.0.0/src/traceable_agent.egg-info/SOURCES.txt +0 -45
- traceable-agent-2.0.0/src/traceable_agent.egg-info/requires.txt +0 -28
- traceable-agent-2.0.0/src/traceable_agent.egg-info/top_level.txt +0 -1
- traceable-agent-2.0.0/src/traceableai/config/config_pb2.py +0 -91
- traceable-agent-2.0.0/src/traceableai/config/default.py +0 -54
- traceable-agent-2.0.0/src/traceableai/filter/filter.py +0 -18
- traceable-agent-2.0.0/src/traceableai/filter/registry.py +0 -42
- traceable-agent-2.0.0/src/traceableai/filter/traceable.py +0 -260
- traceable-agent-2.0.0/src/traceableai/instrumentation/boto/__init__.py +0 -11
- traceable-agent-2.0.0/src/traceableai/instrumentation/instrumentation_definitions.py +0 -99
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/MANIFEST.in +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/README.md +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/setup.cfg +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceable_agent.egg-info/dependency_links.txt +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/__init__.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/autoinstrumentation/__init__.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/autoinstrumentation/sitecustomize.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/autoinstrumentation/wrapper.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/config/__init__.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/env.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/botocore/__init__.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/django/django_auto_instrumentation_compat.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/fast_api/fast_api_auto_instrumentation_compat.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/mysql/__init__.py +0 -0
- {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/postgresql/__init__.py +0 -0
|
@@ -1,15 +1,56 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: traceable-agent
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0b1
|
|
4
4
|
Summary: Traceable.ai Python Agent
|
|
5
5
|
Home-page: https://traceable.ai
|
|
6
6
|
Author: Traceable.ai
|
|
7
|
-
License: UNKNOWN
|
|
8
|
-
Platform: UNKNOWN
|
|
9
7
|
Classifier: Programming Language :: Python :: 3
|
|
10
8
|
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.10
|
|
12
10
|
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: opentelemetry-api==1.41.1
|
|
12
|
+
Requires-Dist: opentelemetry-exporter-otlp==1.41.1
|
|
13
|
+
Requires-Dist: opentelemetry-instrumentation==0.62b1
|
|
14
|
+
Requires-Dist: opentelemetry-instrumentation-aiohttp-client==0.62b1
|
|
15
|
+
Requires-Dist: opentelemetry-instrumentation-botocore==0.62b1
|
|
16
|
+
Requires-Dist: opentelemetry-instrumentation-wsgi==0.62b1
|
|
17
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi==0.62b1
|
|
18
|
+
Requires-Dist: opentelemetry-instrumentation-flask==0.62b1
|
|
19
|
+
Requires-Dist: opentelemetry-instrumentation-mysql==0.62b1
|
|
20
|
+
Requires-Dist: opentelemetry-instrumentation-psycopg2==0.62b1
|
|
21
|
+
Requires-Dist: opentelemetry-instrumentation-requests==0.62b1
|
|
22
|
+
Requires-Dist: opentelemetry-instrumentation-httpx==0.62b1
|
|
23
|
+
Requires-Dist: opentelemetry-instrumentation-grpc==0.62b1
|
|
24
|
+
Requires-Dist: opentelemetry-instrumentation-django==0.62b1
|
|
25
|
+
Requires-Dist: opentelemetry-instrumentation-aws-lambda==0.62b1
|
|
26
|
+
Requires-Dist: opentelemetry-instrumentation-mcp==0.60.0
|
|
27
|
+
Requires-Dist: opentelemetry-semantic-conventions-ai<0.6.0,>=0.5.1
|
|
28
|
+
Requires-Dist: opentelemetry-propagator-b3==1.41.1
|
|
29
|
+
Requires-Dist: opentelemetry-proto==1.41.1
|
|
30
|
+
Requires-Dist: opentelemetry-sdk==1.41.1
|
|
31
|
+
Requires-Dist: opentelemetry-util-http==0.62b1
|
|
32
|
+
Requires-Dist: google>=3.0.0
|
|
33
|
+
Requires-Dist: pyyaml
|
|
34
|
+
Requires-Dist: protobuf
|
|
35
|
+
Requires-Dist: psutil
|
|
36
|
+
Requires-Dist: distro
|
|
37
|
+
Requires-Dist: cffi
|
|
38
|
+
Requires-Dist: setuptools
|
|
39
|
+
Requires-Dist: jaraco.text
|
|
40
|
+
Requires-Dist: platformdirs
|
|
41
|
+
Provides-Extra: anthropic
|
|
42
|
+
Requires-Dist: anthropic>=0.34.0; extra == "anthropic"
|
|
43
|
+
Provides-Extra: openai
|
|
44
|
+
Requires-Dist: openai>=1.40.0; extra == "openai"
|
|
45
|
+
Provides-Extra: litellm
|
|
46
|
+
Requires-Dist: litellm>=1.60.0; extra == "litellm"
|
|
47
|
+
Dynamic: author
|
|
48
|
+
Dynamic: classifier
|
|
49
|
+
Dynamic: home-page
|
|
50
|
+
Dynamic: provides-extra
|
|
51
|
+
Dynamic: requires-dist
|
|
52
|
+
Dynamic: requires-python
|
|
53
|
+
Dynamic: summary
|
|
13
54
|
|
|
14
55
|
# Python Agent
|
|
15
56
|
|
|
@@ -93,5 +134,3 @@ git submodule update --init --recursive
|
|
|
93
134
|
```
|
|
94
135
|
- Test applications
|
|
95
136
|
1. Refer to `docker/test_apps/`.
|
|
96
|
-
|
|
97
|
-
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools>=42",
|
|
4
|
+
"wheel",
|
|
5
|
+
|
|
6
|
+
]
|
|
7
|
+
build-backend = "setuptools.build_meta"
|
|
8
|
+
|
|
9
|
+
[tool.pytest.ini_options]
|
|
10
|
+
asyncio_mode = "auto"
|
|
11
|
+
testpaths = ["test"]
|
|
12
|
+
|
|
13
|
+
[tool.pylint.messages_control]
|
|
14
|
+
disable = ["trailing-whitespace"]
|
|
15
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# pylint: skip-file
|
|
2
|
+
import os
|
|
3
|
+
from setuptools import setup, find_packages, Extension
|
|
4
|
+
|
|
5
|
+
exec(open('src/traceableai/version.py').read())
|
|
6
|
+
|
|
7
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
8
|
+
long_description = fh.read()
|
|
9
|
+
|
|
10
|
+
# Bundle pre-release vendor packages that aren't yet on PyPI.
|
|
11
|
+
# These are fetched by scripts/fetch-vendor.sh before building.
|
|
12
|
+
# We copy them under src/ and list them explicitly (they're namespace packages
|
|
13
|
+
# so find_packages() won't discover them automatically).
|
|
14
|
+
# Once they're published to PyPI, remove this block and add them
|
|
15
|
+
# as versioned dependencies in extras_require['anthropic'] instead.
|
|
16
|
+
import shutil
|
|
17
|
+
|
|
18
|
+
_ROOT = os.path.dirname(os.path.abspath(__file__))
|
|
19
|
+
|
|
20
|
+
_VENDOR_SRC = [
|
|
21
|
+
os.path.join(_ROOT, 'temporary-vendor', 'opentelemetry-instrumentation-anthropic', 'src', 'opentelemetry'),
|
|
22
|
+
os.path.join(_ROOT, 'temporary-vendor', 'opentelemetry-instrumentation-openai-v2', 'src', 'opentelemetry'),
|
|
23
|
+
os.path.join(_ROOT, 'temporary-vendor', 'opentelemetry-util-genai', 'src', 'opentelemetry'),
|
|
24
|
+
]
|
|
25
|
+
_VENDOR_DST = os.path.join(_ROOT, 'src', 'opentelemetry')
|
|
26
|
+
|
|
27
|
+
for _src in _VENDOR_SRC:
|
|
28
|
+
if os.path.isdir(_src):
|
|
29
|
+
shutil.copytree(_src, _VENDOR_DST, dirs_exist_ok=True)
|
|
30
|
+
|
|
31
|
+
# Collect all sub-packages with __init__.py from the merged destination
|
|
32
|
+
# (namespace package roots like opentelemetry/ have no __init__.py and are excluded)
|
|
33
|
+
_VENDOR_PACKAGES = []
|
|
34
|
+
if os.path.isdir(_VENDOR_DST):
|
|
35
|
+
for _dirpath, _dirnames, _filenames in os.walk(_VENDOR_DST):
|
|
36
|
+
if '__init__.py' in _filenames:
|
|
37
|
+
_rel = os.path.relpath(_dirpath, os.path.join(_ROOT, 'src'))
|
|
38
|
+
_VENDOR_PACKAGES.append(_rel.replace(os.sep, '.'))
|
|
39
|
+
|
|
40
|
+
setup(
|
|
41
|
+
name="traceable-agent",
|
|
42
|
+
version=__version__,
|
|
43
|
+
author="Traceable.ai",
|
|
44
|
+
description="Traceable.ai Python Agent",
|
|
45
|
+
url="https://traceable.ai",
|
|
46
|
+
classifiers=[
|
|
47
|
+
"Programming Language :: Python :: 3",
|
|
48
|
+
"Operating System :: OS Independent"
|
|
49
|
+
],
|
|
50
|
+
package_dir={"": "src"},
|
|
51
|
+
packages=find_packages(where="src") + _VENDOR_PACKAGES,
|
|
52
|
+
include_package_data=True,
|
|
53
|
+
python_requires=">=3.10",
|
|
54
|
+
ext_modules=[
|
|
55
|
+
# this option will let wheel builder know to package c extensions when ran from CI. No effect for source tar installs
|
|
56
|
+
Extension(
|
|
57
|
+
name='traceableai.filter._libtraceable',
|
|
58
|
+
sources=[],
|
|
59
|
+
optional=os.environ.get('CIBUILDWHEEL', '0') != '1'
|
|
60
|
+
)
|
|
61
|
+
],
|
|
62
|
+
install_requires=[
|
|
63
|
+
"opentelemetry-api==1.41.1",
|
|
64
|
+
"opentelemetry-exporter-otlp==1.41.1",
|
|
65
|
+
"opentelemetry-instrumentation==0.62b1",
|
|
66
|
+
"opentelemetry-instrumentation-aiohttp-client==0.62b1",
|
|
67
|
+
"opentelemetry-instrumentation-botocore==0.62b1",
|
|
68
|
+
"opentelemetry-instrumentation-wsgi==0.62b1",
|
|
69
|
+
"opentelemetry-instrumentation-fastapi==0.62b1",
|
|
70
|
+
"opentelemetry-instrumentation-flask==0.62b1",
|
|
71
|
+
"opentelemetry-instrumentation-mysql==0.62b1",
|
|
72
|
+
"opentelemetry-instrumentation-psycopg2==0.62b1",
|
|
73
|
+
"opentelemetry-instrumentation-requests==0.62b1",
|
|
74
|
+
"opentelemetry-instrumentation-httpx==0.62b1",
|
|
75
|
+
"opentelemetry-instrumentation-grpc==0.62b1",
|
|
76
|
+
"opentelemetry-instrumentation-django==0.62b1",
|
|
77
|
+
"opentelemetry-instrumentation-aws-lambda==0.62b1",
|
|
78
|
+
"opentelemetry-instrumentation-mcp==0.60.0",
|
|
79
|
+
"opentelemetry-semantic-conventions-ai>=0.5.1,<0.6.0",
|
|
80
|
+
"opentelemetry-propagator-b3==1.41.1",
|
|
81
|
+
"opentelemetry-proto==1.41.1",
|
|
82
|
+
"opentelemetry-sdk==1.41.1",
|
|
83
|
+
"opentelemetry-util-http==0.62b1",
|
|
84
|
+
"google>=3.0.0",
|
|
85
|
+
"pyyaml",
|
|
86
|
+
"protobuf",
|
|
87
|
+
"psutil",
|
|
88
|
+
"distro",
|
|
89
|
+
"cffi",
|
|
90
|
+
"setuptools",
|
|
91
|
+
"jaraco.text",
|
|
92
|
+
"platformdirs"
|
|
93
|
+
],
|
|
94
|
+
entry_points={
|
|
95
|
+
'console_scripts': [
|
|
96
|
+
'traceableai-instrument = traceableai.autoinstrumentation.wrapper:run',
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
extras_require={
|
|
100
|
+
'anthropic': [
|
|
101
|
+
'anthropic>=0.34.0',
|
|
102
|
+
# opentelemetry-util-genai and opentelemetry-instrumentation-anthropic
|
|
103
|
+
# are bundled directly (not yet on PyPI). When they are published,
|
|
104
|
+
# add them here as versioned deps and remove the bundle logic above.
|
|
105
|
+
],
|
|
106
|
+
'openai': [
|
|
107
|
+
'openai>=1.40.0',
|
|
108
|
+
],
|
|
109
|
+
'litellm': [
|
|
110
|
+
'litellm>=1.60.0',
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Copyright The OpenTelemetry Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
OpenTelemetry Anthropic Instrumentation
|
|
17
|
+
========================================
|
|
18
|
+
|
|
19
|
+
Instrumentation for the Anthropic Python SDK.
|
|
20
|
+
|
|
21
|
+
Usage
|
|
22
|
+
-----
|
|
23
|
+
|
|
24
|
+
.. code-block:: python
|
|
25
|
+
|
|
26
|
+
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
|
|
27
|
+
import anthropic
|
|
28
|
+
|
|
29
|
+
# Enable instrumentation
|
|
30
|
+
AnthropicInstrumentor().instrument()
|
|
31
|
+
|
|
32
|
+
# Use Anthropic client normally
|
|
33
|
+
client = anthropic.Anthropic()
|
|
34
|
+
response = client.messages.create(
|
|
35
|
+
model="claude-3-5-sonnet-20241022",
|
|
36
|
+
max_tokens=1024,
|
|
37
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
Configuration
|
|
41
|
+
-------------
|
|
42
|
+
|
|
43
|
+
Message content capture can be enabled by setting the environment variable:
|
|
44
|
+
``OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true``
|
|
45
|
+
|
|
46
|
+
API
|
|
47
|
+
---
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from typing import Any, Collection
|
|
51
|
+
|
|
52
|
+
from wrapt import (
|
|
53
|
+
wrap_function_wrapper, # pyright: ignore[reportUnknownVariableType]
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
from opentelemetry.instrumentation.anthropic.package import _instruments
|
|
57
|
+
from opentelemetry.instrumentation.anthropic.patch import (
|
|
58
|
+
messages_create,
|
|
59
|
+
)
|
|
60
|
+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
61
|
+
from opentelemetry.instrumentation.utils import unwrap
|
|
62
|
+
from opentelemetry.util.genai.handler import TelemetryHandler
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class AnthropicInstrumentor(BaseInstrumentor):
|
|
66
|
+
"""An instrumentor for the Anthropic Python SDK.
|
|
67
|
+
|
|
68
|
+
This instrumentor will automatically trace Anthropic API calls and
|
|
69
|
+
optionally capture message content as events.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(self) -> None:
|
|
73
|
+
super().__init__()
|
|
74
|
+
self._tracer = None
|
|
75
|
+
self._logger = None
|
|
76
|
+
self._meter = None
|
|
77
|
+
|
|
78
|
+
# pylint: disable=no-self-use
|
|
79
|
+
def instrumentation_dependencies(self) -> Collection[str]:
|
|
80
|
+
return _instruments
|
|
81
|
+
|
|
82
|
+
def _instrument(self, **kwargs: Any) -> None:
|
|
83
|
+
"""Enable Anthropic instrumentation.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
**kwargs: Optional arguments
|
|
87
|
+
- tracer_provider: TracerProvider instance
|
|
88
|
+
- meter_provider: MeterProvider instance
|
|
89
|
+
- logger_provider: LoggerProvider instance
|
|
90
|
+
"""
|
|
91
|
+
# Get providers from kwargs
|
|
92
|
+
tracer_provider = kwargs.get("tracer_provider")
|
|
93
|
+
meter_provider = kwargs.get("meter_provider")
|
|
94
|
+
logger_provider = kwargs.get("logger_provider")
|
|
95
|
+
|
|
96
|
+
handler = TelemetryHandler(
|
|
97
|
+
tracer_provider=tracer_provider,
|
|
98
|
+
meter_provider=meter_provider,
|
|
99
|
+
logger_provider=logger_provider,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Patch Messages.create
|
|
103
|
+
wrap_function_wrapper(
|
|
104
|
+
"anthropic.resources.messages",
|
|
105
|
+
"Messages.create",
|
|
106
|
+
messages_create(handler),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def _uninstrument(self, **kwargs: Any) -> None:
|
|
110
|
+
"""Disable Anthropic instrumentation.
|
|
111
|
+
|
|
112
|
+
This removes all patches applied during instrumentation.
|
|
113
|
+
"""
|
|
114
|
+
import anthropic # pylint: disable=import-outside-toplevel # noqa: PLC0415
|
|
115
|
+
|
|
116
|
+
unwrap(
|
|
117
|
+
anthropic.resources.messages.Messages, # pyright: ignore[reportAttributeAccessIssue,reportUnknownMemberType,reportUnknownArgumentType]
|
|
118
|
+
"create",
|
|
119
|
+
)
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Copyright The OpenTelemetry Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Get/extract helpers for Anthropic Messages instrumentation."""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from typing import TYPE_CHECKING, Sequence
|
|
21
|
+
|
|
22
|
+
from anthropic.types import MessageDeltaUsage
|
|
23
|
+
|
|
24
|
+
from opentelemetry.semconv._incubating.attributes import (
|
|
25
|
+
gen_ai_attributes as GenAIAttributes,
|
|
26
|
+
)
|
|
27
|
+
from opentelemetry.semconv._incubating.attributes import (
|
|
28
|
+
server_attributes as ServerAttributes,
|
|
29
|
+
)
|
|
30
|
+
from opentelemetry.util.genai.types import (
|
|
31
|
+
InputMessage,
|
|
32
|
+
LLMInvocation,
|
|
33
|
+
MessagePart,
|
|
34
|
+
OutputMessage,
|
|
35
|
+
)
|
|
36
|
+
from opentelemetry.util.types import AttributeValue
|
|
37
|
+
|
|
38
|
+
from .utils import (
|
|
39
|
+
convert_content_to_parts,
|
|
40
|
+
normalize_finish_reason,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from collections.abc import Iterable, Mapping
|
|
45
|
+
|
|
46
|
+
import httpx
|
|
47
|
+
from anthropic.resources.messages import Messages
|
|
48
|
+
from anthropic.types import (
|
|
49
|
+
Message,
|
|
50
|
+
MessageParam,
|
|
51
|
+
MetadataParam,
|
|
52
|
+
TextBlockParam,
|
|
53
|
+
ThinkingConfigParam,
|
|
54
|
+
ToolChoiceParam,
|
|
55
|
+
ToolUnionParam,
|
|
56
|
+
Usage,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class MessageRequestParams:
|
|
62
|
+
model: str | None = None
|
|
63
|
+
max_tokens: int | None = None
|
|
64
|
+
temperature: float | None = None
|
|
65
|
+
top_k: int | None = None
|
|
66
|
+
top_p: float | None = None
|
|
67
|
+
stop_sequences: Sequence[str] | None = None
|
|
68
|
+
stream: bool | None = None
|
|
69
|
+
messages: Iterable[MessageParam] | None = None
|
|
70
|
+
system: str | Iterable[TextBlockParam] | None = None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = (
|
|
74
|
+
"gen_ai.usage.cache_creation.input_tokens"
|
|
75
|
+
)
|
|
76
|
+
GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class UsageTokens:
|
|
81
|
+
input_tokens: int | None = None
|
|
82
|
+
output_tokens: int | None = None
|
|
83
|
+
cache_creation_input_tokens: int | None = None
|
|
84
|
+
cache_read_input_tokens: int | None = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def extract_usage_tokens(
|
|
88
|
+
usage: Usage | MessageDeltaUsage | None,
|
|
89
|
+
) -> UsageTokens:
|
|
90
|
+
if usage is None:
|
|
91
|
+
return UsageTokens()
|
|
92
|
+
|
|
93
|
+
input_tokens = usage.input_tokens
|
|
94
|
+
output_tokens = usage.output_tokens
|
|
95
|
+
cache_creation_input_tokens = usage.cache_creation_input_tokens
|
|
96
|
+
cache_read_input_tokens = usage.cache_read_input_tokens
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
input_tokens is None
|
|
100
|
+
and cache_creation_input_tokens is None
|
|
101
|
+
and cache_read_input_tokens is None
|
|
102
|
+
):
|
|
103
|
+
total_input_tokens = None
|
|
104
|
+
else:
|
|
105
|
+
total_input_tokens = (
|
|
106
|
+
(input_tokens or 0)
|
|
107
|
+
+ (cache_creation_input_tokens or 0)
|
|
108
|
+
+ (cache_read_input_tokens or 0)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return UsageTokens(
|
|
112
|
+
input_tokens=total_input_tokens,
|
|
113
|
+
output_tokens=output_tokens,
|
|
114
|
+
cache_creation_input_tokens=cache_creation_input_tokens,
|
|
115
|
+
cache_read_input_tokens=cache_read_input_tokens,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_input_messages(
|
|
120
|
+
messages: Iterable[MessageParam] | None,
|
|
121
|
+
) -> list[InputMessage]:
|
|
122
|
+
if messages is None:
|
|
123
|
+
return []
|
|
124
|
+
result: list[InputMessage] = []
|
|
125
|
+
for message in messages:
|
|
126
|
+
role = message["role"]
|
|
127
|
+
parts = convert_content_to_parts(message["content"])
|
|
128
|
+
result.append(InputMessage(role=role, parts=parts))
|
|
129
|
+
return result
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_system_instruction(
|
|
133
|
+
system: str | Iterable[TextBlockParam] | None,
|
|
134
|
+
) -> list[MessagePart]:
|
|
135
|
+
if system is None:
|
|
136
|
+
return []
|
|
137
|
+
return convert_content_to_parts(system)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_output_messages_from_message(
|
|
141
|
+
message: Message | None,
|
|
142
|
+
) -> list[OutputMessage]:
|
|
143
|
+
if message is None:
|
|
144
|
+
return []
|
|
145
|
+
|
|
146
|
+
parts = convert_content_to_parts(message.content)
|
|
147
|
+
finish_reason = normalize_finish_reason(message.stop_reason)
|
|
148
|
+
return [
|
|
149
|
+
OutputMessage(
|
|
150
|
+
role=message.role,
|
|
151
|
+
parts=parts,
|
|
152
|
+
finish_reason=finish_reason or "",
|
|
153
|
+
)
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def set_invocation_response_attributes(
|
|
158
|
+
invocation: LLMInvocation,
|
|
159
|
+
message: Message | None,
|
|
160
|
+
capture_content: bool,
|
|
161
|
+
) -> None:
|
|
162
|
+
if message is None:
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
invocation.response_model_name = message.model
|
|
166
|
+
invocation.response_id = message.id
|
|
167
|
+
|
|
168
|
+
finish_reason = normalize_finish_reason(message.stop_reason)
|
|
169
|
+
if finish_reason:
|
|
170
|
+
invocation.finish_reasons = [finish_reason]
|
|
171
|
+
|
|
172
|
+
tokens = extract_usage_tokens(message.usage)
|
|
173
|
+
invocation.input_tokens = tokens.input_tokens
|
|
174
|
+
invocation.output_tokens = tokens.output_tokens
|
|
175
|
+
if tokens.cache_creation_input_tokens is not None:
|
|
176
|
+
invocation.attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS] = (
|
|
177
|
+
tokens.cache_creation_input_tokens
|
|
178
|
+
)
|
|
179
|
+
if tokens.cache_read_input_tokens is not None:
|
|
180
|
+
invocation.attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = (
|
|
181
|
+
tokens.cache_read_input_tokens
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if capture_content:
|
|
185
|
+
invocation.output_messages = get_output_messages_from_message(message)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def extract_params( # pylint: disable=too-many-locals
|
|
189
|
+
*,
|
|
190
|
+
max_tokens: int | None = None,
|
|
191
|
+
messages: Iterable[MessageParam] | None = None,
|
|
192
|
+
model: str | None = None,
|
|
193
|
+
metadata: MetadataParam | None = None,
|
|
194
|
+
service_tier: str | None = None,
|
|
195
|
+
stop_sequences: Sequence[str] | None = None,
|
|
196
|
+
stream: bool | None = None,
|
|
197
|
+
system: str | Iterable[TextBlockParam] | None = None,
|
|
198
|
+
temperature: float | None = None,
|
|
199
|
+
thinking: ThinkingConfigParam | None = None,
|
|
200
|
+
tool_choice: ToolChoiceParam | None = None,
|
|
201
|
+
tools: Iterable[ToolUnionParam] | None = None,
|
|
202
|
+
top_k: int | None = None,
|
|
203
|
+
top_p: float | None = None,
|
|
204
|
+
extra_headers: Mapping[str, str] | None = None,
|
|
205
|
+
extra_query: Mapping[str, object] | None = None,
|
|
206
|
+
extra_body: object | None = None,
|
|
207
|
+
timeout: float | httpx.Timeout | None = None,
|
|
208
|
+
**_kwargs: object,
|
|
209
|
+
) -> MessageRequestParams:
|
|
210
|
+
return MessageRequestParams(
|
|
211
|
+
model=model,
|
|
212
|
+
max_tokens=max_tokens,
|
|
213
|
+
temperature=temperature,
|
|
214
|
+
top_p=top_p,
|
|
215
|
+
top_k=top_k,
|
|
216
|
+
stop_sequences=stop_sequences,
|
|
217
|
+
stream=stream,
|
|
218
|
+
messages=messages,
|
|
219
|
+
system=system,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _set_server_address_and_port(
|
|
224
|
+
client_instance: "Messages",
|
|
225
|
+
attributes: dict[str, AttributeValue | None],
|
|
226
|
+
) -> None:
|
|
227
|
+
base_url = client_instance._client.base_url
|
|
228
|
+
host = base_url.host
|
|
229
|
+
if host:
|
|
230
|
+
attributes[ServerAttributes.SERVER_ADDRESS] = host
|
|
231
|
+
|
|
232
|
+
port = base_url.port
|
|
233
|
+
if port and port != 443 and port > 0:
|
|
234
|
+
attributes[ServerAttributes.SERVER_PORT] = port
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def get_llm_request_attributes(
|
|
238
|
+
params: MessageRequestParams, client_instance: "Messages"
|
|
239
|
+
) -> dict[str, AttributeValue]:
|
|
240
|
+
attributes: dict[str, AttributeValue | None] = {
|
|
241
|
+
GenAIAttributes.GEN_AI_OPERATION_NAME: GenAIAttributes.GenAiOperationNameValues.CHAT.value,
|
|
242
|
+
GenAIAttributes.GEN_AI_SYSTEM: GenAIAttributes.GenAiSystemValues.ANTHROPIC.value, # pyright: ignore[reportDeprecated]
|
|
243
|
+
GenAIAttributes.GEN_AI_REQUEST_MODEL: params.model,
|
|
244
|
+
GenAIAttributes.GEN_AI_REQUEST_MAX_TOKENS: params.max_tokens,
|
|
245
|
+
GenAIAttributes.GEN_AI_REQUEST_TEMPERATURE: params.temperature,
|
|
246
|
+
GenAIAttributes.GEN_AI_REQUEST_TOP_P: params.top_p,
|
|
247
|
+
GenAIAttributes.GEN_AI_REQUEST_TOP_K: params.top_k,
|
|
248
|
+
GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES: params.stop_sequences,
|
|
249
|
+
}
|
|
250
|
+
_set_server_address_and_port(client_instance, attributes)
|
|
251
|
+
return {k: v for k, v in attributes.items() if v is not None}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright The OpenTelemetry Authors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
_instruments = ("anthropic >= 0.16.0",)
|