ioa-observe-sdk 1.0.18__tar.gz → 1.0.20__tar.gz

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.
Files changed (58) hide show
  1. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/PKG-INFO +1 -1
  2. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/decorators/__init__.py +19 -1
  3. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/decorators/base.py +58 -8
  4. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/instrumentations/slim.py +10 -1
  5. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/tracing/tracing.py +8 -0
  6. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe_sdk.egg-info/PKG-INFO +1 -1
  7. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/pyproject.toml +1 -1
  8. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/LICENSE.md +0 -0
  9. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/README.md +0 -0
  10. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/__init__.py +0 -0
  11. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/__init__.py +0 -0
  12. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/client/__init__.py +0 -0
  13. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/client/client.py +0 -0
  14. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/client/http.py +0 -0
  15. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/config/__init__.py +0 -0
  16. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/connectors/__init__.py +0 -0
  17. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/connectors/slim.py +0 -0
  18. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/decorators/helpers.py +0 -0
  19. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/decorators/util.py +0 -0
  20. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/instrumentations/__init__.py +0 -0
  21. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/instrumentations/a2a.py +0 -0
  22. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/instrumentations/mcp.py +0 -0
  23. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/instruments.py +0 -0
  24. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/logging/__init__.py +0 -0
  25. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/logging/logging.py +0 -0
  26. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/__init__.py +0 -0
  27. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agent.py +0 -0
  28. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agents/__init__.py +0 -0
  29. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agents/agent_connections.py +0 -0
  30. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agents/availability.py +0 -0
  31. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agents/heuristics.py +0 -0
  32. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agents/recovery_tracker.py +0 -0
  33. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agents/tool_call_tracker.py +0 -0
  34. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/agents/tracker.py +0 -0
  35. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/metrics/metrics.py +0 -0
  36. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/telemetry.py +0 -0
  37. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/tracing/__init__.py +0 -0
  38. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/tracing/content_allow_list.py +0 -0
  39. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/tracing/context_manager.py +0 -0
  40. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/tracing/context_utils.py +0 -0
  41. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/tracing/manual.py +0 -0
  42. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/tracing/transform_span.py +0 -0
  43. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/utils/__init__.py +0 -0
  44. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/utils/const.py +0 -0
  45. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/utils/in_memory_span_exporter.py +0 -0
  46. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/utils/json_encoder.py +0 -0
  47. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/utils/package_check.py +0 -0
  48. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe/sdk/version.py +0 -0
  49. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe_sdk.egg-info/SOURCES.txt +0 -0
  50. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe_sdk.egg-info/dependency_links.txt +0 -0
  51. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe_sdk.egg-info/requires.txt +0 -0
  52. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/ioa_observe_sdk.egg-info/top_level.txt +0 -0
  53. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/setup.cfg +0 -0
  54. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/tests/test_client.py +0 -0
  55. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/tests/test_instrumentor.py +0 -0
  56. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/tests/test_manual_instrumentation.py +0 -0
  57. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/tests/test_transform_span.py +0 -0
  58. {ioa_observe_sdk-1.0.18 → ioa_observe_sdk-1.0.20}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ioa-observe-sdk
3
- Version: 1.0.18
3
+ Version: 1.0.20
4
4
  Summary: IOA Observability SDK
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -22,6 +22,7 @@ def task(
22
22
  description: Optional[str] = None,
23
23
  version: Optional[int] = None,
24
24
  protocol: Optional[str] = None,
25
+ application_id: Optional[str] = None,
25
26
  method_name: Optional[str] = None,
26
27
  tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
27
28
  ) -> Callable[[F], F]:
@@ -31,6 +32,7 @@ def task(
31
32
  description=description,
32
33
  version=version,
33
34
  protocol=protocol,
35
+ application_id=application_id,
34
36
  tlp_span_kind=tlp_span_kind,
35
37
  )
36
38
  else:
@@ -39,6 +41,7 @@ def task(
39
41
  description=description,
40
42
  version=version,
41
43
  protocol=protocol,
44
+ application_id=application_id,
42
45
  method_name=method_name,
43
46
  tlp_span_kind=tlp_span_kind,
44
47
  )
