langtrace-python-sdk 3.8.15__py3-none-any.whl → 3.8.18__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.
@@ -3,4 +3,8 @@ APIS = {
3
3
  "METHOD": "anthropic.messages.create",
4
4
  "ENDPOINT": "/v1/messages",
5
5
  },
6
+ "MESSAGES_STREAM": {
7
+ "METHOD": "anthropic.messages.stream",
8
+ "ENDPOINT": "/v1/messages",
9
+ },
6
10
  }
@@ -23,7 +23,7 @@ from opentelemetry.trace import TracerProvider
23
23
  from opentelemetry.trace import get_tracer
24
24
  from wrapt import wrap_function_wrapper
25
25
  from typing import Any
26
- from langtrace_python_sdk.instrumentation.anthropic.patch import messages_create
26
+ from langtrace_python_sdk.instrumentation.anthropic.patch import messages_create, messages_stream
27
27
 
28
28
  logging.basicConfig(level=logging.FATAL)
29
29
 
@@ -46,6 +46,12 @@ class AnthropicInstrumentation(BaseInstrumentor): # type: ignore[misc]
46
46
  "Messages.create",
47
47
  messages_create(version, tracer),
48
48
  )
49
+
50
+ wrap_function_wrapper(
51
+ "anthropic.resources.messages",
52
+ "Messages.stream",
53
+ messages_stream(version, tracer),
54
+ )
49
55
 
50
56
  def _instrument_module(self, module_name: str) -> None:
51
57
  pass
@@ -56,9 +56,7 @@ def messages_create(version: str, tracer: Tracer) -> Callable[..., Any]:
56
56
  prompts = kwargs.get("messages", [])
57
57
  system = kwargs.get("system")
58
58
  if system:
