sentry-sdk 2.40.0__tar.gz → 2.41.0__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.

Potentially problematic release.


This version of sentry-sdk might be problematic. Click here for more details.

Files changed (208) hide show
  1. {sentry_sdk-2.40.0/sentry_sdk.egg-info → sentry_sdk-2.41.0}/PKG-INFO +1 -1
  2. sentry_sdk-2.41.0/sentry_sdk/_metrics.py +81 -0
  3. sentry_sdk-2.41.0/sentry_sdk/_metrics_batcher.py +156 -0
  4. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/_types.py +27 -22
  5. sentry_sdk-2.41.0/sentry_sdk/ai/__init__.py +7 -0
  6. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/ai/utils.py +48 -0
  7. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/client.py +81 -30
  8. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/consts.py +4 -8
  9. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/envelope.py +3 -3
  10. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/anthropic.py +10 -2
  11. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/langchain.py +29 -4
  12. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/langgraph.py +5 -3
  13. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/logging.py +1 -1
  14. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/loguru.py +1 -1
  15. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai.py +3 -2
  16. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +10 -2
  17. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/utils.py +35 -18
  18. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/threading.py +52 -8
  19. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/logger.py +1 -1
  20. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/tracing.py +0 -26
  21. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/transport.py +1 -17
  22. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/types.py +3 -0
  23. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/utils.py +17 -1
  24. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0/sentry_sdk.egg-info}/PKG-INFO +1 -1
  25. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk.egg-info/SOURCES.txt +2 -1
  26. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/setup.py +1 -1
  27. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_envelope.py +0 -1
  28. sentry_sdk-2.41.0/tests/test_metrics.py +208 -0
  29. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_transport.py +0 -111
  30. sentry_sdk-2.40.0/sentry_sdk/integrations/redis/modules/__init__.py +0 -0
  31. sentry_sdk-2.40.0/sentry_sdk/metrics.py +0 -971
  32. sentry_sdk-2.40.0/tests/test_metrics.py +0 -971
  33. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/LICENSE +0 -0
  34. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/MANIFEST.in +0 -0
  35. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/README.md +0 -0
  36. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/pyproject.toml +0 -0
  37. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/__init__.py +0 -0
  38. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/_compat.py +0 -0
  39. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/_init_implementation.py +0 -0
  40. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/_log_batcher.py +0 -0
  41. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/_lru_cache.py +0 -0
  42. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/_queue.py +0 -0
  43. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/_werkzeug.py +0 -0
  44. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/ai/monitoring.py +0 -0
  45. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/api.py +0 -0
  46. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/attachments.py +0 -0
  47. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/crons/__init__.py +0 -0
  48. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/crons/api.py +0 -0
  49. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/crons/consts.py +0 -0
  50. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/crons/decorator.py +0 -0
  51. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/debug.py +0 -0
  52. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/feature_flags.py +0 -0
  53. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/hub.py +0 -0
  54. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/__init__.py +0 -0
  55. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/_asgi_common.py +0 -0
  56. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/_wsgi_common.py +0 -0
  57. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/aiohttp.py +0 -0
  58. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/argv.py +0 -0
  59. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/ariadne.py +0 -0
  60. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/arq.py +0 -0
  61. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/asgi.py +0 -0
  62. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/asyncio.py +0 -0
  63. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/asyncpg.py +0 -0
  64. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/atexit.py +0 -0
  65. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/aws_lambda.py +0 -0
  66. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/beam.py +0 -0
  67. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/boto3.py +0 -0
  68. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/bottle.py +0 -0
  69. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/celery/__init__.py +0 -0
  70. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/celery/beat.py +0 -0
  71. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/celery/utils.py +0 -0
  72. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/chalice.py +0 -0
  73. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/clickhouse_driver.py +0 -0
  74. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/cloud_resource_context.py +0 -0
  75. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/cohere.py +0 -0
  76. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/dedupe.py +0 -0
  77. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/__init__.py +0 -0
  78. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/asgi.py +0 -0
  79. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/caching.py +0 -0
  80. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/middleware.py +0 -0
  81. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/signals_handlers.py +0 -0
  82. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/templates.py +0 -0
  83. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/transactions.py +0 -0
  84. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/django/views.py +0 -0
  85. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/dramatiq.py +0 -0
  86. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/excepthook.py +0 -0
  87. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/executing.py +0 -0
  88. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/falcon.py +0 -0
  89. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/fastapi.py +0 -0
  90. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/flask.py +0 -0
  91. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/gcp.py +0 -0
  92. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/gnu_backtrace.py +0 -0
  93. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/gql.py +0 -0
  94. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/graphene.py +0 -0
  95. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/grpc/__init__.py +0 -0
  96. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/grpc/aio/__init__.py +0 -0
  97. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/grpc/aio/client.py +0 -0
  98. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/grpc/aio/server.py +0 -0
  99. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/grpc/client.py +0 -0
  100. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/grpc/consts.py +0 -0
  101. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/grpc/server.py +0 -0
  102. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/httpx.py +0 -0
  103. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/huey.py +0 -0
  104. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/huggingface_hub.py +0 -0
  105. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/launchdarkly.py +0 -0
  106. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/litellm.py +0 -0
  107. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/litestar.py +0 -0
  108. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/modules.py +0 -0
  109. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/__init__.py +0 -0
  110. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/consts.py +0 -0
  111. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/patches/__init__.py +0 -0
  112. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/patches/agent_run.py +0 -0
  113. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/patches/models.py +0 -0
  114. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/patches/runner.py +0 -0
  115. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/patches/tools.py +0 -0
  116. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/spans/__init__.py +0 -0
  117. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +0 -0
  118. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/spans/ai_client.py +0 -0
  119. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/spans/execute_tool.py +0 -0
  120. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openai_agents/spans/handoff.py +0 -0
  121. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/openfeature.py +0 -0
  122. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/opentelemetry/__init__.py +0 -0
  123. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/opentelemetry/consts.py +0 -0
  124. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/opentelemetry/integration.py +0 -0
  125. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/opentelemetry/propagator.py +0 -0
  126. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/opentelemetry/span_processor.py +0 -0
  127. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/pure_eval.py +0 -0
  128. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/pymongo.py +0 -0
  129. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/pyramid.py +0 -0
  130. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/quart.py +0 -0
  131. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/ray.py +0 -0
  132. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/__init__.py +0 -0
  133. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/_async_common.py +0 -0
  134. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/_sync_common.py +0 -0
  135. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/consts.py +0 -0
  136. {sentry_sdk-2.40.0/sentry_sdk/ai → sentry_sdk-2.41.0/sentry_sdk/integrations/redis/modules}/__init__.py +0 -0
  137. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/modules/caches.py +0 -0
  138. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/modules/queries.py +0 -0
  139. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/rb.py +0 -0
  140. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/redis.py +0 -0
  141. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/redis_cluster.py +0 -0
  142. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +0 -0
  143. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/redis/utils.py +0 -0
  144. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/rq.py +0 -0
  145. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/rust_tracing.py +0 -0
  146. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/sanic.py +0 -0
  147. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/serverless.py +0 -0
  148. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/socket.py +0 -0
  149. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/spark/__init__.py +0 -0
  150. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/spark/spark_driver.py +0 -0
  151. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/spark/spark_worker.py +0 -0
  152. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/sqlalchemy.py +0 -0
  153. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/starlette.py +0 -0
  154. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/starlite.py +0 -0
  155. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/statsig.py +0 -0
  156. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/stdlib.py +0 -0
  157. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/strawberry.py +0 -0
  158. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/sys_exit.py +0 -0
  159. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/tornado.py +0 -0
  160. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/trytond.py +0 -0
  161. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/typer.py +0 -0
  162. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/unleash.py +0 -0
  163. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/unraisablehook.py +0 -0
  164. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/integrations/wsgi.py +0 -0
  165. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/monitor.py +0 -0
  166. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/profiler/__init__.py +0 -0
  167. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/profiler/continuous_profiler.py +0 -0
  168. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/profiler/transaction_profiler.py +0 -0
  169. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/profiler/utils.py +0 -0
  170. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/py.typed +0 -0
  171. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/scope.py +0 -0
  172. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/scrubber.py +0 -0
  173. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/serializer.py +0 -0
  174. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/session.py +0 -0
  175. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/sessions.py +0 -0
  176. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/spotlight.py +0 -0
  177. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/tracing_utils.py +0 -0
  178. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk/worker.py +0 -0
  179. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk.egg-info/dependency_links.txt +0 -0
  180. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk.egg-info/entry_points.txt +0 -0
  181. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk.egg-info/not-zip-safe +0 -0
  182. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk.egg-info/requires.txt +0 -0
  183. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/sentry_sdk.egg-info/top_level.txt +0 -0
  184. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/setup.cfg +0 -0
  185. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_ai_monitoring.py +0 -0
  186. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_api.py +0 -0
  187. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_basics.py +0 -0
  188. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_client.py +0 -0
  189. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_conftest.py +0 -0
  190. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_crons.py +0 -0
  191. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_dsc.py +0 -0
  192. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_exceptiongroup.py +0 -0
  193. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_feature_flags.py +0 -0
  194. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_full_stack_frames.py +0 -0
  195. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_gevent.py +0 -0
  196. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_import.py +0 -0
  197. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_logs.py +0 -0
  198. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_lru_cache.py +0 -0
  199. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_monitor.py +0 -0
  200. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_propagationcontext.py +0 -0
  201. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_scope.py +0 -0
  202. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_scrubber.py +0 -0
  203. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_serializer.py +0 -0
  204. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_sessions.py +0 -0
  205. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_spotlight.py +0 -0
  206. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_tracing_utils.py +0 -0
  207. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_types.py +0 -0
  208. {sentry_sdk-2.40.0 → sentry_sdk-2.41.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sentry-sdk
3
- Version: 2.40.0
3
+ Version: 2.41.0
4
4
  Summary: Python client for Sentry (https://sentry.io)
5
5
  Home-page: https://github.com/getsentry/sentry-python
6
6
  Author: Sentry Team and Contributors
@@ -0,0 +1,81 @@
1
+ """
2
+ NOTE: This file contains experimental code that may be changed or removed at any
3
+ time without prior notice.
4
+ """
5
+
6
+ import time
7
+ from typing import Any, Optional, TYPE_CHECKING, Union
8
+
9
+ import sentry_sdk
10
+ from sentry_sdk.utils import safe_repr
11
+
12
+ if TYPE_CHECKING:
13
+ from sentry_sdk._types import Metric, MetricType
14
+
15
+
16
+ def _capture_metric(
17
+ name, # type: str
18
+ metric_type, # type: MetricType
19
+ value, # type: float
20
+ unit=None, # type: Optional[str]
21
+ attributes=None, # type: Optional[dict[str, Any]]
22
+ ):
23
+ # type: (...) -> None
24
+ client = sentry_sdk.get_client()
25
+
26
+ attrs = {} # type: dict[str, Union[str, bool, float, int]]
27
+ if attributes:
28
+ for k, v in attributes.items():
29
+ attrs[k] = (
30
+ v
31
+ if (
32
+ isinstance(v, str)
33
+ or isinstance(v, int)
34
+ or isinstance(v, bool)
35
+ or isinstance(v, float)
36
+ )
37
+ else safe_repr(v)
38
+ )
39
+
40
+ metric = {
41
+ "timestamp": time.time(),
42
+ "trace_id": None,
43
+ "span_id": None,
44
+ "name": name,
45
+ "type": metric_type,
46
+ "value": float(value),
47
+ "unit": unit,
48
+ "attributes": attrs,
49
+ } # type: Metric
50
+
51
+ client._capture_metric(metric)
52
+
53
+
54
+ def count(
55
+ name, # type: str
56
+ value, # type: float
57
+ unit=None, # type: Optional[str]
58
+ attributes=None, # type: Optional[dict[str, Any]]
59
+ ):
60
+ # type: (...) -> None
61
+ _capture_metric(name, "counter", value, unit, attributes)
62
+
63
+
64
+ def gauge(
65
+ name, # type: str
66
+ value, # type: float
67
+ unit=None, # type: Optional[str]
68
+ attributes=None, # type: Optional[dict[str, Any]]
69
+ ):
70
+ # type: (...) -> None
71
+ _capture_metric(name, "gauge", value, unit, attributes)
72
+
73
+
74
+ def distribution(
75
+ name, # type: str
76
+ value, # type: float
77
+ unit=None, # type: Optional[str]
78
+ attributes=None, # type: Optional[dict[str, Any]]
79
+ ):
80
+ # type: (...) -> None
81
+ _capture_metric(name, "distribution", value, unit, attributes)
@@ -0,0 +1,156 @@
1
+ import os
2
+ import random
3
+ import threading
4
+ from datetime import datetime, timezone
5
+ from typing import Optional, List, Callable, TYPE_CHECKING, Any, Union
6
+
7
+ from sentry_sdk.utils import format_timestamp, safe_repr
8
+ from sentry_sdk.envelope import Envelope, Item, PayloadRef
9
+
10
+ if TYPE_CHECKING:
11
+ from sentry_sdk._types import Metric
12
+
13
+
14
+ class MetricsBatcher:
15
+ MAX_METRICS_BEFORE_FLUSH = 100
16
+ FLUSH_WAIT_TIME = 5.0
17
+
18
+ def __init__(
19
+ self,
20
+ capture_func, # type: Callable[[Envelope], None]
21
+ ):
22
+ # type: (...) -> None
23
+ self._metric_buffer = [] # type: List[Metric]
24
+ self._capture_func = capture_func
25
+ self._running = True
26
+ self._lock = threading.Lock()
27
+
28
+ self._flush_event = threading.Event() # type: threading.Event
29
+
30
+ self._flusher = None # type: Optional[threading.Thread]
31
+ self._flusher_pid = None # type: Optional[int]
32
+
33
+ def _ensure_thread(self):
34
+ # type: (...) -> bool
35
+ if not self._running:
36
+ return False
37
+
38
+ pid = os.getpid()
39
+ if self._flusher_pid == pid:
40
+ return True
41
+
42
+ with self._lock:
43
+ if self._flusher_pid == pid:
44
+ return True
45
+
46
+ self._flusher_pid = pid
47
+
48
+ self._flusher = threading.Thread(target=self._flush_loop)
49
+ self._flusher.daemon = True
50
+
51
+ try:
52
+ self._flusher.start()
53
+ except RuntimeError:
54
+ self._running = False
55
+ return False
56
+
57
+ return True
58
+
59
+ def _flush_loop(self):
60
+ # type: (...) -> None
61
+ while self._running:
62
+ self._flush_event.wait(self.FLUSH_WAIT_TIME + random.random())
63
+ self._flush_event.clear()
64
+ self._flush()
65
+
66
+ def add(
67
+ self,
68
+ metric, # type: Metric
69
+ ):
70
+ # type: (...) -> None
71
+ if not self._ensure_thread() or self._flusher is None:
72
+ return None
73
+
74
+ with self._lock:
75
+ self._metric_buffer.append(metric)
76
+ if len(self._metric_buffer) >= self.MAX_METRICS_BEFORE_FLUSH:
77
+ self._flush_event.set()
78
+
79
+ def kill(self):
80
+ # type: (...) -> None
81
+ if self._flusher is None:
82
+ return
83
+
84
+ self._running = False
85
+ self._flush_event.set()
86
+ self._flusher = None
87
+
88
+ def flush(self):
89
+ # type: (...) -> None
90
+ self._flush()
91
+
92
+ @staticmethod
93
+ def _metric_to_transport_format(metric):
94
+ # type: (Metric) -> Any
95
+ def format_attribute(val):
96
+ # type: (Union[int, float, str, bool]) -> Any
97
+ if isinstance(val, bool):
98
+ return {"value": val, "type": "boolean"}
99
+ if isinstance(val, int):
100
+ return {"value": val, "type": "integer"}
101
+ if isinstance(val, float):
102
+ return {"value": val, "type": "double"}
103
+ if isinstance(val, str):
104
+ return {"value": val, "type": "string"}
105
+ return {"value": safe_repr(val), "type": "string"}
106
+
107
+ res = {
108
+ "timestamp": metric["timestamp"],
109
+ "trace_id": metric["trace_id"],
110
+ "name": metric["name"],
111
+ "type": metric["type"],
112
+ "value": metric["value"],
113
+ "attributes": {
114
+ k: format_attribute(v) for (k, v) in metric["attributes"].items()
115
+ },
116
+ }
117
+
118
+ if metric.get("span_id") is not None:
119
+ res["span_id"] = metric["span_id"]
120
+
121
+ if metric.get("unit") is not None:
122
+ res["unit"] = metric["unit"]
123
+
124
+ return res
125
+
126
+ def _flush(self):
127
+ # type: (...) -> Optional[Envelope]
128
+
129
+ envelope = Envelope(
130
+ headers={"sent_at": format_timestamp(datetime.now(timezone.utc))}
131
+ )
132
+ with self._lock:
133
+ if len(self._metric_buffer) == 0:
134
+ return None
135
+
136
+ envelope.add_item(
137
+ Item(
138
+ type="trace_metric",
139
+ content_type="application/vnd.sentry.items.trace-metric+json",
140
+ headers={
141
+ "item_count": len(self._metric_buffer),
142
+ },
143
+ payload=PayloadRef(
144
+ json={
145
+ "items": [
146
+ self._metric_to_transport_format(metric)
147
+ for metric in self._metric_buffer
148
+ ]
149
+ }
150
+ ),
151
+ )
152
+ )
153
+ self._metric_buffer.clear()
154
+
155
+ self._capture_func(envelope)
156
+ return envelope
@@ -210,7 +210,6 @@ if TYPE_CHECKING:
210
210
  "type": Literal["check_in", "transaction"],
211
211
  "user": dict[str, object],
212
212
  "_dropped_spans": int,
213
- "_metrics_summary": dict[str, object],
214
213
  },
215
214
  total=False,
216
215
  )
