ioa-observe-sdk 1.0.9__py3-none-any.whl → 1.0.10__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.
@@ -1,6 +1,6 @@
1
1
  # Copyright AGNTCY Contributors (https://github.com/agntcy)
2
2
  # SPDX-License-Identifier: Apache-2.0
3
-
3
+ import inspect
4
4
  from typing import Optional, Union, TypeVar, Callable, Awaitable
5
5
 
6
6
  from typing_extensions import ParamSpec
@@ -50,18 +50,31 @@ def workflow(
50
50
  Union[ObserveSpanKindValues, str]
51
51
  ] = ObserveSpanKindValues.WORKFLOW,
52
52
  ) -> Callable[[F], F]:
53
- # Always use entity_class for class decorators
54
- return entity_class(
55
- name=name,
56
- description=description,
57
- version=version,
58
- method_name=method_name,
59
- tlp_span_kind=tlp_span_kind,
60
- )
53
+ def decorator(target):
54
+ # Check if target is a class
55
+ if inspect.isclass(target):
56
+ return entity_class(
57
+ name=name,
58
+ description=description,
59
+ version=version,
60
+ method_name=method_name,
61
+ tlp_span_kind=tlp_span_kind,
62
+ )(target)
63
+ else:
64
+ # Target is a function/method
65
+ return entity_method(
66
+ name=name,
67
+ description=description,
68
+ version=version,
69
+ tlp_span_kind=tlp_span_kind,
70
+ )(target)
71
+
72
+ return decorator
61
73
 
62
74
 
63
75
  def graph(
64
76
  name: Optional[str] = None,
77
+ description: Optional[str] = None,
65
78
  version: Optional[int] = None,
66
79
  method_name: Optional[str] = None,
67
80
  ) -> Callable[[F], F]:
@@ -158,15 +158,41 @@ def _handle_span_input(span, args, kwargs, cls=None):
158
158
  """Handles entity input logging in JSON for both sync and async functions"""
159
159
  try:
160
160
  if _should_send_prompts():
161
- json_input = json.dumps(
162
- {"args": args, "kwargs": kwargs}, **({"cls": cls} if cls else {})
163
- )
161
+ # Use a safer serialization approach to avoid recursion
162
+ safe_args = []
163
+ safe_kwargs = {}
164
+
165
+ # Safely convert args
166
+ for arg in args:
167
+ try:
168
+ # Test if the object can be JSON serialized
169
+ json.dumps(arg)
170
+ safe_args.append(arg)
171
+ except (TypeError, ValueError, PydanticSerializationError):
172
+ # If it can't be serialized, use string representation
173
+ safe_args.append(str(arg))
174
+
175
+ # Safely convert kwargs
176
+ for key, value in kwargs.items():
177
+ try:
178
+ # Test if the object can be JSON serialized
179
+ json.dumps(value)
180
+ safe_kwargs[key] = value
181
+ except (TypeError, ValueError, PydanticSerializationError):
182
+ # If it can't be serialized, use string representation
183
+ safe_kwargs[key] = str(value)
184
+
185
+ # Create the JSON without custom encoder to avoid recursion
186
+ json_input = json.dumps({"args": safe_args, "kwargs": safe_kwargs})
187
+
164
188
  if _is_json_size_valid(json_input):
165
189
  span.set_attribute(
166
190
  OBSERVE_ENTITY_INPUT,
167
191
  json_input,
168
192
  )
169
- except TypeError as e:
193
+ except Exception as e:
194
+ # Log the exception but don't fail the actual function call
195
+ print(f"Warning: Failed to serialize input for span: {e}")
170
196
  Telemetry().log_exception(e)
171
197
 
172
198
 
@@ -219,27 +245,32 @@ def _handle_span_output(span, tlp_span_kind, res, cls=None):
219
245
  "gen_ai.ioa.agent.connection_reliability", reliability
220
246
  )
221
247
 
222
- if _should_send_prompts():
223
- try:
224
- json_output = json.dumps(res, **({"cls": cls} if cls else {}))
225
- except (TypeError, PydanticSerializationError):
226
- # Fallback for objects that can't be directly serialized
248
+ if _should_send_prompts():
227
249
  try:
228
- # Try to serialize a string representation
229
- safe_output = str(res)
230
- json_output = json.dumps({"__str_representation__": safe_output})
231
- except Exception:
232
- # If all serialization fails, skip output attribute
233
- json_output = None
234
- if _is_json_size_valid(json_output):
235
- span.set_attribute(
236
- OBSERVE_ENTITY_OUTPUT,
237
- json_output,
238
- )
239
- TracerWrapper().span_processor_on_ending(
240
- span
241
- ) # record the response latency
242
- except TypeError as e:
250
+ # Try direct JSON serialization first (without custom encoder)
251
+ json_output = json.dumps(res)
252
+ except (TypeError, PydanticSerializationError, ValueError):
253
+ # Fallback for objects that can't be directly serialized
254
+ try:
255
+ # Try to serialize a string representation
256
+ safe_output = str(res)
257
+ json_output = json.dumps(
258
+ {"__str_representation__": safe_output}
259
+ )
260
+ except Exception:
261
+ # If all serialization fails, skip output attribute
262
+ json_output = None
263
+
264
+ if json_output and _is_json_size_valid(json_output):
265
+ span.set_attribute(
266
+ OBSERVE_ENTITY_OUTPUT,
267
+ json_output,
268
+ )
269
+ TracerWrapper().span_processor_on_ending(
270
+ span
271
+ ) # record the response latency
272
+ except Exception as e:
273
+ print(f"Warning: Failed to serialize output for span: {e}")
243
274
  Telemetry().log_exception(e)
244
275
 
245
276
 