59
- prompts = [{"role": "system", "content": system}] + kwargs.get(
60
- "messages", []
61
- )
59
+ prompts.append({"role": "system", "content": system})
62
60
  span_attributes = {
63
61
  **get_langtrace_attributes(version, service_provider),
64
62
  **get_llm_request_attributes(kwargs, prompts=prompts),
@@ -72,7 +70,14 @@ def messages_create(version: str, tracer: Tracer) -> Callable[..., Any]:
72
70
  span = tracer.start_span(
73
71
  name=get_span_name(APIS["MESSAGES_CREATE"]["METHOD"]), kind=SpanKind.CLIENT
74
72
  )
73
+
75
74
  set_span_attributes(span, attributes)
75
+
76
+ tools = []
77
+ if kwargs.get("tools") is not None and kwargs.get("tools"):
78
+ tools.append(json.dumps(kwargs.get("tools")))
79
+ set_span_attribute(span, SpanAttributes.LLM_TOOLS, json.dumps(tools))
80
+
76
81
  try:
77
82
  # Attempt to call the original method
78
83
  result = wrapped(*args, **kwargs)
@@ -127,7 +132,149 @@ def messages_create(version: str, tracer: Tracer) -> Callable[..., Any]:
127
132
  span.end()
128
133
  return result
129
134
  else:
130
- return StreamWrapper(result, span)
135
+ return StreamWrapper(result, span, tool_calls=True)
131
136
 
132
137
  # return the wrapped method
133
138
  return traced_method
139
+
140
+
141
+ def messages_stream(version: str, tracer: Tracer) -> Callable[..., Any]:
142
+
143
+ def traced_method(
144
+ wrapped: Callable[..., Any],
145
+ instance: Any,
146
+ args: List[Any],
147
+ kwargs: MessagesCreateKwargs,
148
+ ) -> Any:
149
+ service_provider = SERVICE_PROVIDERS["ANTHROPIC"]
150
+
151
+ prompts = kwargs.get("messages", [])
152
+ system = kwargs.get("system")
153
+ if system:
154
+ prompts.append({"role": "assistant", "content": system})
155
+ span_attributes = {
156
+ **get_langtrace_attributes(version, service_provider),
157
+ **get_llm_request_attributes(kwargs, prompts=prompts),
158
+ **get_llm_url(instance),
159
+ SpanAttributes.LLM_PATH: APIS["MESSAGES_STREAM"]["ENDPOINT"],
160
+ **get_extra_attributes(),
161
+ }
162
+
163
+ attributes = LLMSpanAttributes(**span_attributes)
164
+
165
+ span = tracer.start_span(
166
+ name=get_span_name(APIS["MESSAGES_STREAM"]["METHOD"]), kind=SpanKind.CLIENT
167
+ )
168
+
169
+ set_span_attributes(span, attributes)
170
+
171
+ tools = []
172
+ if kwargs.get("tools") is not None:
173
+ tools.append(json.dumps(kwargs.get("tools")))
174
+ set_span_attribute(span, SpanAttributes.LLM_TOOLS, json.dumps(tools))
175
+
176
+ try:
177
+ # Create the original message stream manager
178
+ original_stream_manager = wrapped(*args, **kwargs)
179
+
180
+ # Create a new stream manager that will instrument the stream
181
+ # while preserving the stream
182
+ class InstrumentedMessageStreamManager:
183
+ def __init__(self, original_manager, span):
184
+ self.original_manager = original_manager
185
+ self.span = span
186
+
187
+ def __enter__(self):
188
+ # Enter the original context manager to get the stream
189
+ original_stream = self.original_manager.__enter__()
190
+
191
+ # Create a wrapper iterator
192
+ class InstrumentedStream:
193
+ def __init__(self, original_stream, span):
194
+ self.original_stream = original_stream
195
+ self.span = span
196
+ self.message_stop_processed = False
197
+
198
+ def __iter__(self):
199
+ return self
200
+
201
+ def __next__(self):
202
+ try:
203
+ chunk = next(self.original_stream)
204
+
205
+ # Apply instrumentation only once on message_stop
206
+ if chunk.type == "message_stop" and not self.message_stop_processed:
207
+ self.message_stop_processed = True
208
+ response_message = chunk.message
209
+
210
+ responses = [
211
+ {
212
+ "role": (
213
+ response_message.role
214
+ if response_message.role
215
+ else "assistant"
216
+ ),
217
+ "content": message.text,
218
+ }
219
+ for message in response_message.content if message.type == "text"
220
+ ]
221
+
222
+ set_event_completion(self.span, responses)
223
+
224
+ if hasattr(response_message, "usage") and response_message.usage is not None:
225
+ set_span_attribute(
226
+ self.span,
227
+ SpanAttributes.LLM_USAGE_PROMPT_TOKENS,
228
+ response_message.usage.input_tokens,
229
+ )
230
+ set_span_attribute(
231
+ self.span,
232
+ SpanAttributes.LLM_USAGE_COMPLETION_TOKENS,
233
+ response_message.usage.output_tokens,
234
+ )
235
+ set_span_attribute(
236
+ self.span,
237
+ SpanAttributes.LLM_USAGE_TOTAL_TOKENS,
238
+ response_message.usage.input_tokens + response_message.usage.output_tokens,
239
+ )
240
+
241
+ # Forward the chunk
242
+ return chunk
243
+ except StopIteration:
244
+ # End the span when we're done with the stream
245
+ self.span.end()
246
+ raise
247
+ except Exception as err:
248
+ self.span.record_exception(err)
249
+ self.span.set_status(StatusCode.ERROR, str(err))
250
+ self.span.end()
251
+ raise
252
+
253
+ def close(self):
254
+ self.original_stream.close()
255
+ if not self.message_stop_processed:
256
+ self.span.end()
257
+
258
+ # Return our instrumented stream wrapper
259
+ return InstrumentedStream(original_stream, self.span)
260
+
261
+ def __exit__(self, exc_type, exc_val, exc_tb):
262
+ result = self.original_manager.__exit__(exc_type, exc_val, exc_tb)
263
+
264
+ if exc_type is not None:
265
+ self.span.record_exception(exc_val)
266
+ self.span.set_status(StatusCode.ERROR, str(exc_val))
267
+ self.span.end()
268
+
269
+ return result
270
+
271
+ # Return the instrumented stream manager
272
+ return InstrumentedMessageStreamManager(original_stream_manager, span)
273
+
274
+ except Exception as err:
275
+ span.record_exception(err)
276
+ span.set_status(StatusCode.ERROR, str(err))
277
+ span.end()
278
+ raise
279
+
280
+ return traced_method
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  from langtrace_python_sdk.utils.llm import (
2
3
  get_langtrace_attributes,
3
4
  get_llm_request_attributes,
@@ -14,20 +15,65 @@ from langtrace.trace_attributes import SpanAttributes
14
15
 
15
16
  from typing import Iterator
16
17
 
18
+ def capture_input_data(contents):
19
+ input_data = []
20
+
21
+ if isinstance(contents, list):
22
+ content_parts = []
23
+ for content in contents:
24
+ if hasattr(content, 'parts'):
25
+ for part in content.parts:
26
+ if hasattr(part, 'text') and part.text:
27
+ content_parts.append({
28
+ "type": "text",
29
+ "text": part.text
30
+ })
31
+ if hasattr(part, 'inline_data') and part.inline_data:
32
+ content_parts.append({
33
+ "type": "image_url",
34
+ "image_url": f"data:{part.inline_data.mime_type};base64,{base64.b64encode(part.inline_data.data).decode('utf-8')}"
35
+ })
36
+ else:
37
+ if isinstance(content, str):
38
+ content_parts.append({
39
+ "type": "text",
40
+ "text": content
41
+ })
42
+ elif hasattr(content, 'text') and content.text:
43
+ content_parts.append({
44
+ "type": "text",
45
+ "text": content.text
46
+ })
47
+ elif hasattr(content, 'inline_data') and content.inline_data:
48
+ content_parts.append({
49
+ "type": "image_url",
50
+ "image_url": f"data:{content.inline_data.mime_type};base64,{base64.b64encode(content.inline_data.data).decode('utf-8')}"
51
+ })
52
+ else:
53
+ content_parts.append({
54
+ "type": "text",
55
+ "text": content
56
+ })
57
+ input_data.append({
58
+ "role": "user",
59
+ "content": content_parts
60
+ })
61
+ else:
62
+ input_data.append({
63
+ "role": "user",
64
+ "content": contents
65
+ })
66
+
67
+ return input_data
68
+
17
69
 
18
70
  def patch_google_genai(tracer: Tracer, version: str):
19
71
  def traced_method(wrapped, instance, args, kwargs):
20
- prompt = [
21
- {
22
- "role": "user",
23
- "content": kwargs["contents"],
24
- }
25
- ]
26
72
  span_attributes = {
27
73
  **get_langtrace_attributes(
28
74
  service_provider="google_genai", version=version
29
75
  ),
30
- **get_llm_request_attributes(kwargs=kwargs, prompts=prompt),
76
+ **get_llm_request_attributes(kwargs=kwargs, prompts=capture_input_data(kwargs["contents"])),
31
77
  }
32
78
  with tracer.start_as_current_span(
33
79
  name="google.genai.generate_content",
@@ -1 +1 @@
1
- __version__ = "3.8.15"
1
+ __version__ = "3.8.18"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langtrace-python-sdk
3
- Version: 3.8.15
3
+ Version: 3.8.18
4
4
  Summary: Python SDK for LangTrace
5
5
  Project-URL: Homepage, https://github.com/Scale3-Labs/langtrace-python-sdk
6
6
  Author-email: Scale3 Labs <engineering@scale3labs.com>
@@ -10,7 +10,7 @@ Classifier: License :: OSI Approved :: Apache Software License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.9
13
- Requires-Dist: boto3
13
+ Requires-Dist: boto3==1.38.0
14
14
  Requires-Dist: colorama>=0.4.6
15
15
  Requires-Dist: fsspec>=2024.6.0
16
16
  Requires-Dist: opentelemetry-api>=1.25.0
@@ -121,11 +121,11 @@ examples/weaviate_example/__init__.py,sha256=8JMDBsRSEV10HfTd-YC7xb4txBjD3la56sn
121
121
  examples/weaviate_example/query_text.py,sha256=wPHQTc_58kPoKTZMygVjTj-2ZcdrIuaausJfMxNQnQc,127162
122
122
  langtrace_python_sdk/__init__.py,sha256=VZM6i71NR7pBQK6XvJWRelknuTYUhqwqE7PlicKa5Wg,1166
123
123
  langtrace_python_sdk/langtrace.py,sha256=JYBCbklWv463lzRUH7pfkHAVoc3YHh-q_2Iv6LZ2HhU,14896
124
- langtrace_python_sdk/version.py,sha256=zVdntVJdm-11j_n2E2RXCsklIAlpwHH2GJDDprZvteg,23
124
+ langtrace_python_sdk/version.py,sha256=whHLu8P7QnGYdLeAzZ-eOHwlW3MJuanQ-a9U15xrJzY,23
125
125
  langtrace_python_sdk/constants/__init__.py,sha256=3CNYkWMdd1DrkGqzLUgNZXjdAlM6UFMlf_F-odAToyc,146
126
126
  langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=EVCrouYCpY98f0KSaKr4PzNxPULTZZO6dSA_crEOyJU,106
127
127
  langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
128
- langtrace_python_sdk/constants/instrumentation/anthropic.py,sha256=YX3llt3zwDY6XrYk3CB8WEVqgrzRXEw_ffyk56JoF3k,126
128
+ langtrace_python_sdk/constants/instrumentation/anthropic.py,sha256=XZjpyILHQsx52Qe926ahisqTCi_iVReCkkj7Pr2gEZ8,241
129
129
  langtrace_python_sdk/constants/instrumentation/aws_bedrock.py,sha256=QwKtO4NBarOZoGkt5cFCcpxAw3zvZxcMMWBbzPPGv-g,422
130
130
  langtrace_python_sdk/constants/instrumentation/chroma.py,sha256=hiPGYdHS0Yj4Kh3eaYBbuCAl_swqIygu80yFqkOgdak,955
131
131
  langtrace_python_sdk/constants/instrumentation/cohere.py,sha256=9yD133VdrYZ5BoJR4nJHlj67gHEImB9-KsD-NkzHW1I,1159
@@ -152,8 +152,8 @@ langtrace_python_sdk/instrumentation/agno/__init__.py,sha256=95fn4oA-CHB0mxc6KnV
152
152
  langtrace_python_sdk/instrumentation/agno/instrumentation.py,sha256=DWOIEl9Q4IhCFgFQPW3pILdVq_C0RJiaGm_diZ73XJU,3284
153
153
  langtrace_python_sdk/instrumentation/agno/patch.py,sha256=wHMDNiH-XOUTnqC6HKGGbYOp04R8DG2xJqm9Ex_h9AE,22868
154
154
  langtrace_python_sdk/instrumentation/anthropic/__init__.py,sha256=donrurJAGYlxrSRA3BIf76jGeUcAx9Tq8CVpah68S0Y,101
155
- langtrace_python_sdk/instrumentation/anthropic/instrumentation.py,sha256=ndXdruI0BG7n75rsuEpKjfzePxrZxg40gZ39ONmD_v4,1845
156
- langtrace_python_sdk/instrumentation/anthropic/patch.py,sha256=ztPN4VZujoxYOKhTbFnup7Ibms9NAzYCPAJY43NUgKw,4935
155
+ langtrace_python_sdk/instrumentation/anthropic/instrumentation.py,sha256=nvRKdf5zj-YbeSpi8moXER5WTi0Oexus6w9E73zsUkc,2033
156
+ langtrace_python_sdk/instrumentation/anthropic/patch.py,sha256=XEhZd4fL2jt39cs775Xp6DXqFwDml-TWzcsmoZnvtyA,11850
157
157
  langtrace_python_sdk/instrumentation/anthropic/types.py,sha256=WdeXe2tkjAisMLK38STKwVe7MJS5SLoETJ_aKhA_-UU,2463
158
158
  langtrace_python_sdk/instrumentation/autogen/__init__.py,sha256=unDhpqWQIdHFw24lRsRu1Mm1NCZxZgyBrPRZrAJL3Lo,90
159
159
  langtrace_python_sdk/instrumentation/autogen/instrumentation.py,sha256=MVDUCBi6XzLQYmZd6myAounI0HeM8QWX5leuul5Hj0Q,1262
@@ -192,7 +192,7 @@ langtrace_python_sdk/instrumentation/gemini/instrumentation.py,sha256=eGWr2dy1f_
192
192
  langtrace_python_sdk/instrumentation/gemini/patch.py,sha256=PG5E5v253x2ufQ81-aUUFzDRYq-DDhiqwmNoplHPjsM,6261
193
193
  langtrace_python_sdk/instrumentation/google_genai/__init__.py,sha256=nQ2kSsUDpLGZ_kz2QGddDwnawL9juVA7pW4G-BOe5VI,98
194
194
  langtrace_python_sdk/instrumentation/google_genai/instrumentation.py,sha256=rSB3nOXXe4gMVPWMGPnIHvWjqyPeRvifxOI62ulJ7uY,1049
195
- langtrace_python_sdk/instrumentation/google_genai/patch.py,sha256=X0TWY1D4XHweaNu70PlXBDzKEaHIibLpkJiIsp4jF6A,4115
195
+ langtrace_python_sdk/instrumentation/google_genai/patch.py,sha256=WS7VLe5lbbTU0BAiP69vJ5V5plyQ4GeXs1LQDBNqINQ,5995
196
196
  langtrace_python_sdk/instrumentation/graphlit/__init__.py,sha256=mkUPwozR-I-D5ZrumJs1gS7sSouY0UN68Ne9LiNEBTs,92
197
197
  langtrace_python_sdk/instrumentation/graphlit/instrumentation.py,sha256=V9GRZ6cj2lt20xCVfL55lGU4p0HlZxAYUqwpWogXDtY,2074
198
198
  langtrace_python_sdk/instrumentation/graphlit/patch.py,sha256=jVME4xlFTwJRPWOBGUtQiZrGkr1ScR2DSYwcWPd5smk,2921
@@ -312,8 +312,8 @@ tests/pinecone/cassettes/test_query.yaml,sha256=b5v9G3ssUy00oG63PlFUR3JErF2Js-5A
312
312
  tests/pinecone/cassettes/test_upsert.yaml,sha256=neWmQ1v3d03V8WoLl8FoFeeCYImb8pxlJBWnFd_lITU,38607
313
313
  tests/qdrant/conftest.py,sha256=9n0uHxxIjWk9fbYc4bx-uP8lSAgLBVx-cV9UjnsyCHM,381
314
314
  tests/qdrant/test_qdrant.py,sha256=pzjAjVY2kmsmGfrI2Gs2xrolfuaNHz7l1fqGQCjp5_o,3353
315
- langtrace_python_sdk-3.8.15.dist-info/METADATA,sha256=bwp9ceCkaDntiZrMjbZb4IPkzgJVPMS-dqibINbH-KA,15845
316
- langtrace_python_sdk-3.8.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
317
- langtrace_python_sdk-3.8.15.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
318
- langtrace_python_sdk-3.8.15.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
319
- langtrace_python_sdk-3.8.15.dist-info/RECORD,,
315
+ langtrace_python_sdk-3.8.18.dist-info/METADATA,sha256=XHSAwzWsHAh5FLLWZ9896hei2d_hqQeEkomYDIEV0KM,15853
316
+ langtrace_python_sdk-3.8.18.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
317
+ langtrace_python_sdk-3.8.18.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
318
+ langtrace_python_sdk-3.8.18.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
319
+ langtrace_python_sdk-3.8.18.dist-info/RECORD,,