@@ -235,6 +234,32 @@ if TYPE_CHECKING:
235
234
  },
236
235
  )
237
236
 
237
+ MetricType = Literal["counter", "gauge", "distribution"]
238
+
239
+ MetricAttributeValue = TypedDict(
240
+ "MetricAttributeValue",
241
+ {
242
+ "value": Union[str, bool, float, int],
243
+ "type": Literal["string", "boolean", "double", "integer"],
244
+ },
245
+ )
246
+
247
+ Metric = TypedDict(
248
+ "Metric",
249
+ {
250
+ "timestamp": float,
251
+ "trace_id": Optional[str],
252
+ "span_id": Optional[str],
253
+ "name": str,
254
+ "type": MetricType,
255
+ "value": float,
256
+ "unit": Optional[str],
257
+ "attributes": dict[str, str | bool | float | int],
258
+ },
259
+ )
260
+
261
+ MetricProcessor = Callable[[Metric, Hint], Optional[Metric]]
262
+
238
263
  # TODO: Make a proper type definition for this (PRs welcome!)
239
264
  Breadcrumb = Dict[str, Any]
240
265
 
@@ -266,36 +291,16 @@ if TYPE_CHECKING:
266
291
  "internal",
267
292
  "profile",
268
293
  "profile_chunk",
