ioa-observe-sdk 1.0.15__py3-none-any.whl → 1.0.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.
@@ -38,6 +38,10 @@ from opentelemetry.semconv_ai import SpanAttributes
38
38
  from ioa_observe.sdk import Telemetry
39
39
  from ioa_observe.sdk.instruments import Instruments
40
40
  from ioa_observe.sdk.tracing.content_allow_list import ContentAllowList
41
+ from ioa_observe.sdk.tracing.transform_span import (
42
+ transform_json_object_configurable,
43
+ validate_transformer_rules,
44
+ )
41
45
  from ioa_observe.sdk.utils import is_notebook
42
46
  from ioa_observe.sdk.client import kv_store
43
47
 
@@ -106,8 +110,8 @@ def determine_reliability_score(span):
106
110
  class TracerWrapper(object):
107
111
  resource_attributes: dict = {}
108
112
  enable_content_tracing: bool = True
109
- endpoint: str = (None,)
110
- app_name: str = (None,)
113
+ endpoint: str = None
114
+ app_name: str = None
111
115
  headers: Dict[str, str] = {}
112
116
  __tracer_provider: TracerProvider = None
113
117
  __disabled: bool = False
@@ -129,7 +133,10 @@ class TracerWrapper(object):
129
133
  return obj
130
134
 
131
135
  obj.__image_uploader = image_uploader
132
- obj._agent_execution_counts = {} # {(agent_name): [success_count, total_count]}
136
+ # {(agent_name): [success_count, total_count]}
137
+ obj._agent_execution_counts = {}
138
+ # Track spans that have been processed to avoid duplicates
139
+ obj._processed_spans = set()
133
140
  TracerWrapper.app_name = TracerWrapper.resource_attributes.get(
134
141
  "service.name", "observe"
135
142
  )
@@ -139,6 +146,7 @@ class TracerWrapper(object):
139
146
  Telemetry().capture("tracer:init", {"processor": "custom"})
140
147
  obj.__spans_processor: SpanProcessor = processor
141
148
  obj.__spans_processor_original_on_start = processor.on_start
149
+ obj.__spans_processor_original_on_end = processor.on_end
142
150
  else:
143
151
  if exporter:
144
152
  Telemetry().capture(
@@ -175,9 +183,10 @@ class TracerWrapper(object):
175
183
  schedule_delay_millis=5000,
176
184
  )
177
185
  obj.__spans_processor_original_on_start = None
186
+ obj.__spans_processor_original_on_end = obj.__spans_processor.on_end
178
187
 
179
188
  obj.__spans_processor.on_start = obj._span_processor_on_start
180
- # obj.__spans_processor.on_end = obj.span_processor_on_ending
189
+ obj.__spans_processor.on_end = obj.span_processor_on_ending
181
190
  obj.__tracer_provider.add_span_processor(obj.__spans_processor)
182
191
  # Custom metric, for example
183
192
  meter = get_meter("observe")
@@ -338,14 +347,114 @@ class TracerWrapper(object):
338
347
  self.number_active_agents.add(count, attributes=span.attributes)
339
348
 
340
349
  def span_processor_on_ending(self, span):
350
+ # Check if this span has already been processed to avoid duplicate processing
351
+ # Added for avoid duplicate on_ending with manual triggers
352
+ # from decorators (@tool, @workflow) in base.py
353
+ span_id = span.context.span_id
354
+ if span_id in self._processed_spans:
355
+ # This span was already processed, skip to avoid duplicates
356
+ return
357
+
358
+ # Mark this span as processed
359
+ self._processed_spans.add(span_id)
360
+
341
361
  determine_reliability_score(span)
342
362
  start_time = span.attributes.get("ioa_start_time")
343
- # publish span to the exporter
363
+
364
+ # Apply transformations if enabled
365
+ apply_transform = get_value("apply_transform")
366
+ if apply_transform:
367
+ transformer_rules = get_value("transformer_rules")
368
+ if transformer_rules:
369
+ try:
370
+ # Convert span to dict for transformation
371
+ span_dict = self._span_to_dict(span)
372
+ # Apply transformation
373
+ transformed_span_dict = transform_json_object_configurable(
374
+ span_dict, transformer_rules
375
+ )
376
+ # Update span with transformed data
377
+ self._update_span_from_dict(span, transformed_span_dict)
378
+ except Exception as e:
379
+ logging.error(f"Error applying span transformation: {e}")
344
380
 
