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.
Files changed (103) hide show
  1. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/PKG-INFO +46 -7
  2. traceable_agent-2.1.0b1/pyproject.toml +15 -0
  3. traceable_agent-2.1.0b1/setup.py +113 -0
  4. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/__init__.py +119 -0
  5. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/messages_extractors.py +251 -0
  6. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/package.py +15 -0
  7. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/patch.py +131 -0
  8. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/utils.py +243 -0
  9. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/version.py +15 -0
  10. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/anthropic/wrappers.py +435 -0
  11. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/__init__.py +168 -0
  12. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/instruments.py +52 -0
  13. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/package.py +18 -0
  14. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/patch.py +952 -0
  15. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/response_extractors.py +419 -0
  16. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/response_wrappers.py +467 -0
  17. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/utils.py +502 -0
  18. traceable_agent-2.1.0b1/src/opentelemetry/instrumentation/openai_v2/version.py +15 -0
  19. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/__init__.py +13 -0
  20. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_embedding_invocation.py +123 -0
  21. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_inference_invocation.py +358 -0
  22. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_invocation.py +140 -0
  23. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_tool_invocation.py +96 -0
  24. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_upload/__init__.py +90 -0
  25. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_upload/completion_hook.py +404 -0
  26. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/_workflow_invocation.py +115 -0
  27. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/completion_hook.py +126 -0
  28. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/environment_variables.py +71 -0
  29. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/handler.py +335 -0
  30. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/instruments.py +54 -0
  31. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/invocation.py +47 -0
  32. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/metrics.py +54 -0
  33. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/types.py +255 -0
  34. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/utils.py +143 -0
  35. traceable_agent-2.1.0b1/src/opentelemetry/util/genai/version.py +15 -0
  36. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceable_agent.egg-info/PKG-INFO +46 -7
  37. traceable_agent-2.1.0b1/src/traceable_agent.egg-info/SOURCES.txt +90 -0
  38. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceable_agent.egg-info/entry_points.txt +0 -1
  39. traceable_agent-2.1.0b1/src/traceable_agent.egg-info/requires.txt +39 -0
  40. traceable_agent-2.1.0b1/src/traceable_agent.egg-info/top_level.txt +2 -0
  41. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/agent.py +31 -6
  42. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/agent_init.py +60 -15
  43. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/config/config.py +15 -5
  44. traceable_agent-2.1.0b1/src/traceableai/config/config_pb2.py +112 -0
  45. traceable_agent-2.1.0b1/src/traceableai/config/default.py +77 -0
  46. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/config/environment.py +90 -15
  47. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/constants.py +1 -1
  48. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/custom_logger.py +3 -1
  49. traceable_agent-2.1.0b1/src/traceableai/excluded_by_attribute_span_processor.py +40 -0
  50. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/filter/__init__.py +7 -12
  51. traceable_agent-2.1.0b1/src/traceableai/filter/libtraceable_common.py +31 -0
  52. traceable_agent-2.1.0b1/src/traceableai/filter/registry.py +51 -0
  53. traceable_agent-2.1.0b1/src/traceableai/filter/traceable.py +356 -0
  54. traceable_agent-2.1.0b1/src/traceableai/gen_ai/__init__.py +7 -0
  55. traceable_agent-2.1.0b1/src/traceableai/gen_ai/exceptions.py +17 -0
  56. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/__init__.py +21 -62
  57. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/aiohttp/__init__.py +84 -48
  58. traceable_agent-2.1.0b1/src/traceableai/instrumentation/anthropic/__init__.py +379 -0
  59. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/aws_lambda/__init__.py +2 -1
  60. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/django/__init__.py +22 -9
  61. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/fast_api/__init__.py +47 -21
  62. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/flask/__init__.py +16 -12
  63. traceable_agent-2.1.0b1/src/traceableai/instrumentation/genai_env.py +71 -0
  64. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/grpc/__init__.py +6 -5
  65. traceable_agent-2.1.0b1/src/traceableai/instrumentation/httpx/__init__.py +73 -0
  66. traceable_agent-2.1.0b1/src/traceableai/instrumentation/httpx/utils.py +160 -0
  67. traceable_agent-2.1.0b1/src/traceableai/instrumentation/instrumentation_definitions.py +171 -0
  68. traceable_agent-2.1.0b1/src/traceableai/instrumentation/litellm/__init__.py +401 -0
  69. traceable_agent-2.1.0b1/src/traceableai/instrumentation/mcp/__init__.py +40 -0
  70. traceable_agent-2.1.0b1/src/traceableai/instrumentation/mcp/gen_ai_mirror.py +204 -0
  71. traceable_agent-2.1.0b1/src/traceableai/instrumentation/openai/__init__.py +340 -0
  72. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/requests/__init__.py +7 -1
  73. traceable_agent-2.1.0b1/src/traceableai/otlp_reporting.py +75 -0
  74. traceable_agent-2.1.0b1/src/traceableai/sampling_span_processor.py +63 -0
  75. traceable_agent-2.1.0b1/src/traceableai/span_attributes_processor.py +19 -0
  76. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/version.py +1 -1
  77. traceable-agent-2.0.0/pyproject.toml +0 -8
  78. traceable-agent-2.0.0/setup.py +0 -67
  79. traceable-agent-2.0.0/src/traceable_agent.egg-info/SOURCES.txt +0 -45
  80. traceable-agent-2.0.0/src/traceable_agent.egg-info/requires.txt +0 -28
  81. traceable-agent-2.0.0/src/traceable_agent.egg-info/top_level.txt +0 -1
  82. traceable-agent-2.0.0/src/traceableai/config/config_pb2.py +0 -91
  83. traceable-agent-2.0.0/src/traceableai/config/default.py +0 -54
  84. traceable-agent-2.0.0/src/traceableai/filter/filter.py +0 -18
  85. traceable-agent-2.0.0/src/traceableai/filter/registry.py +0 -42
  86. traceable-agent-2.0.0/src/traceableai/filter/traceable.py +0 -260
  87. traceable-agent-2.0.0/src/traceableai/instrumentation/boto/__init__.py +0 -11
  88. traceable-agent-2.0.0/src/traceableai/instrumentation/instrumentation_definitions.py +0 -99
  89. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/MANIFEST.in +0 -0
  90. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/README.md +0 -0
  91. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/setup.cfg +0 -0
  92. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceable_agent.egg-info/dependency_links.txt +0 -0
  93. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/__init__.py +0 -0
  94. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/autoinstrumentation/__init__.py +0 -0
  95. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/autoinstrumentation/sitecustomize.py +0 -0
  96. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/autoinstrumentation/wrapper.py +0 -0
  97. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/config/__init__.py +0 -0
  98. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/env.py +0 -0
  99. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/botocore/__init__.py +0 -0
  100. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/django/django_auto_instrumentation_compat.py +0 -0
  101. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/fast_api/fast_api_auto_instrumentation_compat.py +0 -0
  102. {traceable-agent-2.0.0 → traceable_agent-2.1.0b1}/src/traceableai/instrumentation/mysql/__init__.py +0 -0
  103. {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
1
+ Metadata-Version: 2.4
2
2
  Name: traceable-agent
3
- Version: 2.0.0
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.8
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",)