langtrace-python-sdk 3.8.15__py3-none-any.whl → 3.8.17__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 +1 @@
1
- __version__ = "3.8.15"
1
+ __version__ = "3.8.17"
@@ -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.17
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>
@@ -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=csVUBgURmjY_-YGxUIC12qiRsv-dOi-b_wDRDkd1SUg,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
@@ -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.17.dist-info/METADATA,sha256=feYBeHSHTLTW9hBTvwQxD52bd1Q2eeSpd-8un4fg5Y0,15845
316
+ langtrace_python_sdk-3.8.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
317
+ langtrace_python_sdk-3.8.17.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
318
+ langtrace_python_sdk-3.8.17.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
319
+ langtrace_python_sdk-3.8.17.dist-info/RECORD,,