sentry-sdk 0.18.0__py2.py3-none-any.whl → 2.46.0__py2.py3-none-any.whl

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 (193) hide show
  1. sentry_sdk/__init__.py +48 -6
  2. sentry_sdk/_compat.py +64 -56
  3. sentry_sdk/_init_implementation.py +84 -0
  4. sentry_sdk/_log_batcher.py +172 -0
  5. sentry_sdk/_lru_cache.py +47 -0
  6. sentry_sdk/_metrics_batcher.py +167 -0
  7. sentry_sdk/_queue.py +81 -19
  8. sentry_sdk/_types.py +311 -11
  9. sentry_sdk/_werkzeug.py +98 -0
  10. sentry_sdk/ai/__init__.py +7 -0
  11. sentry_sdk/ai/monitoring.py +137 -0
  12. sentry_sdk/ai/utils.py +144 -0
  13. sentry_sdk/api.py +409 -67
  14. sentry_sdk/attachments.py +75 -0
  15. sentry_sdk/client.py +849 -103
  16. sentry_sdk/consts.py +1389 -34
  17. sentry_sdk/crons/__init__.py +10 -0
  18. sentry_sdk/crons/api.py +62 -0
  19. sentry_sdk/crons/consts.py +4 -0
  20. sentry_sdk/crons/decorator.py +135 -0
  21. sentry_sdk/debug.py +12 -15
  22. sentry_sdk/envelope.py +112 -61
  23. sentry_sdk/feature_flags.py +71 -0
  24. sentry_sdk/hub.py +442 -386
  25. sentry_sdk/integrations/__init__.py +228 -58
  26. sentry_sdk/integrations/_asgi_common.py +108 -0
  27. sentry_sdk/integrations/_wsgi_common.py +131 -40
  28. sentry_sdk/integrations/aiohttp.py +221 -72
  29. sentry_sdk/integrations/anthropic.py +439 -0
  30. sentry_sdk/integrations/argv.py +4 -6
  31. sentry_sdk/integrations/ariadne.py +161 -0
  32. sentry_sdk/integrations/arq.py +247 -0
  33. sentry_sdk/integrations/asgi.py +237 -135
  34. sentry_sdk/integrations/asyncio.py +144 -0
  35. sentry_sdk/integrations/asyncpg.py +208 -0
  36. sentry_sdk/integrations/atexit.py +13 -18
  37. sentry_sdk/integrations/aws_lambda.py +233 -80
  38. sentry_sdk/integrations/beam.py +27 -35
  39. sentry_sdk/integrations/boto3.py +137 -0
  40. sentry_sdk/integrations/bottle.py +91 -69
  41. sentry_sdk/integrations/celery/__init__.py +529 -0
  42. sentry_sdk/integrations/celery/beat.py +293 -0
  43. sentry_sdk/integrations/celery/utils.py +43 -0
  44. sentry_sdk/integrations/chalice.py +35 -28
  45. sentry_sdk/integrations/clickhouse_driver.py +177 -0
  46. sentry_sdk/integrations/cloud_resource_context.py +280 -0
  47. sentry_sdk/integrations/cohere.py +274 -0
  48. sentry_sdk/integrations/dedupe.py +32 -8
  49. sentry_sdk/integrations/django/__init__.py +343 -89
  50. sentry_sdk/integrations/django/asgi.py +201 -22
  51. sentry_sdk/integrations/django/caching.py +204 -0
  52. sentry_sdk/integrations/django/middleware.py +80 -32
  53. sentry_sdk/integrations/django/signals_handlers.py +91 -0
  54. sentry_sdk/integrations/django/templates.py +69 -2
  55. sentry_sdk/integrations/django/transactions.py +39 -14
  56. sentry_sdk/integrations/django/views.py +69 -16
  57. sentry_sdk/integrations/dramatiq.py +226 -0
  58. sentry_sdk/integrations/excepthook.py +19 -13
  59. sentry_sdk/integrations/executing.py +5 -6
  60. sentry_sdk/integrations/falcon.py +128 -65
  61. sentry_sdk/integrations/fastapi.py +141 -0
  62. sentry_sdk/integrations/flask.py +114 -75
  63. sentry_sdk/integrations/gcp.py +67 -36
  64. sentry_sdk/integrations/gnu_backtrace.py +14 -22
  65. sentry_sdk/integrations/google_genai/__init__.py +301 -0
  66. sentry_sdk/integrations/google_genai/consts.py +16 -0
  67. sentry_sdk/integrations/google_genai/streaming.py +155 -0
  68. sentry_sdk/integrations/google_genai/utils.py +576 -0
  69. sentry_sdk/integrations/gql.py +162 -0
  70. sentry_sdk/integrations/graphene.py +151 -0
  71. sentry_sdk/integrations/grpc/__init__.py +168 -0
  72. sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
  73. sentry_sdk/integrations/grpc/aio/client.py +95 -0
  74. sentry_sdk/integrations/grpc/aio/server.py +100 -0
  75. sentry_sdk/integrations/grpc/client.py +91 -0
  76. sentry_sdk/integrations/grpc/consts.py +1 -0
  77. sentry_sdk/integrations/grpc/server.py +66 -0
  78. sentry_sdk/integrations/httpx.py +178 -0
  79. sentry_sdk/integrations/huey.py +174 -0
  80. sentry_sdk/integrations/huggingface_hub.py +378 -0
  81. sentry_sdk/integrations/langchain.py +1132 -0
  82. sentry_sdk/integrations/langgraph.py +337 -0
  83. sentry_sdk/integrations/launchdarkly.py +61 -0
  84. sentry_sdk/integrations/litellm.py +287 -0
  85. sentry_sdk/integrations/litestar.py +315 -0
  86. sentry_sdk/integrations/logging.py +261 -85
  87. sentry_sdk/integrations/loguru.py +213 -0
  88. sentry_sdk/integrations/mcp.py +566 -0
  89. sentry_sdk/integrations/modules.py +6 -33
  90. sentry_sdk/integrations/openai.py +725 -0
  91. sentry_sdk/integrations/openai_agents/__init__.py +61 -0
  92. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  93. sentry_sdk/integrations/openai_agents/patches/__init__.py +5 -0
  94. sentry_sdk/integrations/openai_agents/patches/agent_run.py +140 -0
  95. sentry_sdk/integrations/openai_agents/patches/error_tracing.py +77 -0
  96. sentry_sdk/integrations/openai_agents/patches/models.py +50 -0
  97. sentry_sdk/integrations/openai_agents/patches/runner.py +45 -0
  98. sentry_sdk/integrations/openai_agents/patches/tools.py +77 -0
  99. sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
  100. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +21 -0
  101. sentry_sdk/integrations/openai_agents/spans/ai_client.py +42 -0
  102. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +48 -0
  103. sentry_sdk/integrations/openai_agents/spans/handoff.py +19 -0
  104. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +86 -0
  105. sentry_sdk/integrations/openai_agents/utils.py +199 -0
  106. sentry_sdk/integrations/openfeature.py +35 -0
  107. sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
  108. sentry_sdk/integrations/opentelemetry/consts.py +5 -0
  109. sentry_sdk/integrations/opentelemetry/integration.py +58 -0
  110. sentry_sdk/integrations/opentelemetry/propagator.py +117 -0
  111. sentry_sdk/integrations/opentelemetry/span_processor.py +391 -0
  112. sentry_sdk/integrations/otlp.py +82 -0
  113. sentry_sdk/integrations/pure_eval.py +20 -11
  114. sentry_sdk/integrations/pydantic_ai/__init__.py +47 -0
  115. sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
  116. sentry_sdk/integrations/pydantic_ai/patches/__init__.py +4 -0
  117. sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +215 -0
  118. sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +110 -0
  119. sentry_sdk/integrations/pydantic_ai/patches/model_request.py +40 -0
  120. sentry_sdk/integrations/pydantic_ai/patches/tools.py +98 -0
  121. sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
  122. sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +246 -0
  123. sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +49 -0
  124. sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +112 -0
  125. sentry_sdk/integrations/pydantic_ai/utils.py +223 -0
  126. sentry_sdk/integrations/pymongo.py +214 -0
  127. sentry_sdk/integrations/pyramid.py +71 -60
  128. sentry_sdk/integrations/quart.py +237 -0
  129. sentry_sdk/integrations/ray.py +165 -0
  130. sentry_sdk/integrations/redis/__init__.py +48 -0
  131. sentry_sdk/integrations/redis/_async_common.py +116 -0
  132. sentry_sdk/integrations/redis/_sync_common.py +119 -0
  133. sentry_sdk/integrations/redis/consts.py +19 -0
  134. sentry_sdk/integrations/redis/modules/__init__.py +0 -0
  135. sentry_sdk/integrations/redis/modules/caches.py +118 -0
  136. sentry_sdk/integrations/redis/modules/queries.py +65 -0
  137. sentry_sdk/integrations/redis/rb.py +32 -0
  138. sentry_sdk/integrations/redis/redis.py +69 -0
  139. sentry_sdk/integrations/redis/redis_cluster.py +107 -0
  140. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +50 -0
  141. sentry_sdk/integrations/redis/utils.py +148 -0
  142. sentry_sdk/integrations/rq.py +62 -52
  143. sentry_sdk/integrations/rust_tracing.py +284 -0
  144. sentry_sdk/integrations/sanic.py +248 -114
  145. sentry_sdk/integrations/serverless.py +13 -22
  146. sentry_sdk/integrations/socket.py +96 -0
  147. sentry_sdk/integrations/spark/spark_driver.py +115 -62
  148. sentry_sdk/integrations/spark/spark_worker.py +42 -50
  149. sentry_sdk/integrations/sqlalchemy.py +82 -37
  150. sentry_sdk/integrations/starlette.py +737 -0
  151. sentry_sdk/integrations/starlite.py +292 -0
  152. sentry_sdk/integrations/statsig.py +37 -0
  153. sentry_sdk/integrations/stdlib.py +100 -58
  154. sentry_sdk/integrations/strawberry.py +394 -0
  155. sentry_sdk/integrations/sys_exit.py +70 -0
  156. sentry_sdk/integrations/threading.py +142 -38
  157. sentry_sdk/integrations/tornado.py +68 -53
  158. sentry_sdk/integrations/trytond.py +15 -20
  159. sentry_sdk/integrations/typer.py +60 -0
  160. sentry_sdk/integrations/unleash.py +33 -0
  161. sentry_sdk/integrations/unraisablehook.py +53 -0
  162. sentry_sdk/integrations/wsgi.py +126 -125
  163. sentry_sdk/logger.py +96 -0
  164. sentry_sdk/metrics.py +81 -0
  165. sentry_sdk/monitor.py +120 -0
  166. sentry_sdk/profiler/__init__.py +49 -0
  167. sentry_sdk/profiler/continuous_profiler.py +730 -0
  168. sentry_sdk/profiler/transaction_profiler.py +839 -0
  169. sentry_sdk/profiler/utils.py +195 -0
  170. sentry_sdk/scope.py +1542 -112
  171. sentry_sdk/scrubber.py +177 -0
  172. sentry_sdk/serializer.py +152 -210
  173. sentry_sdk/session.py +177 -0
  174. sentry_sdk/sessions.py +202 -179
  175. sentry_sdk/spotlight.py +242 -0
  176. sentry_sdk/tracing.py +1202 -294
  177. sentry_sdk/tracing_utils.py +1236 -0
  178. sentry_sdk/transport.py +693 -189
  179. sentry_sdk/types.py +52 -0
  180. sentry_sdk/utils.py +1395 -228
  181. sentry_sdk/worker.py +30 -17
  182. sentry_sdk-2.46.0.dist-info/METADATA +268 -0
  183. sentry_sdk-2.46.0.dist-info/RECORD +189 -0
  184. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
  185. sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
  186. sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
  187. sentry_sdk/_functools.py +0 -66
  188. sentry_sdk/integrations/celery.py +0 -275
  189. sentry_sdk/integrations/redis.py +0 -103
  190. sentry_sdk-0.18.0.dist-info/LICENSE +0 -9
  191. sentry_sdk-0.18.0.dist-info/METADATA +0 -66
  192. sentry_sdk-0.18.0.dist-info/RECORD +0 -65
  193. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,137 @@