269
- "metric_bucket",
270
294
  "monitor",
271
295
  "span",
272
296
  "log_item",
297
+ "trace_metric",
273
298
  ]
274
299
  SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
275
300
 
276
301
  ContinuousProfilerMode = Literal["thread", "gevent", "unknown"]
277
302
  ProfilerMode = Union[ContinuousProfilerMode, Literal["sleep"]]
278
303
 
279
- # Type of the metric.
280
- MetricType = Literal["d", "s", "g", "c"]
281
-
282
- # Value of the metric.
283
- MetricValue = Union[int, float, str]
284
-
285
- # Internal representation of tags as a tuple of tuples (this is done in order to allow for the same key to exist
286
- # multiple times).
287
- MetricTagsInternal = Tuple[Tuple[str, str], ...]
288
-
289
- # External representation of tags as a dictionary.
290
- MetricTagValue = Union[str, int, float, None]
291
- MetricTags = Mapping[str, MetricTagValue]
292
-
293
- # Value inside the generator for the metric value.
294
- FlushedMetricValue = Union[int, float]
295
-
296
- BucketKey = Tuple[MetricType, str, MeasurementUnit, MetricTagsInternal]
297
- MetricMetaKey = Tuple[MetricType, str, MeasurementUnit]
298
-
299
304
  MonitorConfigScheduleType = Literal["crontab", "interval"]