345
381
  if start_time is not None:
346
382
  latency = (time.time() - start_time) * 1000
347
383
  self.response_latency_histogram.record(latency, attributes=span.attributes)
348
384
 
385
+ # Call original on_end method if it exists
386
+ if (
387
+ hasattr(self, "_TracerWrapper__spans_processor_original_on_end")
388
+ and self.__spans_processor_original_on_end
389
+ ):
390
+ self.__spans_processor_original_on_end(span)
391
+
392
+ def _span_to_dict(self, span):
393
+ """Convert span to dictionary for transformation."""
394
+ span_dict = {
395
+ "name": span.name,
396
+ "attributes": dict(span.attributes) if span.attributes else {},
397
+ "status": {
398
+ "status_code": span.status.status_code.name
399
+ if span.status and span.status.status_code
400
+ else None,
401
+ "description": span.status.description if span.status else None,
402
+ },
403
+ }
404
+ return span_dict
405
+
406
+ def _update_span_from_dict(self, span, span_dict):
407
+ """Update span with transformed data."""
408
+ # Update span name if it was transformed
409
+ if "name" in span_dict and span_dict["name"] != span.name:
410
+ # Directly modify the internal name attribute
411
+ if hasattr(span, "_name"):
412
+ span._name = span_dict["name"]
413
+
414
+ # Update attributes if they were transformed
415
+ if "attributes" in span_dict:
416
+ # Try multiple approaches to update span attributes
417
+ updated = False
418
+
419
+ # Method 1: Try using set_attribute if available and mutable
420
+ if (
421
+ hasattr(span, "set_attribute")
422
+ and hasattr(span, "_ended")
423
+ and not span._ended
424
+ ):
425
+ try:
426
+ # Clear existing attributes by setting them to None
427
+ if hasattr(span, "_attributes"):
428
+ keys_to_remove = list(span._attributes.keys())
429
+ for key in keys_to_remove:
430
+ span.set_attribute(key, None)
431
+
432
+ # Set new attributes
433
+ for key, value in span_dict["attributes"].items():
434
+ span.set_attribute(key, value)
435
+ updated = True
436
+ except (AttributeError, TypeError):
437
+ pass
438
+
439
+ # Method 2: Direct attribute manipulation
440
+ if not updated:
441
+ try:
442
+ if hasattr(span, "_attributes"):
443
+ span._attributes.clear()
444
+ span._attributes.update(span_dict["attributes"])
445
+ updated = True
446
+ elif hasattr(span, "attributes") and hasattr(
447
+ span.attributes, "clear"
448
+ ):
449
+ span.attributes.clear()
450
+ span.attributes.update(span_dict["attributes"])
451
+ updated = True
452
+ except (AttributeError, TypeError):
453
+ pass
454
+
455
+ if not updated:
456
+ logging.warning("Cannot modify span attributes - span may be finalized")
457
+
349
458
  @staticmethod