@@ -286,7 +317,7 @@ def entity_method(
286
317
  entity_name,
287
318
  tlp_span_kind,
288
319
  version,
289
- description if description else None,
320
+ description,
290
321
  )
291
322
  _handle_span_input(span, args, kwargs, cls=JSONEncoder)
292
323
 
@@ -307,8 +338,12 @@ def entity_method(
307
338
  entity_name,
308
339
  tlp_span_kind,
309
340
  version,
310
- description if description else None,
341
+ description,
311
342
  )
343
+
344
+ # Handle case where span setup failed
345
+ if span is None:
346
+ return fn(*args, **kwargs)
312
347
  _handle_span_input(span, args, kwargs, cls=JSONEncoder)
313
348
  success = False
314
349
  try:
@@ -399,9 +434,13 @@ def entity_method(
399
434
  entity_name,
400
435
  tlp_span_kind,
401
436
  version,
402
- description if description else None,
437
+ description,
403
438
  )
404
439
 
440
+ # Handle case where span setup failed
441
+ if span is None:
442
+ return fn(*args, **kwargs)
443
+
405
444
  _handle_span_input(span, args, kwargs, cls=JSONEncoder)
406
445
  _handle_agent_span(span, entity_name, description, tlp_span_kind)
407
446
  success = False
@@ -571,15 +610,15 @@ def _handle_agent_failure_event(res, span, tlp_span_kind):
571
610
  # Skip if span is already ended
572
611
  return
573
612
  if tlp_span_kind == ObserveSpanKindValues.AGENT:
574
- TracerWrapper().failing_agents_counter.add(
575
- 1,
576
- attributes={
577
- "agent_name": span.attributes["observe.workflow.name"]
578
- if "observe.workflow.name" in span.attributes
579
- else span.attributes["traceloop.workflow.name"],
580
- "failure_reason": res, # or could be "none" if you don't want to specify
581
- },
582
- )
613
+ attributes = {}
614
+ attributes["failure_reason"] = res
615
+
616
+ if "observe.workflow.name" in span.attributes:
617
+ attributes["agent_name"] = span.attributes["observe.workflow.name"]
618
+ elif "traceloop.workflow.name" in span.attributes:
619
+ attributes["agent_name"] = span.attributes["traceloop.workflow.name"]
620
+
621
+ TracerWrapper().failing_agents_counter.add(1, attributes=attributes)
583
622
 
584
623
 
585
624
  def _handle_execution_result(span, success):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ioa-observe-sdk
3
- Version: 1.0.9
3
+ Version: 1.0.10
4
4
  Summary: IOA Observability SDK
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -9,8 +9,8 @@ ioa_observe/sdk/client/http.py,sha256=LdLYSQPFIhKN5BTB-N78jLO7ITl7jGjA0-qpewEIvO
9
9
  ioa_observe/sdk/config/__init__.py,sha256=8aVNaw0yRNLFPxlf97iOZLlJVcV81ivSDnudH2m1OIo,572
10
10
  ioa_observe/sdk/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  ioa_observe/sdk/connectors/slim.py,sha256=NwbKEV7d5NIOqmG8zKqtgGigSJl7kf3QJ65z2gxpsY8,8498
12
- ioa_observe/sdk/decorators/__init__.py,sha256=nopDcih_VW0GeDlgwA2y4gkHP9PZ2sQKdjfG6Iyn0LE,2805
13
- ioa_observe/sdk/decorators/base.py,sha256=4ytlog5Ub02BqmhWO5csXvplxp5k1AzheSDwRb7P2SA,26821
12
+ ioa_observe/sdk/decorators/__init__.py,sha256=Lv5EbouBazvWaYB0N82v26pqKtj2FAqlwfLKEh5e8Q0,3251
13
+ ioa_observe/sdk/decorators/base.py,sha256=vZuF9bsaMhH0BAJjPvmPA-cvdgaXucRuSvGHhvc_8tc,28483
14
14
  ioa_observe/sdk/decorators/util.py,sha256=WMkzmwD7Js0g1BbId9_qR4pwhnaIJdW588zVc5dpqdQ,25399
15
15
  ioa_observe/sdk/instrumentations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  ioa_observe/sdk/instrumentations/a2a.py,sha256=ov_9ckkymf_qFXG0iXVWfxlW-3kFcP-knrM_t-Cf72w,4414
@@ -38,8 +38,8 @@ ioa_observe/sdk/utils/const.py,sha256=GwbHakKPjBL4wLqAVkDrSoKB-8p18EUrbaqPuRuV_x
38
38
  ioa_observe/sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
39
39
  ioa_observe/sdk/utils/json_encoder.py,sha256=g4NQ0tTqgWssY6I1D7r4zo0G6PiUo61jhofTAw5-jno,639
40
40
  ioa_observe/sdk/utils/package_check.py,sha256=1d1MjxhwoEZIx9dumirT2pRsEWgn-m-SI4npDeEalew,576
41
- ioa_observe_sdk-1.0.9.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
42
- ioa_observe_sdk-1.0.9.dist-info/METADATA,sha256=A1X5wIkGUPhTgxRUJZEXRd8G4bNr_phxw2tTa1SBVw4,7746
43
- ioa_observe_sdk-1.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- ioa_observe_sdk-1.0.9.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
45
- ioa_observe_sdk-1.0.9.dist-info/RECORD,,
41
+ ioa_observe_sdk-1.0.10.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
42
+ ioa_observe_sdk-1.0.10.dist-info/METADATA,sha256=eNDXQNsig6-m56PqwvFuYH6XUtYG8konoqFk2UUHW9w,7747
43
+ ioa_observe_sdk-1.0.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
+ ioa_observe_sdk-1.0.10.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
45
+ ioa_observe_sdk-1.0.10.dist-info/RECORD,,