300
305
  MonitorConfigScheduleUnit = Literal[
301
306
  "year",
@@ -0,0 +1,7 @@
1
+ from .utils import (
2
+ set_data_normalized,
3
+ GEN_AI_MESSAGE_ROLE_MAPPING,
4
+ GEN_AI_MESSAGE_ROLE_REVERSE_MAPPING,
5
+ normalize_message_role,
6
+ normalize_message_roles,
7
+ ) # noqa: F401
@@ -10,6 +10,26 @@ import sentry_sdk
10
10
  from sentry_sdk.utils import logger
11
11
 
12
12
 
13
+ class GEN_AI_ALLOWED_MESSAGE_ROLES:
14
+ SYSTEM = "system"
15
+ USER = "user"
16
+ ASSISTANT = "assistant"
17
+ TOOL = "tool"
18
+
19
+
20
+ GEN_AI_MESSAGE_ROLE_REVERSE_MAPPING = {
21
+ GEN_AI_ALLOWED_MESSAGE_ROLES.SYSTEM: ["system"],
22
+ GEN_AI_ALLOWED_MESSAGE_ROLES.USER: ["user", "human"],
23
+ GEN_AI_ALLOWED_MESSAGE_ROLES.ASSISTANT: ["assistant", "ai"],
24
+ GEN_AI_ALLOWED_MESSAGE_ROLES.TOOL: ["tool", "tool_call"],
25
+ }
26
+
27
+ GEN_AI_MESSAGE_ROLE_MAPPING = {}
28
+ for target_role, source_roles in GEN_AI_MESSAGE_ROLE_REVERSE_MAPPING.items():
29
+ for source_role in source_roles:
30
+ GEN_AI_MESSAGE_ROLE_MAPPING[source_role] = target_role
31
+
32
+
13
33
  def _normalize_data(data, unpack=True):
14
34
  # type: (Any, bool) -> Any
15
35
  # convert pydantic data (e.g. OpenAI v1+) to json compatible format
@@ -40,6 +60,34 @@ def set_data_normalized(span, key, value, unpack=True):
40
60
  span.set_data(key, json.dumps(normalized))
41
61
 
42
62
 
63
+ def normalize_message_role(role):
64
+ # type: (str) -> str
65
+ """
66
+ Normalize a message role to one of the 4 allowed gen_ai role values.
67
+ Maps "ai" -> "assistant" and keeps other standard roles unchanged.
68
+ """
69
+ return GEN_AI_MESSAGE_ROLE_MAPPING.get(role, role)
70
+
71
+
72
+ def normalize_message_roles(messages):
73
+ # type: (list[dict[str, Any]]) -> list[dict[str, Any]]
74
+ """
75
+ Normalize roles in a list of messages to use standard gen_ai role values.
76
+ Creates a deep copy to avoid modifying the original messages.
77
+ """
78
+ normalized_messages = []
79
+ for message in messages:
80
+ if not isinstance(message, dict):
81
+ normalized_messages.append(message)
82
+ continue
83
+ normalized_message = message.copy()
84
+ if "role" in message:
85
+ normalized_message["role"] = normalize_message_role(message["role"])
86
+ normalized_messages.append(normalized_message)
87
+
88
+ return normalized_messages
89
+
90
+
43
91
  def get_start_span_function():
44
92
  # type: () -> Callable[..., Any]
45
93
  current_span = sentry_sdk.get_current_span()
@@ -24,7 +24,9 @@ from sentry_sdk.utils import (
24
24
  is_gevent,
25
25
  logger,
26
26
  get_before_send_log,
27
+ get_before_send_metric,
27
28
  has_logs_enabled,
29
+ has_metrics_enabled,
28
30
  )
29
31
  from sentry_sdk.serializer import serialize
30
32
  from sentry_sdk.tracing import trace
@@ -59,14 +61,14 @@ if TYPE_CHECKING:
59
61
  from typing import Union
60
62
  from typing import TypeVar
61
63
 
62
- from sentry_sdk._types import Event, Hint, SDKInfo, Log
64
+ from sentry_sdk._types import Event, Hint, SDKInfo, Log, Metric
63
65
  from sentry_sdk.integrations import Integration
64
- from sentry_sdk.metrics import MetricsAggregator
65
66
  from sentry_sdk.scope import Scope
66
67
  from sentry_sdk.session import Session
67
68
  from sentry_sdk.spotlight import SpotlightClient
68
69
  from sentry_sdk.transport import Transport
69
70
  from sentry_sdk._log_batcher import LogBatcher
71
+ from sentry_sdk._metrics_batcher import MetricsBatcher
70
72
 
71
73
  I = TypeVar("I", bound=Integration) # noqa: E741
72
74
 
@@ -182,8 +184,8 @@ class BaseClient:
182
184
 
183
185
  self.transport = None # type: Optional[Transport]
184
186
  self.monitor = None # type: Optional[Monitor]
185
- self.metrics_aggregator = None # type: Optional[MetricsAggregator]
186
187
  self.log_batcher = None # type: Optional[LogBatcher]
188
+ self.metrics_batcher = None # type: Optional[MetricsBatcher]
187
189
 
188
190
  def __getstate__(self, *args, **kwargs):
189
191
  # type: (*Any, **Any) -> Any
@@ -215,10 +217,14 @@ class BaseClient:
215
217
  # type: (*Any, **Any) -> Optional[str]
216
218
  return None
217
219
 
218
- def _capture_experimental_log(self, log):
220
+ def _capture_log(self, log):
219
221
  # type: (Log) -> None
220
222
  pass
221
223
 
224
+ def _capture_metric(self, metric):
225
+ # type: (Metric) -> None
226
+ pass
227
+
222
228
  def capture_session(self, *args, **kwargs):
223
229
  # type: (*Any, **Any) -> None
224
230
  return None
@@ -361,26 +367,6 @@ class _Client(BaseClient):
361
367
 
362
368
  self.session_flusher = SessionFlusher(capture_func=_capture_envelope)
363
369
 
364
- self.metrics_aggregator = None # type: Optional[MetricsAggregator]
365
- experiments = self.options.get("_experiments", {})
366
- if experiments.get("enable_metrics", True):
367
- # Context vars are not working correctly on Python <=3.6
368
- # with gevent.
369
- metrics_supported = not is_gevent() or PY37
370
- if metrics_supported:
371
- from sentry_sdk.metrics import MetricsAggregator
372
-
373
- self.metrics_aggregator = MetricsAggregator(
374
- capture_func=_capture_envelope,
375
- enable_code_locations=bool(
376
- experiments.get("metric_code_locations", True)
377
- ),
378
- )
379
- else:
380
- logger.info(
381
- "Metrics not supported on Python 3.6 and lower with gevent."
382
- )
383
-
384
370
  self.log_batcher = None
385
371
 
386
372
  if has_logs_enabled(self.options):
@@ -388,6 +374,13 @@ class _Client(BaseClient):
388
374
 
389
375
  self.log_batcher = LogBatcher(capture_func=_capture_envelope)
390
376
 
377
+ self.metrics_batcher = None
378
+
379
+ if has_metrics_enabled(self.options):
380
+ from sentry_sdk._metrics_batcher import MetricsBatcher
381
+
382
+ self.metrics_batcher = MetricsBatcher(capture_func=_capture_envelope)
383
+
391
384
  max_request_body_size = ("always", "never", "small", "medium")
392
385
  if self.options["max_request_body_size"] not in max_request_body_size:
393
386
  raise ValueError(
@@ -467,7 +460,6 @@ class _Client(BaseClient):
467
460
 
468
461
  if (
469
462
  self.monitor
470
- or self.metrics_aggregator
471
463
  or self.log_batcher
472
464
  or has_profiling_enabled(self.options)
473
465
  or isinstance(self.transport, BaseHttpTransport)
@@ -900,7 +892,7 @@ class _Client(BaseClient):
900
892
 
901
893
  return return_value
902
894
 
903
- def _capture_experimental_log(self, log):
895
+ def _capture_log(self, log):
904
896
  # type: (Optional[Log]) -> None
905
897
  if not has_logs_enabled(self.options) or log is None:
906
898
  return
@@ -967,6 +959,65 @@ class _Client(BaseClient):
967
959
  if self.log_batcher:
968
960
  self.log_batcher.add(log)
969
961
 
962
+ def _capture_metric(self, metric):
963
+ # type: (Optional[Metric]) -> None
964
+ if not has_metrics_enabled(self.options) or metric is None:
965
+ return
966
+
967
+ isolation_scope = sentry_sdk.get_isolation_scope()
968
+
969
+ metric["attributes"]["sentry.sdk.name"] = SDK_INFO["name"]
970
+ metric["attributes"]["sentry.sdk.version"] = SDK_INFO["version"]
971
+
972
+ environment = self.options.get("environment")
973
+ if environment is not None and "sentry.environment" not in metric["attributes"]:
974
+ metric["attributes"]["sentry.environment"] = environment
975
+
976
+ release = self.options.get("release")
977
+ if release is not None and "sentry.release" not in metric["attributes"]:
978
+ metric["attributes"]["sentry.release"] = release
979
+
980
+ span = sentry_sdk.get_current_span()
981
+ metric["trace_id"] = "00000000-0000-0000-0000-000000000000"
982
+
983
+ if span:
984
+ metric["trace_id"] = span.trace_id
985
+ metric["span_id"] = span.span_id
986
+ else:
987
+ propagation_context = isolation_scope.get_active_propagation_context()
988
+ if propagation_context and propagation_context.trace_id:
989
+ metric["trace_id"] = propagation_context.trace_id
990
+
991
+ if isolation_scope._user is not None:
992
+ for metric_attribute, user_attribute in (
993
+ ("user.id", "id"),
994
+ ("user.name", "username"),
995
+ ("user.email", "email"),
996
+ ):
997
+ if (
998
+ user_attribute in isolation_scope._user
999
+ and metric_attribute not in metric["attributes"]
1000
+ ):
1001
+ metric["attributes"][metric_attribute] = isolation_scope._user[
1002
+ user_attribute
1003
+ ]
1004
+
1005
+ debug = self.options.get("debug", False)
1006
+ if debug:
1007
+ logger.debug(
1008
+ f"[Sentry Metrics] [{metric.get('type')}] {metric.get('name')}: {metric.get('value')}"
1009
+ )
1010
+
1011
+ before_send_metric = get_before_send_metric(self.options)
1012
+ if before_send_metric is not None:
1013
+ metric = before_send_metric(metric, {})
1014
+
1015
+ if metric is None:
1016
+ return
1017
+
1018
+ if self.metrics_batcher:
1019
+ self.metrics_batcher.add(metric)
1020
+
970
1021
  def capture_session(
971
1022
  self,
972
1023
  session, # type: Session
@@ -1019,10 +1070,10 @@ class _Client(BaseClient):
1019
1070
  if self.transport is not None:
1020
1071
  self.flush(timeout=timeout, callback=callback)
1021
1072
  self.session_flusher.kill()
1022
- if self.metrics_aggregator is not None:
1023
- self.metrics_aggregator.kill()
1024
1073
  if self.log_batcher is not None:
1025
1074
  self.log_batcher.kill()
1075
+ if self.metrics_batcher is not None:
1076
+ self.metrics_batcher.kill()
1026
1077
  if self.monitor:
1027
1078
  self.monitor.kill()
1028
1079
  self.transport.kill()
@@ -1045,10 +1096,10 @@ class _Client(BaseClient):
1045
1096
  if timeout is None:
1046
1097
  timeout = self.options["shutdown_timeout"]
1047
1098
  self.session_flusher.flush()
1048
- if self.metrics_aggregator is not None:
1049
- self.metrics_aggregator.flush()
1050
1099
  if self.log_batcher is not None:
1051
1100
  self.log_batcher.flush()
1101
+ if self.metrics_batcher is not None:
1102
+ self.metrics_batcher.flush()
1052
1103
  self.transport.flush(timeout=timeout, callback=callback)
1053
1104
 
1054
1105
  def __enter__(self):
@@ -52,11 +52,10 @@ if TYPE_CHECKING:
52
52
  Hint,
53
53
  Log,
54
54
  MeasurementUnit,
55
+ Metric,
55
56
  ProfilerMode,
56
57
  TracesSampler,
57
58
  TransactionProcessor,
58
- MetricTags,
59
- MetricValue,
60
59
  )
61
60
 
62
61
  # Experiments are feature flags to enable and disable certain unstable SDK
@@ -77,13 +76,10 @@ if TYPE_CHECKING:
77
76
  "transport_compression_algo": Optional[CompressionAlgo],
78
77
  "transport_num_pools": Optional[int],
79
78
  "transport_http2": Optional[bool],
80
- "enable_metrics": Optional[bool],
81
- "before_emit_metric": Optional[
82
- Callable[[str, MetricValue, MeasurementUnit, MetricTags], bool]
83
- ],
84
- "metric_code_locations": Optional[bool],
85
79
  "enable_logs": Optional[bool],
86
80
  "before_send_log": Optional[Callable[[Log, Hint], Optional[Log]]],
81
+ "enable_metrics": Optional[bool],
82
+ "before_send_metric": Optional[Callable[[Metric, Hint], Optional[Metric]]],
87
83
  },
88
84
  total=False,
89
85
  )
@@ -1343,4 +1339,4 @@ DEFAULT_OPTIONS = _get_default_options()
1343
1339
  del _get_default_options
1344
1340
 
1345
1341
 
1346
- VERSION = "2.40.0"
1342
+ VERSION = "2.41.0"
@@ -285,14 +285,14 @@ class Item:
285
285
  return "error"
286
286
  elif ty == "log":
287
287
  return "log_item"
288
+ elif ty == "trace_metric":
289
+ return "trace_metric"
288
290
  elif ty == "client_report":
289
291
  return "internal"
290
292
  elif ty == "profile":
291
293
  return "profile"
292
294
  elif ty == "profile_chunk":
293
295
  return "profile_chunk"
294
- elif ty == "statsd":
295
- return "metric_bucket"
296
296
  elif ty == "check_in":
297
297
  return "monitor"
298
298
  else:
@@ -354,7 +354,7 @@ class Item:
354
354
  # if no length was specified we need to read up to the end of line
355
355
  # and remove it (if it is present, i.e. not the very last char in an eof terminated envelope)
356
356
  payload = f.readline().rstrip(b"\n")
357
- if headers.get("type") in ("event", "transaction", "metric_buckets"):
357
+ if headers.get("type") in ("event", "transaction"):
358
358
  rv = cls(headers=headers, payload=PayloadRef(json=parse_json(payload)))
359
359
  else:
360
360
  rv = cls(headers=headers, payload=payload)
@@ -3,7 +3,11 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  import sentry_sdk
5
5
  from sentry_sdk.ai.monitoring import record_token_usage
6
- from sentry_sdk.ai.utils import set_data_normalized, get_start_span_function
6
+ from sentry_sdk.ai.utils import (
7
+ set_data_normalized,
8
+ normalize_message_roles,
9
+ get_start_span_function,
10
+ )
7
11
  from sentry_sdk.consts import OP, SPANDATA, SPANSTATUS
8
12
  from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
9
13
  from sentry_sdk.scope import should_send_default_pii
@@ -140,8 +144,12 @@ def _set_input_data(span, kwargs, integration):
140
144
  else:
141
145
  normalized_messages.append(message)
142
146
 
147
+ role_normalized_messages = normalize_message_roles(normalized_messages)
143
148
  set_data_normalized(
144
- span, SPANDATA.GEN_AI_REQUEST_MESSAGES, normalized_messages, unpack=False
149
+ span,
150
+ SPANDATA.GEN_AI_REQUEST_MESSAGES,
151
+ role_normalized_messages,
152
+ unpack=False,
145
153
  )
146
154
 
147
155
  set_data_normalized(