openinference-instrumentation-beeai 0.1.8__py3-none-any.whl → 0.1.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,11 @@
1
+ import contextlib
1
2
  import logging
2
3
  from importlib.metadata import PackageNotFoundError, version
3
- from typing import TYPE_CHECKING, Any, Callable, Collection
4
+ from typing import TYPE_CHECKING, Any, Callable, Collection, Generator
5
+
6
+ from opentelemetry.trace import StatusCode
7
+
8
+ from openinference.instrumentation._spans import OpenInferenceSpan
4
9
 
5
10
  if TYPE_CHECKING:
6
11
  from beeai_framework.emitter import EventMeta
@@ -27,12 +32,13 @@ except PackageNotFoundError:
27
32
 
28
33
 
29
34
  class BeeAIInstrumentor(BaseInstrumentor): # type: ignore
30
- __slots__ = ("_tracer", "_cleanup", "_processes")
35
+ __slots__ = ("_tracer", "_cleanup", "_processes", "_processes_deps")
31
36
 
32
37
  def __init__(self, *args: Any, **kwargs: Any) -> None:
33
38
  super().__init__(*args, **kwargs)
34
39
  self._cleanup: Callable[[], None] = lambda: None
35
40
  self._processes: dict[str, Processor] = {}
41
+ self._processes_deps: dict[str, list[Processor]] = {}
36
42
 
37
43
  def instrumentation_dependencies(self) -> Collection[str]:
38
44
  return _instruments
@@ -64,8 +70,16 @@ class BeeAIInstrumentor(BaseInstrumentor): # type: ignore
64
70
  def _uninstrument(self, **kwargs: Any) -> None:
65
71
  self._cleanup()
66
72
  self._processes.clear()
73
+ self._processes_deps.clear()
67
74
 
68
- def _build_tree(self, node: SpanWrapper) -> None:
75
+ def _build_tree(self, processor: Processor) -> None:
76
+ with self._build_tree_for_span(processor.span):
77
+ for child in self._processes_deps.pop(processor.run_id):
78
+ self._build_tree(child)
79
+ self._processes.pop(processor.run_id)
80
+
81
+ @contextlib.contextmanager
82
+ def _build_tree_for_span(self, node: SpanWrapper) -> Generator[OpenInferenceSpan, None, None]:
69
83
  with self._tracer.start_as_current_span(
70
84
  name=node.name,
71
85
  openinference_span_kind=node.kind,
@@ -73,23 +87,27 @@ class BeeAIInstrumentor(BaseInstrumentor): # type: ignore
73
87
  start_time=_datetime_to_span_time(node.started_at) if node.started_at else None,
74
88
  end_on_exit=False, # we do it manually
75
89
  ) as current_span:
90
+ yield current_span
91
+
76
92
  for event in node.events:
77
93
  current_span.add_event(
78
94
  name=event.name, attributes=event.attributes, timestamp=event.timestamp
79
95
  )
80
96
 
81
97
  for children in node.children:
82
- self._build_tree(children)
98
+ with self._build_tree_for_span(children):
99
+ pass
83
100
 
84
101
  current_span.set_status(node.status)
85
- if node.error is not None:
102
+ if node.error is not None and node.status == StatusCode.ERROR:
86
103
  current_span.record_exception(node.error)
87
104
 
88
105
  current_span.end(_datetime_to_span_time(node.ended_at) if node.ended_at else None)
89
106
 
90
107
  @exception_handler
91
108
  async def _handler(self, data: Any, event: "EventMeta") -> None:
92
- assert event.trace is not None, "Event must have a trace"
109
+ if event.trace is None:
110
+ return
93
111
 
94
112
  if event.trace.run_id not in self._processes:
95
113
  parent = (
@@ -100,9 +118,10 @@ class BeeAIInstrumentor(BaseInstrumentor): # type: ignore
100
118
  if event.trace.parent_run_id and not parent:
101
119
  raise ValueError(f"Parent run with ID {event.trace.parent_run_id} was not found!")
102
120
 
121
+ self._processes_deps[event.trace.run_id] = []
103
122
  node = self._processes[event.trace.run_id] = ProcessorLocator.locate(data, event)
104
123
  if parent is not None:
105
- parent.span.children.append(node.span)
124
+ self._processes_deps[parent.run_id].append(node)
106
125
  else:
107
126
  node = self._processes[event.trace.run_id]
108
127
 
@@ -110,8 +129,8 @@ class BeeAIInstrumentor(BaseInstrumentor): # type: ignore
110
129
 
111
130
  if isinstance(data, RunContextFinishEvent):
112
131
  await node.end(data, event)
113
- self._build_tree(node.span)
114
- self._processes.pop(event.trace.run_id)
132
+ if event.trace.parent_run_id is None:
133
+ self._build_tree(node)
115
134
  else:
116
135
  if event.context.get("internal"):
117
136
  return
@@ -77,5 +77,20 @@ class SpanWrapper:
77
77
  def set_status(self, status: StatusCode) -> None:
78
78
  self.status = status
79
79
 
80
+ def reset_exception(self) -> None:
81
+ self.error = None
82
+ self.set_status(StatusCode.OK)
83
+
80
84
  def record_exception(self, error: Exception) -> None:
85
+ from beeai_framework.errors import FrameworkError
86
+
81
87
  self.error = error
88
+ self.set_status(StatusCode.ERROR)
89
+ self.set_attributes(
90
+ {
91
+ SpanAttributes.OUTPUT_VALUE: error.explain()
92
+ if isinstance(error, FrameworkError)
93
+ else str(error),
94
+ SpanAttributes.OUTPUT_MIME_TYPE: OpenInferenceMimeTypeValues.TEXT.value,
95
+ }
96
+ )
@@ -22,7 +22,7 @@ def _unpack_object(obj: dict[str, Any] | list[Any] | BaseModel, prefix: str = ""
22
22
  obj_ref = obj
23
23
  obj = json.loads(stringify(obj))
24
24
  if not isinstance(obj, dict) and not isinstance(obj, list):
25
- logger.warning(f"Cannot unpack object of type {type(obj_ref)} (prefix={prefix})")
25
+ logger.debug(f"Cannot unpack object of type {type(obj_ref)} (prefix={prefix})")
26
26
  return {"value": str(obj)}
27
27
 
28
28
  if prefix and prefix.startswith("."):
@@ -34,7 +34,7 @@ def _unpack_object(obj: dict[str, Any] | list[Any] | BaseModel, prefix: str = ""
34
34
  for key, value in obj.items() if isinstance(obj, dict) else enumerate(obj):
35
35
  if value is None:
36
36
  continue
37
- if is_primitive(value):
37
+ if is_primitive(value) or has_custom_str(value):
38
38
  output[f"{prefix}{key}"] = str(value)
39
39
  else:
40
40
  output.update(_unpack_object(value, prefix=f"{prefix}{key}"))
@@ -45,6 +45,13 @@ def is_primitive(value: Any) -> bool:
45
45
  return isinstance(value, str | bool | int | float | type(None))
46
46
 
47
47
 
48
+ def has_custom_str(value: Any) -> bool:
49
+ if value.__class__.__module__ == "builtins":
50
+ return False
51
+
52
+ return value.__class__.__str__ is not object.__str__
53
+
54
+
48
55
  def stringify(value: Any, pretty: bool = False) -> str:
49
56
  if is_primitive(value):
50
57
  return str(value)
@@ -24,6 +24,9 @@ class Processor:
24
24
  assert isinstance(meta.creator, RunContext)
25
25
  target_cls = type(meta.creator.instance)
26
26
 
27
+ assert meta.trace is not None
28
+ self.run_id = meta.trace.run_id
29
+
27
30
  self.span = SpanWrapper(name=target_cls.__name__, kind=type(self).kind)
28
31
  self.span.started_at = meta.created_at
29
32
  self.span.attributes.update(
@@ -43,6 +46,9 @@ class Processor:
43
46
  pass
44
47
 
45
48
  async def end(self, event: "RunContextFinishEvent", meta: "EventMeta") -> None:
49
+ if event.error is not None:
50
+ self.span.record_exception(event.error)
51
+
46
52
  if event.output is not None:
47
53
  if SpanAttributes.OUTPUT_VALUE not in self.span.attributes:
48
54
  self.span.attributes.update(
@@ -53,8 +59,4 @@ class Processor:
53
59
  )
54
60
  self.span.set_status(StatusCode.OK)
55
61
 
56
- if event.error is not None:
57
- self.span.set_status(StatusCode.ERROR)
58
- self.span.record_exception(event.error)
59
-
60
62
  self.span.ended_at = meta.created_at
@@ -2,7 +2,7 @@ from typing import Any, ClassVar
2
2
 
3
3
  from beeai_framework.context import RunContext, RunContextStartEvent
4
4
  from beeai_framework.emitter import EventMeta
5
- from beeai_framework.tools import ToolErrorEvent, ToolRetryEvent, ToolSuccessEvent
5
+ from beeai_framework.tools import ToolErrorEvent, ToolRetryEvent, ToolStartEvent, ToolSuccessEvent
6
6
  from beeai_framework.tools.tool import Tool
7
7
  from typing_extensions import override
8
8
 
@@ -48,9 +48,14 @@ class ToolProcessor(Processor):
48
48
  self.span.add_event(f"{meta.name} ({meta.path})", timestamp=meta.created_at)
49
49
 
50
50
  match event:
51
+ case ToolStartEvent():
52
+ pass
53
+ case None: # finish event
54
+ pass
51
55
  case ToolSuccessEvent():
52
56
  output_cls = type(event.output)
53
57
 
58
+ self.span.reset_exception()
54
59
  self.span.set_attributes(
55
60
  {
56
61
  SpanAttributes.OUTPUT_VALUE: event.output.get_text_content(),
@@ -62,6 +67,7 @@ class ToolProcessor(Processor):
62
67
  case ToolErrorEvent():
63
68
  span = self.span.child(meta.name, event=(event, meta))
64
69
  span.record_exception(event.error)
70
+ self.span.record_exception(event.error)
65
71
  case ToolRetryEvent():
66
72
  self.span.child(meta.name, event=(event, meta))
67
73
  case _:
@@ -1 +1 @@
1
- __version__ = "0.1.8"
1
+ __version__ = "0.1.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openinference-instrumentation-beeai
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: OpenInference BeeAI Instrumentation
5
5
  Project-URL: Homepage, https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-beeai
6
6
  Author: IBM Corp.
@@ -1,21 +1,21 @@
1
- openinference/instrumentation/beeai/__init__.py,sha256=5_bUn-XMonUXUgbbqU_PrGyW2ojiWQD7LUvjB7B-g0A,4467
2
- openinference/instrumentation/beeai/_span.py,sha256=VZfoQHDUlAeyHxI7MnQ6QGq2LwhHiI1AQ2Uw_4Dn5Zw,2750
3
- openinference/instrumentation/beeai/_utils.py,sha256=m4ZXoGLm-iJGAJBjrfSh06OU2EaDMNgStVTLbljmPSA,2328
4
- openinference/instrumentation/beeai/version.py,sha256=C69ADlbQREQlR15trneyA2sk8x0-oH4rDAX5fsv19_U,22
1
+ openinference/instrumentation/beeai/__init__.py,sha256=yTygxcKT4VHgegEQndDrUrhjKZ9ifLsJtwZjF6A3R2M,5229
2
+ openinference/instrumentation/beeai/_span.py,sha256=iVlYou4vnNKtDxpypMdZuD2AKeaDiG1Cu5PXVzgQ8w4,3259
3
+ openinference/instrumentation/beeai/_utils.py,sha256=tfQsQEcevyLJno8WmLTOe936GVTIS2etnAFVbAPyztc,2521
4
+ openinference/instrumentation/beeai/version.py,sha256=z0zCHFTcKSR0tJ6h5qrpNmRVP21QIPP8N0p7quCnnm0,23
5
5
  openinference/instrumentation/beeai/processors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- openinference/instrumentation/beeai/processors/base.py,sha256=akaQCsZmbapEC5tctD_VXWiBOu2pYHFRJRZ_pynpApc,2176
6
+ openinference/instrumentation/beeai/processors/base.py,sha256=-h8tx1moKVi-1t3RpN-c7klGRJyHwoYe16rKWcZ_bhs,2204
7
7
  openinference/instrumentation/beeai/processors/chat.py,sha256=a4Ps6opRb2POrQ8Nla1JHUhkdVFZF5rmht8Xca3F_xA,9285
8
8
  openinference/instrumentation/beeai/processors/embedding.py,sha256=T9fZs2M7qEs4SnLYbSXRbhe3P7rCNch-snRQBDSC9Es,2598
9
9
  openinference/instrumentation/beeai/processors/locator.py,sha256=G9TFW_HgXM1TrOVdvtRU2Eq3D-atHLAET4fSo4F02X8,3635
10
10
  openinference/instrumentation/beeai/processors/requirement.py,sha256=Q9DgHDd-5rmP88Fe00d7cNKQg5zQ7ILuRzVAej5xfmk,2666
11
- openinference/instrumentation/beeai/processors/tool.py,sha256=03pNF04ELsxRl8EK3JhHktoQl_aXAY1rrXHV4QuXcJk,2533
11
+ openinference/instrumentation/beeai/processors/tool.py,sha256=o5aKAqEHZIk9bmK2rWUqtACiyMltIFCnzaunCKxRtu8,2765
12
12
  openinference/instrumentation/beeai/processors/workflow.py,sha256=OMwFFHv3mp4M4hFvH7utYd_fiSkTnBcl2oUVaMEdy-A,3815
13
13
  openinference/instrumentation/beeai/processors/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  openinference/instrumentation/beeai/processors/agents/base.py,sha256=3fidrUoU9pVixq9YN_y1jkNMozNMX-YG2CMuh855cLk,1244
15
15
  openinference/instrumentation/beeai/processors/agents/react.py,sha256=rS3xlvgyZ5G6MyDMeSh4xFLT_66h7GVAYEYlwZCpIdY,3022
16
16
  openinference/instrumentation/beeai/processors/agents/requirement_agent.py,sha256=HpleY8pNWojuUqcae2PZgat7Xq2edU9C-uz0YjZQUyc,2774
17
17
  openinference/instrumentation/beeai/processors/agents/tool_calling.py,sha256=yaWP5JmGuvZIha9iUSKgv0MJgI0QSbuiJLLQFnbqUZw,1223
18
- openinference_instrumentation_beeai-0.1.8.dist-info/METADATA,sha256=Jp_y16JgscRpObUhhtwhgRW-nG3MwTDKQIgQA_oYFxM,5491
19
- openinference_instrumentation_beeai-0.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- openinference_instrumentation_beeai-0.1.8.dist-info/entry_points.txt,sha256=ee7EUhbWv-XK1dxhPXuFVy9qstzj-lc-265Phe2Ml9s,183
21
- openinference_instrumentation_beeai-0.1.8.dist-info/RECORD,,
18
+ openinference_instrumentation_beeai-0.1.10.dist-info/METADATA,sha256=-Md5ntckIaPomS9iTDOZl55PiZVQS469lj40brz9dG8,5492
19
+ openinference_instrumentation_beeai-0.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
+ openinference_instrumentation_beeai-0.1.10.dist-info/entry_points.txt,sha256=ee7EUhbWv-XK1dxhPXuFVy9qstzj-lc-265Phe2Ml9s,183
21
+ openinference_instrumentation_beeai-0.1.10.dist-info/RECORD,,