350
459
  def set_static_params(
351
460
  resource_attributes: dict,
@@ -423,14 +532,101 @@ def set_workflow_name(workflow_name: str) -> None:
423
532
  attach(set_value("workflow_name", workflow_name))
424
533
 
425
534
 
426
- def session_start():
535
+ def _parse_boolean_env(env_value: str) -> bool:
536
+ """
537
+ Parse boolean value from environment variable string.
538
+
539
+ Args:
540
+ env_value (str): Environment variable value to parse
541
+
542
+ Returns:
543
+ bool: Parsed boolean value
544
+
545
+ Accepts: "0", "1", "true", "false", "True", "False"
546
+ """
547
+ if env_value.lower() in ("true", "1"):
548
+ return True
549
+ elif env_value.lower() in ("false", "0"):
550
+ return False
551
+ else:
552
+ raise ValueError(f"Invalid boolean value: {env_value}")
553
+
554
+
555
+ def session_start(apply_transform: bool = False):
427
556
  """
428
557
  Can be used as a context manager or a normal function.
429
558
  As a context manager, yields session metadata.
430
559
  As a normal function, just sets up the session.
560
+
561
+ Args:
562
+ apply_transform (bool): If True, enables span transformation based on
563
+ rules loaded from SPAN_TRANSFORMER_RULES_FILE env.
564
+ Can be overridden by
565
+ SPAN_TRANSFORMER_RULES_ENABLED env var.
431
566
  """
432
- session_id = TracerWrapper.app_name + "_" + str(uuid.uuid4())
567
+ session_id = (TracerWrapper.app_name or "observe") + "_" + str(uuid.uuid4())
433
568
  set_session_id(session_id)
569
+
570
+ # Check if environment variable overrides the apply_transform parameter
571
+ transformer_enabled_env = os.getenv("SPAN_TRANSFORMER_RULES_ENABLED")
572
+ if transformer_enabled_env:
573
+ try:
574
+ apply_transform = _parse_boolean_env(transformer_enabled_env)
575
+ except ValueError as e:
576
+ logging.error(
577
+ "Invalid SPAN_TRANSFORMER_RULES_ENABLED value: "
578
+ f"{e}. Using parameter value: {apply_transform}"
579
+ )
580
+
581
+ # Handle transformation flag
582
+ if apply_transform:
583
+ transformer_rules_file = os.getenv("SPAN_TRANSFORMER_RULES_FILE")
584
+ if not transformer_rules_file:
585
+ logging.error(
586
+ "SPAN_TRANSFORMER_RULES_FILE environment variable "
587
+ "not set. Disabling transformation."
588
+ )
589
+ apply_transform = False
590
+ elif not os.path.exists(transformer_rules_file):
591
+ logging.error(
592
+ "Transformer rules file not found: "
593
+ f"{transformer_rules_file}. Disabling "
594
+ "transformation."
595
+ )
596
+ apply_transform = False
597
+ else:
598
+ try:
599
+ with open(transformer_rules_file, "r") as f:
600
+ transformer_rules = json.load(f)
601
+ # Validate structure and rules
602
+ validate_transformer_rules(transformer_rules)
603
+ attach(set_value("apply_transform", True))
604
+ attach(set_value("transformer_rules", transformer_rules))
605
+ except json.JSONDecodeError as e:
606
+ logging.error(
607
+ "Failed to load transformer rules from "
608
+ f"{transformer_rules_file}: {e}. Disabling "
609
+ "transformation."
610
+ )
611
+ apply_transform = False
612
+ except ValueError:
613
+ logging.error(
614
+ "Invalid transformer rules structure. "
615
+ "Expected 'RULES' section. "
616
+ "Disabling transformation."
617
+ )
618
+ apply_transform = False
619
+ except (json.JSONDecodeError, Exception) as e:
620
+ logging.error(
621
+ "Failed to load transformer rules from "
622
+ f"{transformer_rules_file}: {e}. "
623
+ "Disabling transformation."
624
+ )
625
+ apply_transform = False
626
+
627
+ if not apply_transform:
628
+ attach(set_value("apply_transform", False))
629
+
434
630
  metadata = {
435
631
  "executionID": get_value("session.id") or session_id,
436
632
  "traceparentID": get_current_traceparent(),
@@ -581,14 +777,23 @@ def is_llm_span(span) -> bool:
581
777
 
582
778
 
583
779
  def init_spans_exporter(api_endpoint: str, headers: Dict[str, str]) -> SpanExporter:
584
- if "http" in api_endpoint.lower() or "https" in api_endpoint.lower():
780
+ if api_endpoint and (
781
+ "http" in api_endpoint.lower() or "https" in api_endpoint.lower()
782
+ ):
585
783
  return HTTPExporter(
586
784
  endpoint=f"{api_endpoint}/v1/traces",
587
785
  headers=headers,
588
786
  compression=Compression.Gzip,
589
787
  )
590
- else:
788
+ elif api_endpoint:
591
789
  return GRPCExporter(endpoint=f"{api_endpoint}", headers=headers)
790
+ else:
791
+ # Default to HTTP exporter with localhost when endpoint is None
792
+ return HTTPExporter(
793
+ endpoint="http://localhost:4318/v1/traces",
794
+ headers=headers,
795
+ compression=Compression.Gzip,
796
+ )
592
797
 
593
798
 
594
799
  def init_tracer_provider(resource: Resource) -> TracerProvider:
@@ -0,0 +1,210 @@
1
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+
5
+ def validate_transformer_rules(config):
6
+ """
7
+ Validates the transformer rules configuration.
8
+
9
+ Args:
10
+ config (dict): The configuration dictionary to validate.
11
+
12
+ Raises:
13
+ ValueError: If the configuration is invalid.
14
+
15
+ Validation rules:
16
+ - Must have RULES section containing a list of transformation rules
17
+ - Each rule must have a 'path' field (list of strings)
18
+ - Path length = 1: Global transformation (e.g., ["old_key"])
19
+ - Path length > 1: Path-specific transformation
20
+ (e.g., ["attributes", "nested_key"])
21
+ - action_conflict must be one of: SKIP, REPLACE, DELETE
22
+ - DELETE action should not have a 'rename' field
23
+ """
24
+ if not isinstance(config, dict):
25
+ raise ValueError("Configuration must be a dictionary")
26
+
27
+ if "RULES" not in config:
28
+ raise ValueError("Configuration must contain 'RULES' section")
29
+
30
+ rules_section = config.get("RULES", [])
31
+ if not isinstance(rules_section, list):
32
+ raise ValueError("RULES section must be a list")
33
+
34
+ for i, rule in enumerate(rules_section):
35
+ if not isinstance(rule, dict):
36
+ raise ValueError(f"Rule {i} must be a dictionary")
37
+
38
+ # Check required fields
39
+ if "path" not in rule:
40
+ raise ValueError(f"Rule {i} must have 'path' field")
41
+
42
+ if not isinstance(rule["path"], list):
43
+ raise ValueError(f"Rule {i} 'path' must be a list")
44
+
45
+ if not rule["path"]:
46
+ raise ValueError(f"Rule {i} 'path' cannot be empty")
47
+
48
+ # Check action_conflict
49
+ action_conflict = rule.get("action_conflict", "REPLACE")
50
+ if action_conflict not in ["SKIP", "REPLACE", "DELETE"]:
51
+ raise ValueError(
52
+ f"Rule {i} action_conflict must be one of: SKIP, REPLACE, DELETE"
53
+ )
54
+
55
+ # Check rename field for non-DELETE actions
56
+ if action_conflict != "DELETE" and "rename" not in rule:
57
+ raise ValueError(
58
+ f"Rule {i} with {action_conflict} action must have 'rename' field"
59
+ )
60
+
61
+ # Check rename field for DELETE action
62
+ if action_conflict == "DELETE" and "rename" in rule:
63
+ raise ValueError(
64
+ f"Rule {i} with DELETE action should not have 'rename' field"
65
+ )
66
+
67
+
68
+ def transform_json_object_configurable(data, config, current_path=()):
69
+ """
70
+ Recursively transforms a JSON object (dict or list) based on a unified
71
+ configuration that contains transformation rules for both global and
72
+ path-specific key renames, along with conflict resolution strategies.
73
+
74
+ Args:
75
+ data (dict or list or primitive): The JSON object or part of it
76
+ to transform.
77
+ config (dict): A dictionary containing transformation rules:
78
+ {
79
+ "RULES": [
80
+ {
81
+ "path": ["old_key"],
82
+ "rename": "new_key",
83
+ "action_conflict": "SKIP"|"REPLACE"|"DELETE"
84
+ },
85
+ {
86
+ "path": ["attributes",
87
+ "traceloop.span.kind"],
88
+ "rename": "ioa_observe.span_kind",
89
+ "action_conflict": "REPLACE"
90
+ },
91
+ ...
92
+ ]
93
+ }
94
+ current_path (tuple): The current path (sequence of keys) from the
95
+ root to the current data element.
96
+ Used for path-specific lookups.
97
+
98
+ Returns:
99
+ dict or list or primitive: The transformed JSON object.
100
+ """
101
+ if isinstance(data, dict):
102
+ new_data = {}
103
+ for key, value in data.items():
104
+ full_key_path = current_path + (key,)
105
+
106
+ # Recursively transform the value first
107
+ transformed_value = transform_json_object_configurable(
108
+ value, config, full_key_path
109
+ )
110
+
111
+ # Default to no rename and 'REPLACE' conflict strategy
112
+ target_new_key = key
113
+ action_on_conflict = "REPLACE"
114
+ rule_applied = False
115
+
116
+ # Check for matching rules based on path
117
+ rules = config.get("RULES", [])
118
+ for rule in rules:
119
+ rule_path = tuple(rule["path"])
120
+
121
+ # Check if this rule applies to the current path
122
+ if len(rule_path) == 1:
123
+ # Global rule: applies if the key matches
124
+ if rule_path[0] == key:
125
+ action_on_conflict = rule.get("action_conflict", "REPLACE")
126
+
127
+ if action_on_conflict == "DELETE":
128
+ # DELETE action: skip adding this key entirely
129
+ rule_applied = True
130
+ break
131
+ elif "rename" in rule:
132
+ target_new_key = rule["rename"]
133
+ rule_applied = True
134
+ break
135
+ else:
136
+ # Path-specific rule: applies if full path matches
137
+ if full_key_path == rule_path:
138
+ action_on_conflict = rule.get("action_conflict", "REPLACE")
139
+
140
+ if action_on_conflict == "DELETE":
141
+ # DELETE action: skip adding this key entirely
142
+ rule_applied = True
143
+ break
144
+ elif "rename" in rule:
145
+ target_new_key = rule["rename"]
146
+ rule_applied = True
147
+ break
148
+
149
+ # Skip to next iteration if DELETE action was applied
150
+ if rule_applied and action_on_conflict == "DELETE":
151
+ continue
152
+
153
+ # Now, decide which key to use in new_data based on rules and
154
+ # conflict strategy
155
+ if rule_applied and target_new_key != key:
156
+ # A rename was proposed
157
+ if action_on_conflict == "SKIP" and (
158
+ target_new_key in data or target_new_key in new_data
159
+ ):
160
+ # If target key already exists in original data OR new_data
161
+ # AND action is SKIP, keep the original key and its
162
+ # transformed value. The rename is effectively "skipped".
163
+ new_data[key] = transformed_value
164
+ else:
165
+ # If action is REPLACE, or target key doesn't exist,
166
+ # then perform the rename.
167
+ # This will either add a new key or overwrite an
168
+ # existing one.
169
+ new_data[target_new_key] = transformed_value
170
+ else:
171
+ # No rule applied or DELETE handled above
172
+ new_data[key] = transformed_value
173
+
174
+ return new_data
175
+ elif isinstance(data, list):
176
+ new_list = []
177
+ for item in data:
178
+ # For list items, the path context usually doesn't change
179
+ # for the elements themselves
180
+ new_list.append(
181
+ transform_json_object_configurable(item, config, current_path)
182
+ )
183
+ return new_list
184
+ else:
185
+ # Base case: primitive types (str, int, float, bool, None)
186
+ # are returned as is
187
+ return data
188
+
189
+
190
+ def transform_list_of_json_objects_configurable(json_objects_list, config):
191
+ """
192
+ Transforms a list of JSON objects by applying key replacements based on
193
+ the provided unified configuration.
194
+
195
+ Args:
196
+ json_objects_list (list): A list of Python dictionary objects
197
+ (parsed JSON).
198
+ config (dict): The unified configuration dictionary for
199
+ transformations.
200
+
201
+ Returns:
202
+ list: A new list containing the transformed JSON objects.
203
+ """
204
+ if not isinstance(json_objects_list, list):
205
+ raise TypeError("Input must be a list of JSON objects.")
206
+
207
+ transformed_list = [
208
+ transform_json_object_configurable(obj, config) for obj in json_objects_list
209
+ ]
210
+ return transformed_list
@@ -21,6 +21,13 @@ OBSERVE_PROMPT_VERSION_HASH = "ioa_observe.prompt.version_hash"
21
21
  OBSERVE_PROMPT_TEMPLATE = "ioa_observe.prompt.template"
22
22
  OBSERVE_PROMPT_TEMPLATE_VARIABLES = "ioa_observe.prompt.template_variables"
23
23
 
24
+ # MCP
25
+ MCP_METHOD_NAME = "mcp.method.name"
26
+ MCP_REQUEST_ARGUMENT = "mcp.request.argument"
27
+ MCP_REQUEST_ID = "mcp.request.id"
28
+ MCP_SESSION_INIT_OPTIONS = "mcp.session.init_options"
29
+ MCP_RESPONSE_VALUE = "mcp.response.value"
30
+
24
31
 
25
32
  class ObserveSpanKindValues(Enum):
26
33
  WORKFLOW = "workflow"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ioa-observe-sdk
3
- Version: 1.0.15
3
+ Version: 1.0.17
4
4
  Summary: IOA Observability SDK
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -82,6 +82,8 @@ This schema is designed to provide comprehensive observability for Multi-Agent S
82
82
 
83
83
  Link: [AGNTCY Observability Schema](https://github.com/agntcy/observe/blob/main/schema/)
84
84
 
85
+ An option is made available for transforming spans attributes exported by using options via env variables (SPAN_TRANSFORMER_RULES_ENABLED, SPAN_TRANSFORMER_RULES_FILE). Please read [transform](./sdk/tracing/transform_span.py).
86
+
85
87
  ## Dev
86
88
 
87
89
  Any Opentelemetry compatible backend can be used, but for this guide, we will use ClickhouseDB as the backend database.
@@ -1,21 +1,22 @@
1
1
  ioa_observe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ioa_observe/sdk/__init__.py,sha256=h_dALthPo9HpUwNyEp7p9kTYlkaghx9ZY3b-YzVV2Vs,8824
2
+ ioa_observe/sdk/__init__.py,sha256=GgEjiEhjqvbWh37FCrH1LQb-NROudJCZS4qa9j0Tyic,8845
3
3
  ioa_observe/sdk/instruments.py,sha256=cA5Yq1BYFovMrYUNYQXua-JXsMtMOa_YOn6yiJZNwLg,576
4
4
  ioa_observe/sdk/telemetry.py,sha256=6wwaOYhZMjAZ6dXDdBA2LUWo3LLptTcy93BJqDdbqBM,3103
5
5
  ioa_observe/sdk/version.py,sha256=oriNAY8huVDPw5N_rv5F_PehFrcGo37FSGBCfZCM81M,121
6
6
  ioa_observe/sdk/client/__init__.py,sha256=V4Rt-Z1EHlM12Lx3hGd0Ew70V1JKAQZXNb9ABtdWHEI,224
7
- ioa_observe/sdk/client/client.py,sha256=nGQoH2pGkc6QANrhfq7cgtgN0HlLxDeu6NrRaEbGHLE,2091
7
+ ioa_observe/sdk/client/client.py,sha256=6TVOo_E1ulE3WO_CYG7oPgeucs-qegOA09uTO3yQiyk,2112
8
8
  ioa_observe/sdk/client/http.py,sha256=LdLYSQPFIhKN5BTB-N78jLO7ITl7jGjA0-qpewEIvO4,1724
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=c8gzCG-CASNI61RiTpNiTvMfguWvn4zc25fhBBgbHFA,3626
13
- ioa_observe/sdk/decorators/base.py,sha256=KbSxFUTP_qpZA0gzt_xe4pFTu9SLpAo1MbHskQ3BQ9A,30208
12
+ ioa_observe/sdk/decorators/__init__.py,sha256=GUZs_HA57bQTLSgo7GAnaofAapk2Y-NuE6md0HPSH3s,3603
13
+ ioa_observe/sdk/decorators/base.py,sha256=FLICXVOFzv6yuCQL99G5Do5h1WrKJNsegvfm5KHe9v8,30173
14
14
  ioa_observe/sdk/decorators/helpers.py,sha256=I9HXMBivkZpGDtPe9Ad_UU35p_m_wEPate4r_fU0oOA,2705
15
15
  ioa_observe/sdk/decorators/util.py,sha256=IebvH9gwZN1en3LblYJUh4bAV2STl6xmp8WpZzBDH2g,30068
16
16
  ioa_observe/sdk/instrumentations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- ioa_observe/sdk/instrumentations/a2a.py,sha256=ov_9ckkymf_qFXG0iXVWfxlW-3kFcP-knrM_t-Cf72w,4414
18
- ioa_observe/sdk/instrumentations/slim.py,sha256=J5e6XeshH55xXaUiT9_j4R_n6VQELzBjgRAU-AgZGOg,11435
17
+ ioa_observe/sdk/instrumentations/a2a.py,sha256=ZpqvPl4u-yheQzSdBfxnZhWFZ8ntbKni_uaW3IDyjqw,6309
18
+ ioa_observe/sdk/instrumentations/mcp.py,sha256=vRM3ofnn7AMmry2RrfyZnZVPEutLWiDMghx2TSnm0Wk,18569
19
+ ioa_observe/sdk/instrumentations/slim.py,sha256=9KS9slZKB7-oC-2SH2s2FKB2JHXXHt4_xPhLvxJD-fk,22456
19
20
  ioa_observe/sdk/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
21
  ioa_observe/sdk/logging/logging.py,sha256=HZxW9s8Due7jgiNkdI38cIjv5rC9D-Flta3RQMOnpow,2891
21
22
  ioa_observe/sdk/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -33,14 +34,15 @@ ioa_observe/sdk/tracing/content_allow_list.py,sha256=1fAkpIwUQ7vDwCTkIVrqeltWQtr
33
34
  ioa_observe/sdk/tracing/context_manager.py,sha256=O0JEXYa9h8anhW78R8KKBuqS0j4by1E1KXxNIMPnLr8,400
34
35
  ioa_observe/sdk/tracing/context_utils.py,sha256=-sYS9vPLI87davV9ubneq5xqbV583CC_c0SmOQS1TAs,2933
35
36
  ioa_observe/sdk/tracing/manual.py,sha256=KS6WN-zw9vAACzXYmnMoJm9d1fenYMfvzeK1GrGDPDE,1937
36
- ioa_observe/sdk/tracing/tracing.py,sha256=jU-qeIccoPUFms2uzYUHoBoACzkCqcdEW6PNzi8uz3M,38883
37
+ ioa_observe/sdk/tracing/tracing.py,sha256=lwmatGwviSrf-hlBxRyxa0kv9AQdP302R3duBgNKsSU,47127
38
+ ioa_observe/sdk/tracing/transform_span.py,sha256=XTApi_gJxum7ynvhtcoCfDyK8VVOj91Q1DT6hAeLHA8,8419
37
39
  ioa_observe/sdk/utils/__init__.py,sha256=UPn182U-UblF_XwXaFpx8F-TmQTbm1LYf9y89uSp5Hw,704
38
- ioa_observe/sdk/utils/const.py,sha256=GwbHakKPjBL4wLqAVkDrSoKB-8p18EUrbaqPuRuV_xg,1099
40
+ ioa_observe/sdk/utils/const.py,sha256=d67dUTAH9UpWvUV9GLBUqn1Sc2knJ55dy-e6YoLrvSo,1318
39
41
  ioa_observe/sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
40
42
  ioa_observe/sdk/utils/json_encoder.py,sha256=g4NQ0tTqgWssY6I1D7r4zo0G6PiUo61jhofTAw5-jno,639
41
43
  ioa_observe/sdk/utils/package_check.py,sha256=1d1MjxhwoEZIx9dumirT2pRsEWgn-m-SI4npDeEalew,576
42
- ioa_observe_sdk-1.0.15.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
43
- ioa_observe_sdk-1.0.15.dist-info/METADATA,sha256=WwtaLg1AX6BXTs0ar7ru1rJ2GRKfoFMMpv8raR1FNLM,6799
44
- ioa_observe_sdk-1.0.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
- ioa_observe_sdk-1.0.15.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
46
- ioa_observe_sdk-1.0.15.dist-info/RECORD,,
44
+ ioa_observe_sdk-1.0.17.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
45
+ ioa_observe_sdk-1.0.17.dist-info/METADATA,sha256=jarZLIoRaEz4yKIwUHHKmAwL2e7hkKe_uhTTT6iwyiw,7027
46
+ ioa_observe_sdk-1.0.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
+ ioa_observe_sdk-1.0.17.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
48
+ ioa_observe_sdk-1.0.17.dist-info/RECORD,,