1
+ import inspect
2
+ from functools import wraps
3
+
4
+ from sentry_sdk.consts import SPANDATA
5
+ import sentry_sdk.utils
6
+ from sentry_sdk import start_span
7
+ from sentry_sdk.tracing import Span
8
+ from sentry_sdk.utils import ContextVar
9
+
10
+ from typing import TYPE_CHECKING
11
+
12
+ if TYPE_CHECKING:
13
+ from typing import Optional, Callable, Awaitable, Any, Union, TypeVar
14
+
15
+ F = TypeVar("F", bound=Union[Callable[..., Any], Callable[..., Awaitable[Any]]])
16
+
17
+ _ai_pipeline_name = ContextVar("ai_pipeline_name", default=None)
18
+
19
+
20
+ def set_ai_pipeline_name(name):
21
+ # type: (Optional[str]) -> None
22
+ _ai_pipeline_name.set(name)
23
+
24
+
25
+ def get_ai_pipeline_name():
26
+ # type: () -> Optional[str]
27
+ return _ai_pipeline_name.get()
28
+
29
+
30
+ def ai_track(description, **span_kwargs):
31
+ # type: (str, Any) -> Callable[[F], F]
32
+ def decorator(f):
33
+ # type: (F) -> F
34
+ def sync_wrapped(*args, **kwargs):
35
+ # type: (Any, Any) -> Any
36
+ curr_pipeline = _ai_pipeline_name.get()
37
+ op = span_kwargs.pop("op", "ai.run" if curr_pipeline else "ai.pipeline")
38
+
39
+ with start_span(name=description, op=op, **span_kwargs) as span:
40
+ for k, v in kwargs.pop("sentry_tags", {}).items():
41
+ span.set_tag(k, v)
42
+ for k, v in kwargs.pop("sentry_data", {}).items():
43
+ span.set_data(k, v)
44
+ if curr_pipeline:
45
+ span.set_data(SPANDATA.GEN_AI_PIPELINE_NAME, curr_pipeline)
46
+ return f(*args, **kwargs)
47
+ else:
48
+ _ai_pipeline_name.set(description)
49
+ try:
50
+ res = f(*args, **kwargs)
51
+ except Exception as e:
52
+ event, hint = sentry_sdk.utils.event_from_exception(
53
+ e,
54
+ client_options=sentry_sdk.get_client().options,
55
+ mechanism={"type": "ai_monitoring", "handled": False},
56
+ )
57
+ sentry_sdk.capture_event(event, hint=hint)
58
+ raise e from None
59
+ finally:
60
+ _ai_pipeline_name.set(None)
61
+ return res
62
+
63
+ async def async_wrapped(*args, **kwargs):
64
+ # type: (Any, Any) -> Any
65
+ curr_pipeline = _ai_pipeline_name.get()
66
+ op = span_kwargs.pop("op", "ai.run" if curr_pipeline else "ai.pipeline")
67
+
68
+ with start_span(name=description, op=op, **span_kwargs) as span:
69
+ for k, v in kwargs.pop("sentry_tags", {}).items():
70
+ span.set_tag(k, v)
71
+ for k, v in kwargs.pop("sentry_data", {}).items():
72
+ span.set_data(k, v)
73
+ if curr_pipeline:
74
+ span.set_data(SPANDATA.GEN_AI_PIPELINE_NAME, curr_pipeline)
75
+ return await f(*args, **kwargs)
76
+ else:
77
+ _ai_pipeline_name.set(description)
78
+ try:
79
+ res = await f(*args, **kwargs)
80
+ except Exception as e:
81
+ event, hint = sentry_sdk.utils.event_from_exception(
82
+ e,
83
+ client_options=sentry_sdk.get_client().options,
84
+ mechanism={"type": "ai_monitoring", "handled": False},
85
+ )
86
+ sentry_sdk.capture_event(event, hint=hint)
87
+ raise e from None
88
+ finally:
89
+ _ai_pipeline_name.set(None)
90
+ return res
91
+
92
+ if inspect.iscoroutinefunction(f):
93
+ return wraps(f)(async_wrapped) # type: ignore
94
+ else:
95
+ return wraps(f)(sync_wrapped) # type: ignore
96
+
97
+ return decorator
98
+
99
+
100
+ def record_token_usage(
101
+ span,
102
+ input_tokens=None,
103
+ input_tokens_cached=None,
104
+ output_tokens=None,
105
+ output_tokens_reasoning=None,
106
+ total_tokens=None,
107
+ ):
108
+ # type: (Span, Optional[int], Optional[int], Optional[int], Optional[int], Optional[int]) -> None
109
+
110
+ # TODO: move pipeline name elsewhere
111
+ ai_pipeline_name = get_ai_pipeline_name()
112
+ if ai_pipeline_name:
113
+ span.set_data(SPANDATA.GEN_AI_PIPELINE_NAME, ai_pipeline_name)
114
+
115
+ if input_tokens is not None:
116
+ span.set_data(SPANDATA.GEN_AI_USAGE_INPUT_TOKENS, input_tokens)
117
+
118
+ if input_tokens_cached is not None:
119
+ span.set_data(
120
+ SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED,
121
+ input_tokens_cached,
122
+ )
123
+
124
+ if output_tokens is not None:
125
+ span.set_data(SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens)
126
+
127
+ if output_tokens_reasoning is not None:
128
+ span.set_data(
129
+ SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS_REASONING,
130
+ output_tokens_reasoning,
131
+ )
132
+
133
+ if total_tokens is None and input_tokens is not None and output_tokens is not None:
134
+ total_tokens = input_tokens + output_tokens
135
+
136
+ if total_tokens is not None:
137
+ span.set_data(SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS, total_tokens)
sentry_sdk/ai/utils.py ADDED
@@ -0,0 +1,144 @@
1
+ import json
2
+ from collections import deque
3
+ from typing import TYPE_CHECKING
4
+ from sys import getsizeof
5
+
6
+ if TYPE_CHECKING:
7
+ from typing import Any, Callable, Dict, List, Optional, Tuple
8
+
9
+ from sentry_sdk.tracing import Span
10
+
11
+ import sentry_sdk
12
+ from sentry_sdk.utils import logger
13
+
14
+ MAX_GEN_AI_MESSAGE_BYTES = 20_000 # 20KB
15
+
16
+
17
+ class GEN_AI_ALLOWED_MESSAGE_ROLES:
18
+ SYSTEM = "system"
19
+ USER = "user"
20
+ ASSISTANT = "assistant"
21
+ TOOL = "tool"
22
+
23
+
24
+ GEN_AI_MESSAGE_ROLE_REVERSE_MAPPING = {
25
+ GEN_AI_ALLOWED_MESSAGE_ROLES.SYSTEM: ["system"],
26
+ GEN_AI_ALLOWED_MESSAGE_ROLES.USER: ["user", "human"],
27
+ GEN_AI_ALLOWED_MESSAGE_ROLES.ASSISTANT: ["assistant", "ai"],
28
+ GEN_AI_ALLOWED_MESSAGE_ROLES.TOOL: ["tool", "tool_call"],
29
+ }
30
+
31
+ GEN_AI_MESSAGE_ROLE_MAPPING = {}
32
+ for target_role, source_roles in GEN_AI_MESSAGE_ROLE_REVERSE_MAPPING.items():
33
+ for source_role in source_roles:
34
+ GEN_AI_MESSAGE_ROLE_MAPPING[source_role] = target_role
35
+
36
+
37
+ def _normalize_data(data, unpack=True):
38
+ # type: (Any, bool) -> Any
39
+ # convert pydantic data (e.g. OpenAI v1+) to json compatible format
40
+ if hasattr(data, "model_dump"):
41
+ try:
42
+ return _normalize_data(data.model_dump(), unpack=unpack)
43
+ except Exception as e:
44
+ logger.warning("Could not convert pydantic data to JSON: %s", e)
45
+ return data if isinstance(data, (int, float, bool, str)) else str(data)
46
+
47
+ if isinstance(data, list):
48
+ if unpack and len(data) == 1:
49
+ return _normalize_data(data[0], unpack=unpack) # remove empty dimensions
50
+ return list(_normalize_data(x, unpack=unpack) for x in data)
51
+
52
+ if isinstance(data, dict):
53
+ return {k: _normalize_data(v, unpack=unpack) for (k, v) in data.items()}
54
+
55
+ return data if isinstance(data, (int, float, bool, str)) else str(data)
56
+
57
+
58
+ def set_data_normalized(span, key, value, unpack=True):
59
+ # type: (Span, str, Any, bool) -> None
60
+ normalized = _normalize_data(value, unpack=unpack)
61
+ if isinstance(normalized, (int, float, bool, str)):
62
+ span.set_data(key, normalized)
63
+ else:
64
+ span.set_data(key, json.dumps(normalized))
65
+
66
+
67
+ def normalize_message_role(role):
68
+ # type: (str) -> str
69
+ """
70
+ Normalize a message role to one of the 4 allowed gen_ai role values.
71
+ Maps "ai" -> "assistant" and keeps other standard roles unchanged.
72
+ """
73
+ return GEN_AI_MESSAGE_ROLE_MAPPING.get(role, role)
74
+
75
+
76
+ def normalize_message_roles(messages):
77
+ # type: (list[dict[str, Any]]) -> list[dict[str, Any]]
78
+ """
79
+ Normalize roles in a list of messages to use standard gen_ai role values.
80
+ Creates a deep copy to avoid modifying the original messages.
81
+ """
82
+ normalized_messages = []
83
+ for message in messages:
84
+ if not isinstance(message, dict):
85
+ normalized_messages.append(message)
86
+ continue
87
+ normalized_message = message.copy()
88
+ if "role" in message:
89
+ normalized_message["role"] = normalize_message_role(message["role"])
90
+ normalized_messages.append(normalized_message)
91
+
92
+ return normalized_messages
93
+
94
+
95
+ def get_start_span_function():
96
+ # type: () -> Callable[..., Any]
97
+ current_span = sentry_sdk.get_current_span()
98
+ transaction_exists = (
99
+ current_span is not None and current_span.containing_transaction is not None
100
+ )
101
+ return sentry_sdk.start_span if transaction_exists else sentry_sdk.start_transaction
102
+
103
+
104
+ def _find_truncation_index(messages, max_bytes):
105
+ # type: (List[Dict[str, Any]], int) -> int
106
+ """
107
+ Find the index of the first message that would exceed the max bytes limit.
108
+ Compute the individual message sizes, and return the index of the first message from the back
109
+ of the list that would exceed the max bytes limit.
110
+ """
111
+ running_sum = 0
112
+ for idx in range(len(messages) - 1, -1, -1):
113
+ size = len(json.dumps(messages[idx], separators=(",", ":")).encode("utf-8"))
114
+ running_sum += size
115
+ if running_sum > max_bytes:
116
+ return idx + 1
117
+
118
+ return 0
119
+
120
+
121
+ def truncate_messages_by_size(messages, max_bytes=MAX_GEN_AI_MESSAGE_BYTES):
122
+ # type: (List[Dict[str, Any]], int) -> Tuple[List[Dict[str, Any]], int]
123
+ serialized_json = json.dumps(messages, separators=(",", ":"))
124
+ current_size = len(serialized_json.encode("utf-8"))
125
+
126
+ if current_size <= max_bytes:
127
+ return messages, 0
128
+
129
+ truncation_index = _find_truncation_index(messages, max_bytes)
130
+ return messages[truncation_index:], truncation_index
131
+
132
+
133
+ def truncate_and_annotate_messages(
134
+ messages, span, scope, max_bytes=MAX_GEN_AI_MESSAGE_BYTES
135
+ ):
136
+ # type: (Optional[List[Dict[str, Any]]], Any, Any, int) -> Optional[List[Dict[str, Any]]]
137
+ if not messages:
138
+ return None
139
+
140
+ truncated_messages, removed_count = truncate_messages_by_size(messages, max_bytes)
141
+ if removed_count > 0:
142
+ scope._gen_ai_original_message_count[span.span_id] = len(messages)
143
+
144
+ return truncated_messages