@@ -49,6 +52,7 @@ def workflow(
49
52
  description: Optional[str] = None,
50
53
  version: Optional[int] = None,
51
54
  protocol: Optional[str] = None,
55
+ application_id: Optional[str] = None,
52
56
  method_name: Optional[str] = None,
53
57
  tlp_span_kind: Optional[
54
58
  Union[ObserveSpanKindValues, str]
@@ -62,6 +66,7 @@ def workflow(
62
66
  description=description,
63
67
  version=version,
64
68
  protocol=protocol,
69
+ application_id=application_id,
65
70
  method_name=method_name,
66
71
  tlp_span_kind=tlp_span_kind,
67
72
  )(target)
@@ -72,6 +77,7 @@ def workflow(
72
77
  description=description,
73
78
  version=version,
74
79
  protocol=protocol,
80
+ application_id=application_id,
75
81
  tlp_span_kind=tlp_span_kind,
76
82
  )(target)
77
83
 
@@ -82,19 +88,27 @@ def graph(
82
88
  name: Optional[str] = None,
83
89
  description: Optional[str] = None,
84
90
  version: Optional[int] = None,
91
+ application_id: Optional[str] = None,
85
92
  method_name: Optional[str] = None,
86
93
  protocol: Optional[str] = None,
87
94
  ) -> Callable[[F], F]:
88
95
  if method_name is None:
89
96
  return entity_method(
90
- name=name, version=version, protocol=protocol, tlp_span_kind="graph"
97
+ name=name,
98
+ description=description,
99
+ version=version,
100
+ protocol=protocol,
101
+ application_id=application_id,
102
+ tlp_span_kind="graph",
91
103
  )
92
104
  else:
93
105
  return entity_class(
94
106
  name=name,
107
+ description=description,
95
108
  version=version,
96
109
  method_name=method_name,
97
110
  protocol=protocol,
111
+ application_id=application_id,
98
112
  tlp_span_kind="graph",
99
113
  )
100
114
 
@@ -104,6 +118,7 @@ def agent(
104
118
  description: Optional[str] = None,
105
119
  version: Optional[int] = None,
106
120
  protocol: Optional[str] = None,
121
+ application_id: Optional[str] = None,
107
122
  method_name: Optional[str] = None,
108
123
  ) -> Callable[[F], F]:
109
124
  return workflow(
@@ -111,6 +126,7 @@ def agent(
111
126
  description=description,
112
127
  version=version,
113
128
  protocol=protocol,
129
+ application_id=application_id,
114
130
  method_name=method_name,
115
131
  tlp_span_kind=ObserveSpanKindValues.AGENT,
116
132
  )
@@ -120,12 +136,14 @@ def tool(
120
136
  name: Optional[str] = None,
121
137
  description: Optional[str] = None,
122
138
  version: Optional[int] = None,
139
+ application_id: Optional[str] = None,
123
140
  method_name: Optional[str] = None,
124
141
  ) -> Callable[[F], F]:
125
142
  return task(
126
143
  name=name,
127
144
  description=description,
128
145
  version=version,
146
+ application_id=application_id,
129
147
  method_name=method_name,
130
148
  tlp_span_kind=ObserveSpanKindValues.TOOL,
131
149
  )
@@ -16,10 +16,10 @@ from ioa_observe.sdk.decorators.helpers import (
16
16
  _is_async_generator,
17
17
  )
18
18
 
19
-
20
19
  from langgraph.graph.state import CompiledStateGraph
21
20
  from opentelemetry import trace
22
21
  from opentelemetry import context as context_api
22
+ from opentelemetry.context import get_value, attach, set_value
23
23
  from pydantic_core import PydanticSerializationError
24
24
  from typing_extensions import ParamSpec
25
25
 
@@ -36,6 +36,7 @@ from ioa_observe.sdk.tracing.tracing import (
36
36
  set_entity_path,
37
37
  get_chained_entity_path,
38
38
  set_agent_id_event,
39
+ set_application_id,
39
40
  )
40
41
  from ioa_observe.sdk.metrics.agents.agent_connections import connection_reliability
41
42
  from ioa_observe.sdk.utils import camel_to_snake
@@ -91,6 +92,7 @@ def _setup_span(
91
92
  tlp_span_kind: Optional[ObserveSpanKindValues] = None,
92
93
  version: Optional[int] = None,
93
94
  description: Optional[str] = None,
95
+ application_id: Optional[str] = None,
94
96
  ):
95
97
  """Sets up the OpenTelemetry span and context"""
96
98
  if tlp_span_kind in [
@@ -104,16 +106,56 @@ def _setup_span(
104
106
  # set_session_id(session_id)
105
107
  if tlp_span_kind == "graph":
106
108
  span_name = f"{entity_name}.{tlp_span_kind}"
109
+
107
110
  else:
108
111
  span_name = f"{entity_name}.{tlp_span_kind.value}"
109
112
 
110
113
  with get_tracer() as tracer:
111
114
  span = tracer.start_span(span_name)
112
115
  ctx = trace.set_span_in_context(span)
116
+
117
+ # Preserve existing context values before attaching new context
118
+ session_id = get_value("session.id")
119
+ current_traceparent = get_value("current_traceparent")
120
+ agent_id = get_value("agent_id")
121
+ application_id_ctx = get_value("application_id")
122
+ association_properties = get_value("association_properties")
123
+ managed_prompt = get_value("managed_prompt")
124
+ prompt_key = get_value("prompt_key")
125
+ prompt_version = get_value("prompt_version")
126
+ prompt_version_name = get_value("prompt_version_name")
127
+ prompt_version_hash = get_value("prompt_version_hash")
128
+ prompt_template = get_value("prompt_template")
129
+ prompt_template_variables = get_value("prompt_template_variables")
130
+
113
131
  ctx_token = context_api.attach(ctx)
114
- span.set_attribute(
115
- "agent_id", entity_name
116
- ) if tlp_span_kind == ObserveSpanKindValues.AGENT else None
132
+
133
+ # Re-attach preserved context values to the new context
134
+ if session_id is not None:
135
+ attach(set_value("session.id", session_id))
136
+ if current_traceparent is not None:
137
+ attach(set_value("current_traceparent", current_traceparent))
138
+ if agent_id is not None:
139
+ attach(set_value("agent_id", agent_id))
140
+ if application_id_ctx is not None:
141
+ attach(set_value("application_id", application_id_ctx))
142
+ if association_properties is not None:
143
+ attach(set_value("association_properties", association_properties))
144
+ if managed_prompt is not None:
145
+ attach(set_value("managed_prompt", managed_prompt))
146
+ if prompt_key is not None:
147
+ attach(set_value("prompt_key", prompt_key))
148
+ if prompt_version is not None:
149
+ attach(set_value("prompt_version", prompt_version))
150
+ if prompt_version_name is not None:
151
+ attach(set_value("prompt_version_name", prompt_version_name))
152
+ if prompt_version_hash is not None:
153
+ attach(set_value("prompt_version_hash", prompt_version_hash))
154
+ if prompt_template is not None:
155
+ attach(set_value("prompt_template", prompt_template))
156
+ if prompt_template_variables is not None:
157
+ attach(set_value("prompt_template_variables", prompt_template_variables))
158
+
117
159
  if tlp_span_kind == ObserveSpanKindValues.AGENT:
118
160
  with trace.get_tracer(__name__).start_span(
119
161
  "agent_start_event", context=trace.set_span_in_context(span)
@@ -127,9 +169,6 @@ def _setup_span(
127
169
  },
128
170
  )
129
171
  # start_span.end() # end the span immediately
130
- # session_id = get_value("session.id")
131
- # if session_id is not None:
132
- # span.set_attribute("session.id", session_id)
133
172
  if tlp_span_kind in [
134
173
  ObserveSpanKindValues.TASK,
135
174
  ObserveSpanKindValues.TOOL,
@@ -148,7 +187,11 @@ def _setup_span(
148
187
 
149
188
  if tlp_span_kind == ObserveSpanKindValues.AGENT:
150
189
  span.set_attribute("agent_chain_start_time", time.time())
151
-
190
+ if application_id:
191
+ set_application_id(application_id)
192
+ span.set_attribute(
193
+ "application_id", application_id
194
+ ) # set application id attribute
152
195
  return span, ctx, ctx_token
153
196
 
154
197
 
@@ -297,6 +340,7 @@ def entity_method(
297
340
  description: Optional[str] = None,
298
341
  version: Optional[int] = None,
299
342
  protocol: Optional[str] = None,
343
+ application_id: Optional[str] = None,
300
344
  tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
301
345
  ) -> Callable[[F], F]:
302
346
  def decorate(fn: F) -> F:
@@ -319,6 +363,7 @@ def entity_method(
319
363
  tlp_span_kind,
320
364
  version,
321
365
  description,
366
+ application_id,
322
367
  )
323
368
  _handle_span_input(span, args, kwargs, cls=JSONEncoder)
324
369
 
@@ -340,6 +385,7 @@ def entity_method(
340
385
  tlp_span_kind,
341
386
  version,
342
387
  description,
388
+ application_id,
343
389
  )
344
390
 
345
391
  # Handle case where span setup failed
@@ -436,6 +482,7 @@ def entity_method(
436
482
  tlp_span_kind,
437
483
  version,
438
484
  description,
485
+ application_id,
439
486
  )
440
487
 
441
488
  # Handle case where span setup failed
@@ -549,6 +596,7 @@ def entity_class(
549
596
  description: Optional[str],
550
597
  version: Optional[int],
551
598
  protocol: Optional[str],
599
+ application_id: Optional[str],
552
600
  method_name: Optional[str],
553
601
  tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
554
602
  ):
@@ -603,6 +651,7 @@ def entity_class(
603
651
  description=description,
604
652
  version=version,
605
653
  protocol=protocol,
654
+ application_id=application_id,
606
655
  tlp_span_kind=tlp_span_kind,
607
656
  )(unwrapped_method)
608
657
  # Set the wrapped method on the class
@@ -619,6 +668,7 @@ def entity_class(
619
668
  def _handle_agent_span(span, entity_name, description, tlp_span_kind):
620
669
  if tlp_span_kind == ObserveSpanKindValues.AGENT:
621
670
  try:
671
+ span.set_attribute("agent_id", entity_name) # set the agent id attribute
622
672
  set_agent_id_event(entity_name)
623
673
  span.add_event(
624
674
  "agent_start_event",
@@ -349,8 +349,17 @@ class SLIMInstrumentor(BaseInstrumentor):
349
349
  # Store in kv_store with thread safety
350
350
  with _kv_lock:
351
351
  kv_store.set(f"execution.{traceparent}", session_id)
352
- finally:
352
+
353
+ # DON'T detach the context yet - we need it to persist for the callback
354
+ # The context will be cleaned up later or by the garbage collector
355
+
356
+ except Exception as e:
357
+ # Only detach on error
353
358
  context.detach(token)
359
+ raise e
360
+ elif traceparent and session_id and session_id != "None":
361
+ # Even without carrier context, set session ID if we have the data
362
+ set_session_id(session_id, traceparent=traceparent)
354
363
 
355
364
  # Fallback: check stored execution ID if not found in headers
356
365
  if traceparent and (not session_id or session_id == "None"):
@@ -291,6 +291,10 @@ class TracerWrapper(object):
291
291
  if agent_id is not None:
292
292
  span.set_attribute("agent_id", agent_id)
293
293
 
294
+ application_id = get_value("application_id")
295
+ if application_id is not None:
296
+ span.set_attribute("application_id", application_id)
297
+
294
298
  if is_llm_span(span):
295
299
  self.llm_call_counter.add(1, attributes=span.attributes)
296
300
 
@@ -736,6 +740,10 @@ def set_agent_id_event(agent_id: str) -> None:
736
740
  attach(set_value("agent_id", agent_id))
737
741
 
738
742
 
743
+ def set_application_id(app_id: str) -> None:
744
+ attach(set_value("application_id", app_id))
745
+
746
+
739
747
  def set_entity_path(entity_path: str) -> None:
740
748
  attach(set_value("entity_path", entity_path))
741
749
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ioa-observe-sdk
3
- Version: 1.0.18
3
+ Version: 1.0.20
4
4
  Summary: IOA Observability SDK
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "ioa-observe-sdk"
8
- version = "1.0.18"
8
+ version = "1.0.20"
9
9
  description = "IOA Observability SDK"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.10"