ioa-observe-sdk 1.0.12__py3-none-any.whl → 1.0.14__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.
@@ -21,6 +21,7 @@ def task(
21
21
  name: Optional[str] = None,
22
22
  description: Optional[str] = None,
23
23
  version: Optional[int] = None,
24
+ protocol: Optional[str] = None,
24
25
  method_name: Optional[str] = None,
25
26
  tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
26
27
  ) -> Callable[[F], F]:
@@ -29,6 +30,7 @@ def task(
29
30
  name=name,
30
31
  description=description,
31
32
  version=version,
33
+ protocol=protocol,
32
34
  tlp_span_kind=tlp_span_kind,
33
35
  )
34
36
  else:
@@ -36,6 +38,7 @@ def task(
36
38
  name=name,
37
39
  description=description,
38
40
  version=version,
41
+ protocol=protocol,
39
42
  method_name=method_name,
40
43
  tlp_span_kind=tlp_span_kind,
41
44
  )
@@ -45,6 +48,7 @@ def workflow(
45
48
  name: Optional[str] = None,
46
49
  description: Optional[str] = None,
47
50
  version: Optional[int] = None,
51
+ protocol: Optional[str] = None,
48
52
  method_name: Optional[str] = None,
49
53
  tlp_span_kind: Optional[
50
54
  Union[ObserveSpanKindValues, str]
@@ -57,6 +61,7 @@ def workflow(
57
61
  name=name,
58
62
  description=description,
59
63
  version=version,
64
+ protocol=protocol,
60
65
  method_name=method_name,
61
66
  tlp_span_kind=tlp_span_kind,
62
67
  )(target)
@@ -66,6 +71,7 @@ def workflow(
66
71
  name=name,
67
72
  description=description,
68
73
  version=version,
74
+ protocol=protocol,
69
75
  tlp_span_kind=tlp_span_kind,
70
76
  )(target)
71
77
 
@@ -77,14 +83,18 @@ def graph(
77
83
  description: Optional[str] = None,
78
84
  version: Optional[int] = None,
79
85
  method_name: Optional[str] = None,
86
+ protocol: Optional[str] = None,
80
87
  ) -> Callable[[F], F]:
81
88
  if method_name is None:
82
- return entity_method(name=name, version=version, tlp_span_kind="graph")
89
+ return entity_method(
90
+ name=name, version=version, protocol=protocol, tlp_span_kind="graph"
91
+ )
83
92
  else:
84
93
  return entity_class(
85
94
  name=name,
86
95
  version=version,
87
96
  method_name=method_name,
97
+ protocol=protocol,
88
98
  tlp_span_kind="graph",
89
99
  )
90
100
 
@@ -93,12 +103,14 @@ def agent(
93
103
  name: Optional[str] = None,
94
104
  description: Optional[str] = None,
95
105
  version: Optional[int] = None,
106
+ protocol: Optional[str] = None,
96
107
  method_name: Optional[str] = None,
97
108
  ) -> Callable[[F], F]:
98
109
  return workflow(
99
110
  name=name,
100
111
  description=description,
101
112
  version=version,
113
+ protocol=protocol,
102
114
  method_name=method_name,
103
115
  tlp_span_kind=ObserveSpanKindValues.AGENT,
104
116
  )
@@ -7,16 +7,23 @@ import traceback
7
7
  from functools import wraps
8
8
  import os
9
9
  import types
10
- from typing import Optional, TypeVar, Callable, Awaitable, Any, cast, Union
10
+ from typing import Optional, TypeVar, Callable, Awaitable, Any, Union
11
11
  import inspect
12
12
 
13
+ from ioa_observe.sdk.decorators.helpers import (
14
+ _is_async_method,
15
+ _get_original_function_name,
16
+ _is_async_generator,
17
+ )
18
+
19
+
13
20
  from langgraph.graph.state import CompiledStateGraph
14
21
  from opentelemetry import trace
15
22
  from opentelemetry import context as context_api
16
23
  from pydantic_core import PydanticSerializationError
17
24
  from typing_extensions import ParamSpec
18
25
 
19
- from ioa_observe.sdk.decorators.util import determine_workflow_type
26
+ from ioa_observe.sdk.decorators.util import determine_workflow_type, _serialize_object
20
27
  from ioa_observe.sdk.metrics.agents.availability import agent_availability
21
28
  from ioa_observe.sdk.metrics.agents.recovery_tracker import agent_recovery_tracker
22
29
  from ioa_observe.sdk.metrics.agents.tool_call_tracker import tool_call_tracker
@@ -79,14 +86,6 @@ def _should_send_prompts():
79
86
  ).lower() == "true" or context_api.get_value("override_enable_content_tracing")
80
87
 
81
88
 
82
- # Unified Decorators : handles both sync and async operations
83
-
84
-
85
- def _is_async_method(fn):
86
- # check if co-routine function or async generator( example : using async & yield)
87
- return inspect.iscoroutinefunction(fn) or inspect.isasyncgenfunction(fn)
88
-
89
-
90
89
  def _setup_span(
91
90
  entity_name,
92
91
  tlp_span_kind: Optional[ObserveSpanKindValues] = None,
@@ -164,24 +163,24 @@ def _handle_span_input(span, args, kwargs, cls=None):
164
163
  # Safely convert args
165
164
  for arg in args:
166
165
  try:
167
- # Test if the object can be JSON serialized
166
+ # Check if the object can be JSON serialized directly
168
167
  json.dumps(arg)
169
168
  safe_args.append(arg)
170
169
  except (TypeError, ValueError, PydanticSerializationError):
171
- # If it can't be serialized, use string representation
172
- safe_args.append(str(arg))
170
+ # Use intelligent serialization
171
+ safe_args.append(_serialize_object(arg))
173
172
 
174
173
  # Safely convert kwargs
175
174
  for key, value in kwargs.items():
176
175
  try:
177
- # Test if the object can be JSON serialized
176
+ # Test if the object can be JSON serialized directly
178
177
  json.dumps(value)
179
178
  safe_kwargs[key] = value
180
179
  except (TypeError, ValueError, PydanticSerializationError):
181
- # If it can't be serialized, use string representation
182
- safe_kwargs[key] = str(value)
180
+ # Use intelligent serialization
181
+ safe_kwargs[key] = _serialize_object(value)
183
182
 
184
- # Create the JSON without custom encoder to avoid recursion
183
+ # Create the JSON
185
184
  json_input = json.dumps({"args": safe_args, "kwargs": safe_kwargs})
186
185
 
187
186
  if _is_json_size_valid(json_input):
@@ -212,62 +211,55 @@ def _handle_span_output(span, tlp_span_kind, res, cls=None):
212
211
  # end_span.end() # end the span immediately
213
212
  set_agent_id_event("") # reset the agent id event
214
213
  # Add agent interpretation scoring
215
- if (
216
- tlp_span_kind == ObserveSpanKindValues.AGENT
217
- or tlp_span_kind == ObserveSpanKindValues.WORKFLOW
218
- ):
219
- current_agent = span.attributes.get("agent_id", "unknown")
220
-
221
- # Determine next agent from response (if Command object with goto)
222
- next_agent = None
223
- if isinstance(res, dict) and "goto" in res:
224
- next_agent = res["goto"]
225
- # Check if there's an error flag in the response
226
- success = not (
227
- res.get("error", False) or res.get("goto") == "__end__"
214
+ if (
215
+ tlp_span_kind == ObserveSpanKindValues.AGENT
216
+ or tlp_span_kind == ObserveSpanKindValues.WORKFLOW
217
+ ):
218
+ current_agent = span.attributes.get("agent_id", "unknown")
219
+
220
+ # Determine next agent from response (if Command object with goto)
221
+ next_agent = None
222
+ if isinstance(res, dict) and "goto" in res:
223
+ next_agent = res["goto"]
224
+ # Check if there's an error flag in the response
225
+ success = not (res.get("error", False) or res.get("goto") == "__end__")
226
+
227
+ # If we have a chain of communication, compute interpretation score
228
+ if next_agent and next_agent != "__end__":
229
+ score = compute_agent_interpretation_score(
230
+ sender_agent=current_agent,
231
+ receiver_agent=next_agent,
232
+ data=res,
233
+ )
234
+ span.set_attribute("gen_ai.ioa.agent.interpretation_score", score)
235
+ reliability = connection_tracker.record_connection(
236
+ sender=current_agent, receiver=next_agent, success=success
237
+ )
238
+ span.set_attribute(
239
+ "gen_ai.ioa.agent.connection_reliability", reliability
228
240
  )
229
241
 
230
- # If we have a chain of communication, compute interpretation score
231
- if next_agent and next_agent != "__end__":
232
- score = compute_agent_interpretation_score(
233
- sender_agent=current_agent,
234
- receiver_agent=next_agent,
235
- data=res,
236
- )
237
- span.set_attribute(
238
- "gen_ai.ioa.agent.interpretation_score", score
239
- )
240
- reliability = connection_tracker.record_connection(
241
- sender=current_agent, receiver=next_agent, success=success
242
- )
243
- span.set_attribute(
244
- "gen_ai.ioa.agent.connection_reliability", reliability
245
- )
246
-
247
- if _should_send_prompts():
242
+ if _should_send_prompts():
243
+ try:
244
+ # Try direct JSON serialization first
245
+ json_output = json.dumps(res)
246
+ except (TypeError, PydanticSerializationError, ValueError):
247
+ # Use intelligent serialization for complex objects
248
248
  try:
249
- # Try direct JSON serialization first (without custom encoder)
250
- json_output = json.dumps(res)
251
- except (TypeError, PydanticSerializationError, ValueError):
252
- # Fallback for objects that can't be directly serialized
253
- try:
254
- # Try to serialize a string representation
255
- safe_output = str(res)
256
- json_output = json.dumps(
257
- {"__str_representation__": safe_output}
258
- )
259
- except Exception:
260
- # If all serialization fails, skip output attribute
261
- json_output = None
249
+ serialized_res = _serialize_object(res)
250
+ json_output = json.dumps(serialized_res)
251
+ except Exception:
252
+ # If all serialization fails, skip output attribute
253
+ json_output = None
262
254
 
263
- if json_output and _is_json_size_valid(json_output):
264
- span.set_attribute(
265
- OBSERVE_ENTITY_OUTPUT,
266
- json_output,
267
- )
268
- TracerWrapper().span_processor_on_ending(
269
- span
270
- ) # record the response latency
255
+ if json_output and _is_json_size_valid(json_output):
256
+ span.set_attribute(
257
+ OBSERVE_ENTITY_OUTPUT,
258
+ json_output,
259
+ )
260
+ TracerWrapper().span_processor_on_ending(
261
+ span
262
+ ) # record the response latency
271
263
  except Exception as e:
272
264
  print(f"Warning: Failed to serialize output for span: {e}")
273
265
  Telemetry().log_exception(e)
@@ -293,17 +285,27 @@ def _cleanup_span(span, ctx_token):
293
285
  context_api.detach(ctx_token)
294
286
 
295
287
 
288
+ def _unwrap_structured_tool(fn):
289
+ # Unwraps StructuredTool or similar wrappers to get the underlying function
290
+ if hasattr(fn, "func") and callable(fn.func):
291
+ return fn.func
292
+ return fn
293
+
294
+
296
295
  def entity_method(
297
296
  name: Optional[str] = None,
298
297
  description: Optional[str] = None,
299
298
  version: Optional[int] = None,
299
+ protocol: Optional[str] = None,
300
300
  tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
301
301
  ) -> Callable[[F], F]:
302
302
  def decorate(fn: F) -> F:
303
+ # Unwrap StructuredTool if present
304
+ fn = _unwrap_structured_tool(fn)
303
305
  is_async = _is_async_method(fn)
304
- entity_name = name or fn.__qualname__
306
+ entity_name = name or _get_original_function_name(fn)
305
307
  if is_async:
306
- if inspect.isasyncgenfunction(fn):
308
+ if _is_async_generator(fn):
307
309
 
308
310
  @wraps(fn)
309
311
  async def async_gen_wrap(*args: Any, **kwargs: Any) -> Any:
@@ -375,7 +377,7 @@ def entity_method(
375
377
  agent_recovery_tracker.record_agent_recovery(
376
378
  entity_name
377
379
  )
378
- _handle_graph_response(span, res, tlp_span_kind)
380
+ _handle_graph_response(span, res, protocol, tlp_span_kind)
379
381
  # span will be ended in the generator
380
382
  if isinstance(res, types.GeneratorType):
381
383
  return _handle_generator(span, res)
@@ -421,7 +423,7 @@ def entity_method(
421
423
  _cleanup_span(span, ctx_token)
422
424
  return res
423
425
 
424
- return cast(F, async_wrap)
426
+ decorated = async_wrap
425
427
  else:
426
428
 
427
429
  @wraps(fn)
@@ -485,7 +487,7 @@ def entity_method(
485
487
  agent_recovery_tracker.record_agent_recovery(
486
488
  entity_name
487
489
  )
488
- _handle_graph_response(span, res, tlp_span_kind)
490
+ _handle_graph_response(span, res, protocol, tlp_span_kind)
489
491
 
490
492
  # span will be ended in the generator
491
493
  if isinstance(res, types.GeneratorType):
@@ -532,7 +534,12 @@ def entity_method(
532
534
  _cleanup_span(span, ctx_token)
533
535
  return res
534
536
 
535
- return cast(F, sync_wrap)
537
+ decorated = sync_wrap
538
+ # # If the original fn was a StructuredTool, re-wrap
539
+ if hasattr(fn, "func") and callable(fn.func):
540
+ fn.func = decorated
541
+ return fn
542
+ return decorated
536
543
 
537
544
  return decorate
538
545
 
@@ -541,6 +548,7 @@ def entity_class(
541
548
  name: Optional[str],
542
549
  description: Optional[str],
543
550
  version: Optional[int],
551
+ protocol: Optional[str],
544
552
  method_name: Optional[str],
545
553
  tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
546
554
  ):
@@ -585,16 +593,18 @@ def entity_class(
585
593
  if hasattr(cls, method_to_wrap):
586
594
  original_method = getattr(cls, method_to_wrap)
587
595
  # Only wrap actual functions defined in this class
588
- if inspect.isfunction(original_method):
596
+ unwrapped_method = _unwrap_structured_tool(original_method)
597
+ if inspect.isfunction(unwrapped_method):
589
598
  try:
590
599
  # Verify the method has a proper signature
591
- sig = inspect.signature(original_method)
600
+ sig = inspect.signature(unwrapped_method)
592
601
  wrapped_method = entity_method(
593
602
  name=f"{task_name}.{method_to_wrap}",
594
603
  description=description,
595
604
  version=version,
605
+ protocol=protocol,
596
606
  tlp_span_kind=tlp_span_kind,
597
- )(original_method)
607
+ )(unwrapped_method)
598
608
  # Set the wrapped method on the class
599
609
  setattr(cls, method_to_wrap, wrapped_method)
600
610
  except Exception:
@@ -654,15 +664,17 @@ def _handle_execution_result(span, success):
654
664
  return
655
665
 
656
666
 
657
- def _handle_graph_response(span, res, tlp_span_kind):
667
+ def _handle_graph_response(span, res, protocol, tlp_span_kind):
658
668
  if tlp_span_kind == "graph":
669
+ if protocol:
670
+ protocol = protocol.upper()
671
+ span.set_attribute("gen_ai.ioa.graph.protocol", protocol)
659
672
  # Check if the response is a Llama Index Workflow object
660
673
  graph = determine_workflow_type(res)
661
674
  if graph is not None:
662
675
  # Convert the graph to JSON string
663
676
  graph_json = json.dumps(graph, indent=2)
664
677
  span.set_attribute("gen_ai.ioa.graph", graph_json)
665
-
666
678
  # get graph dynamism
667
679
  dynamism = topology_dynamism(graph)
668
680
  span.set_attribute("gen_ai.ioa.graph_dynamism", dynamism)
@@ -0,0 +1,66 @@
1
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ import inspect
5
+
6
+
7
+ def _is_async_method(fn):
8
+ # check if co-routine function or async generator( example : using async & yield)
9
+ if inspect.iscoroutinefunction(fn) or inspect.isasyncgenfunction(fn):
10
+ return True
11
+
12
+ # Check if this is a wrapped function that might hide the original async nature
13
+ # Look for common wrapper attributes that might contain the original function
14
+ for attr_name in ["__wrapped__", "func", "_func", "function"]:
15
+ if hasattr(fn, attr_name):
16
+ wrapped_fn = getattr(fn, attr_name)
17
+ if wrapped_fn and callable(wrapped_fn):
18
+ if inspect.iscoroutinefunction(
19
+ wrapped_fn
20
+ ) or inspect.isasyncgenfunction(wrapped_fn):
21
+ return True
22
+ # Recursively check in case of multiple levels of wrapping
23
+ if _is_async_method(wrapped_fn):
24
+ return True
25
+
26
+ return False
27
+
28
+
29
+ def _is_async_generator(fn):
30
+ """Check if function is an async generator, looking through wrapped functions"""
31
+ if inspect.isasyncgenfunction(fn):
32
+ return True
33
+
34
+ # Check if this is a wrapped function that might hide the original async generator nature
35
+ for attr_name in ["__wrapped__", "func", "_func", "function"]:
36
+ if hasattr(fn, attr_name):
37
+ wrapped_fn = getattr(fn, attr_name)
38
+ if wrapped_fn and callable(wrapped_fn):
39
+ if inspect.isasyncgenfunction(wrapped_fn):
40
+ return True
41
+ # Recursively check in case of multiple levels of wrapping
42
+ if _is_async_generator(wrapped_fn):
43
+ return True
44
+
45
+ return False
46
+
47
+
48
+ def _get_original_function_name(fn):
49
+ """Extract the original function name from potentially wrapped functions"""
50
+ if hasattr(fn, "__qualname__") and fn.__qualname__:
51
+ return fn.__qualname__
52
+
53
+ # Look for the original function in common wrapper attributes
54
+ for attr_name in ["__wrapped__", "func", "_func", "function"]:
55
+ if hasattr(fn, attr_name):
56
+ wrapped_fn = getattr(fn, attr_name)
57
+ if wrapped_fn and callable(wrapped_fn):
58
+ if hasattr(wrapped_fn, "__qualname__") and wrapped_fn.__qualname__:
59
+ return wrapped_fn.__qualname__
60
+ # Recursively check in case of multiple levels of wrapping
61
+ result = _get_original_function_name(wrapped_fn)
62
+ if result:
63
+ return result
64
+
65
+ # Fallback to function name if qualname is not available
66
+ return getattr(fn, "__name__", "unknown_function")
@@ -13,6 +13,129 @@ from llama_index.core.workflow.utils import (
13
13
  )
14
14
 
15
15
 
16
+ def _serialize_object(obj, max_depth=3, current_depth=0):
17
+ """
18
+ Intelligently serialize an object to a more meaningful representation
19
+ """
20
+ if current_depth > max_depth:
21
+ return f"<{type(obj).__name__}:max_depth_reached>"
22
+
23
+ # Handle basic JSON-serializable types
24
+ if obj is None or isinstance(obj, (bool, int, float, str)):
25
+ return obj
26
+
27
+ # Handle lists and tuples
28
+ if isinstance(obj, (list, tuple)):
29
+ try:
30
+ return [
31
+ _serialize_object(item, max_depth, current_depth + 1)
32
+ for item in obj[:10]
33
+ ] # Limit to first 10 items
34
+ except Exception:
35
+ return f"<{type(obj).__name__}:length={len(obj)}>"
36
+
37
+ # Handle dictionaries
38
+ if isinstance(obj, dict):
39
+ try:
40
+ serialized = {}
41
+ for key, value in list(obj.items())[:10]: # Limit to first 10 items
42
+ serialized[str(key)] = _serialize_object(
43
+ value, max_depth, current_depth + 1
44
+ )
45
+ return serialized
46
+ except Exception:
47
+ return f"<dict:keys={len(obj)}>"
48
+
49
+ # Handle common object types with meaningful attributes
50
+ try:
51
+ # Check class attributes first
52
+ class_attrs = {}
53
+ for attr_name in dir(type(obj)):
54
+ if (
55
+ not attr_name.startswith("_")
56
+ and not callable(getattr(type(obj), attr_name, None))
57
+ and hasattr(obj, attr_name)
58
+ ):
59
+ try:
60
+ attr_value = getattr(obj, attr_name)
61
+ if not callable(attr_value):
62
+ class_attrs[attr_name] = _serialize_object(
63
+ attr_value, max_depth, current_depth + 1
64
+ )
65
+ if len(class_attrs) >= 5: # Limit attributes
66
+ break
67
+ except Exception:
68
+ continue
69
+
70
+ # Check if object has a __dict__ with interesting attributes
71
+ instance_attrs = {}
72
+ if hasattr(obj, "__dict__"):
73
+ obj_dict = obj.__dict__
74
+ if obj_dict:
75
+ # Extract meaningful attributes (skip private ones and callables)
76
+ for key, value in obj_dict.items():
77
+ if not key.startswith("_") and not callable(value):
78
+ try:
79
+ instance_attrs[key] = _serialize_object(
80
+ value, max_depth, current_depth + 1
81
+ )
82
+ if len(instance_attrs) >= 5: # Limit attributes
83
+ break
84
+ except Exception:
85
+ continue
86
+
87
+ # Combine class and instance attributes
88
+ all_attrs = {**class_attrs, **instance_attrs}
89
+
90
+ if all_attrs:
91
+ return {
92
+ "__class__": type(obj).__name__,
93
+ "__module__": getattr(type(obj), "__module__", "unknown"),
94
+ "attributes": all_attrs,
95
+ }
96
+
97
+ # Special handling for specific types
98
+ if hasattr(obj, "message") and hasattr(obj.message, "parts"):
99
+ # Handle RequestContext-like objects
100
+ try:
101
+ parts_content = []
102
+ for part in obj.message.parts:
103
+ if hasattr(part, "root") and hasattr(part.root, "text"):
104
+ parts_content.append(part.root.text)
105
+ return {
106
+ "__class__": type(obj).__name__,
107
+ "message_content": parts_content,
108
+ }
109
+ except Exception:
110
+ pass
111
+
112
+ # Check for common readable attributes
113
+ for attr in ["name", "id", "type", "value", "content", "text", "data"]:
114
+ if hasattr(obj, attr):
115
+ try:
116
+ attr_value = getattr(obj, attr)
117
+ if not callable(attr_value):
118
+ return {
119
+ "__class__": type(obj).__name__,
120
+ attr: _serialize_object(
121
+ attr_value, max_depth, current_depth + 1
122
+ ),
123
+ }
124
+ except Exception:
125
+ continue
126
+
127
+ # Fallback to class information
128
+ return {
129
+ "__class__": type(obj).__name__,
130
+ "__module__": getattr(type(obj), "__module__", "unknown"),
131
+ "__repr__": str(obj)[:100] + ("..." if len(str(obj)) > 100 else ""),
132
+ }
133
+
134
+ except Exception:
135
+ # Final fallback
136
+ return f"<{type(obj).__name__}:serialization_failed>"
137
+
138
+
16
139
  def determine_workflow_type(workflow_obj: Any) -> Union[None, dict]:
17
140
  """Determines the workflow type and generates appropriate topology."""
18
141
  # Check if it's a dict mapping agent roles to agent names
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ioa-observe-sdk
3
- Version: 1.0.12
3
+ Version: 1.0.14
4
4
  Summary: IOA Observability SDK
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -15,17 +15,17 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.33.1
15
15
  Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.33.1
16
16
  Requires-Dist: opentelemetry-instrumentation
17
17
  Requires-Dist: opentelemetry-instrumentation-logging==0.54b1
18
- Requires-Dist: opentelemetry-instrumentation-openai==0.40.8
19
- Requires-Dist: opentelemetry-instrumentation-llamaindex==0.40.8
20
- Requires-Dist: opentelemetry-instrumentation-ollama==0.40.8
21
- Requires-Dist: opentelemetry-instrumentation-anthropic==0.40.8
22
- Requires-Dist: opentelemetry-instrumentation-langchain==0.40.8
18
+ Requires-Dist: opentelemetry-instrumentation-openai==0.43.1
19
+ Requires-Dist: opentelemetry-instrumentation-llamaindex==0.43.1
20
+ Requires-Dist: opentelemetry-instrumentation-ollama==0.43.1
21
+ Requires-Dist: opentelemetry-instrumentation-anthropic==0.43.1
22
+ Requires-Dist: opentelemetry-instrumentation-langchain==0.43.1
23
23
  Requires-Dist: opentelemetry-instrumentation-threading==00.54b1
24
24
  Requires-Dist: opentelemetry-instrumentation-urllib3==0.54b1
25
25
  Requires-Dist: opentelemetry-proto==1.33.1
26
26
  Requires-Dist: opentelemetry-sdk==1.33.1
27
27
  Requires-Dist: opentelemetry-semantic-conventions==0.54b1
28
- Requires-Dist: opentelemetry-semantic-conventions-ai==0.4.9
28
+ Requires-Dist: opentelemetry-semantic-conventions-ai>=0.4.11
29
29
  Requires-Dist: opentelemetry-util-http==0.54b1
30
30
  Requires-Dist: langgraph>=0.3.2
31
31
  Requires-Dist: langchain>=0.3.19
@@ -9,9 +9,10 @@ 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=Lv5EbouBazvWaYB0N82v26pqKtj2FAqlwfLKEh5e8Q0,3251
13
- ioa_observe/sdk/decorators/base.py,sha256=kR_0KWMMbOdJ_t9m5EZ9ZUj3pmKWEgkj0-E1F3c-nIE,29969
14
- ioa_observe/sdk/decorators/util.py,sha256=WMkzmwD7Js0g1BbId9_qR4pwhnaIJdW588zVc5dpqdQ,25399
12
+ ioa_observe/sdk/decorators/__init__.py,sha256=c8gzCG-CASNI61RiTpNiTvMfguWvn4zc25fhBBgbHFA,3626
13
+ ioa_observe/sdk/decorators/base.py,sha256=KbSxFUTP_qpZA0gzt_xe4pFTu9SLpAo1MbHskQ3BQ9A,30208
14
+ ioa_observe/sdk/decorators/helpers.py,sha256=I9HXMBivkZpGDtPe9Ad_UU35p_m_wEPate4r_fU0oOA,2705
15
+ ioa_observe/sdk/decorators/util.py,sha256=IebvH9gwZN1en3LblYJUh4bAV2STl6xmp8WpZzBDH2g,30068
15
16
  ioa_observe/sdk/instrumentations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
17
  ioa_observe/sdk/instrumentations/a2a.py,sha256=ov_9ckkymf_qFXG0iXVWfxlW-3kFcP-knrM_t-Cf72w,4414
17
18
  ioa_observe/sdk/instrumentations/slim.py,sha256=J5e6XeshH55xXaUiT9_j4R_n6VQELzBjgRAU-AgZGOg,11435
@@ -38,8 +39,8 @@ ioa_observe/sdk/utils/const.py,sha256=GwbHakKPjBL4wLqAVkDrSoKB-8p18EUrbaqPuRuV_x
38
39
  ioa_observe/sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
39
40
  ioa_observe/sdk/utils/json_encoder.py,sha256=g4NQ0tTqgWssY6I1D7r4zo0G6PiUo61jhofTAw5-jno,639
40
41
  ioa_observe/sdk/utils/package_check.py,sha256=1d1MjxhwoEZIx9dumirT2pRsEWgn-m-SI4npDeEalew,576
41
- ioa_observe_sdk-1.0.12.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
42
- ioa_observe_sdk-1.0.12.dist-info/METADATA,sha256=3owAgvI_xgARwMohc3ticBRqKwju2w-poBOu1SILHC4,6109
43
- ioa_observe_sdk-1.0.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- ioa_observe_sdk-1.0.12.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
45
- ioa_observe_sdk-1.0.12.dist-info/RECORD,,
42
+ ioa_observe_sdk-1.0.14.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
43
+ ioa_observe_sdk-1.0.14.dist-info/METADATA,sha256=SaSIS2l9wA9A4KRRazaD33ZmMPzLsjNKvqeFl50ko0Y,6110
44
+ ioa_observe_sdk-1.0.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
+ ioa_observe_sdk-1.0.14.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
46
+ ioa_observe_sdk-1.0.14.dist-info/RECORD,,