ioa-observe-sdk 1.0.27__py3-none-any.whl → 1.0.28__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.
- ioa_observe/sdk/client/client.py +13 -4
- ioa_observe/sdk/tracing/tracing.py +31 -19
- {ioa_observe_sdk-1.0.27.dist-info → ioa_observe_sdk-1.0.28.dist-info}/METADATA +1 -1
- {ioa_observe_sdk-1.0.27.dist-info → ioa_observe_sdk-1.0.28.dist-info}/RECORD +7 -7
- {ioa_observe_sdk-1.0.27.dist-info → ioa_observe_sdk-1.0.28.dist-info}/WHEEL +0 -0
- {ioa_observe_sdk-1.0.27.dist-info → ioa_observe_sdk-1.0.28.dist-info}/licenses/LICENSE.md +0 -0
- {ioa_observe_sdk-1.0.27.dist-info → ioa_observe_sdk-1.0.28.dist-info}/top_level.txt +0 -0
ioa_observe/sdk/client/client.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import sys
|
|
5
|
+
import threading
|
|
5
6
|
|
|
6
7
|
from .http import HTTPClient
|
|
7
8
|
from ioa_observe.sdk.version import __version__
|
|
@@ -51,21 +52,29 @@ class Client:
|
|
|
51
52
|
class KVStore(object):
|
|
52
53
|
"""
|
|
53
54
|
Key-Value Store for storing key-value pairs (Singleton).
|
|
55
|
+
Thread-safe implementation for concurrent access.
|
|
54
56
|
"""
|
|
55
57
|
|
|
56
58
|
_instance = None
|
|
59
|
+
_instance_lock = threading.Lock()
|
|
57
60
|
|
|
58
61
|
def __new__(cls):
|
|
59
62
|
if cls._instance is None:
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
with cls._instance_lock:
|
|
64
|
+
# Double-check locking pattern
|
|
65
|
+
if cls._instance is None:
|
|
66
|
+
cls._instance = super(KVStore, cls).__new__(cls)
|
|
67
|
+
cls._instance.store = {}
|
|
68
|
+
cls._instance._lock = threading.Lock()
|
|
62
69
|
return cls._instance
|
|
63
70
|
|
|
64
71
|
def set(self, key: str, value: str):
|
|
65
|
-
self.
|
|
72
|
+
with self._lock:
|
|
73
|
+
self.store[key] = value
|
|
66
74
|
|
|
67
75
|
def get(self, key: str):
|
|
68
|
-
|
|
76
|
+
with self._lock:
|
|
77
|
+
return self.store.get(key)
|
|
69
78
|
|
|
70
79
|
|
|
71
80
|
kv_store = KVStore()
|
|
@@ -67,6 +67,7 @@ APP_NAME = ""
|
|
|
67
67
|
|
|
68
68
|
SESSION_IDLE_TIMEOUT_SECONDS = 300 # e.g. 5 minutes
|
|
69
69
|
SESSION_WATCHER_INTERVAL_SECONDS = 30
|
|
70
|
+
MAX_PROCESSED_SPANS_SIZE = 100000 # Maximum size before cleanup
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def determine_reliability_score(span):
|
|
@@ -142,8 +143,10 @@ class TracerWrapper(object):
|
|
|
142
143
|
obj.__image_uploader = image_uploader
|
|
143
144
|
# {(agent_name): [success_count, total_count]}
|
|
144
145
|
obj._agent_execution_counts = {}
|
|
146
|
+
obj._agent_execution_counts_lock = threading.Lock()
|
|
145
147
|
# Track spans that have been processed to avoid duplicates
|
|
146
148
|
obj._processed_spans = set()
|
|
149
|
+
obj._processed_spans_lock = threading.Lock()
|
|
147
150
|
TracerWrapper.app_name = TracerWrapper.resource_attributes.get(
|
|
148
151
|
"service.name", "observe"
|
|
149
152
|
)
|
|
@@ -306,6 +309,11 @@ class TracerWrapper(object):
|
|
|
306
309
|
expired[session_id] = last_ts
|
|
307
310
|
del self._session_last_activity[session_id]
|
|
308
311
|
|
|
312
|
+
# Periodic cleanup of processed spans to prevent unbounded memory growth
|
|
313
|
+
with self._processed_spans_lock:
|
|
314
|
+
if len(self._processed_spans) > MAX_PROCESSED_SPANS_SIZE:
|
|
315
|
+
self._processed_spans.clear()
|
|
316
|
+
|
|
309
317
|
if not expired:
|
|
310
318
|
continue
|
|
311
319
|
|
|
@@ -313,7 +321,6 @@ class TracerWrapper(object):
|
|
|
313
321
|
|
|
314
322
|
# Iterate over a snapshot and do *not* modify `expired` in the loop
|
|
315
323
|
for session_id, _last_ts in list(expired.items()):
|
|
316
|
-
print("ending session", session_id)
|
|
317
324
|
with tracer.start_as_current_span("session.end") as span:
|
|
318
325
|
span.set_attribute("session.id", session_id)
|
|
319
326
|
workflow_name = get_value("workflow_name")
|
|
@@ -420,16 +427,19 @@ class TracerWrapper(object):
|
|
|
420
427
|
# Added for avoid duplicate on_ending with manual triggers
|
|
421
428
|
# from decorators (@tool, @workflow) in base.py
|
|
422
429
|
span_id = span.context.span_id
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
430
|
+
with self._processed_spans_lock:
|
|
431
|
+
if span_id in self._processed_spans:
|
|
432
|
+
# This span was already processed, skip to avoid duplicates
|
|
433
|
+
return
|
|
426
434
|
|
|
427
|
-
|
|
428
|
-
|
|
435
|
+
# Mark this span as processed
|
|
436
|
+
self._processed_spans.add(span_id)
|
|
429
437
|
|
|
430
438
|
# update last activity per session
|
|
439
|
+
# Skip session.end spans to avoid infinite loop - these are cleanup spans
|
|
440
|
+
# that should not re-register the session as active
|
|
431
441
|
session_id = span.attributes.get("session.id")
|
|
432
|
-
if session_id:
|
|
442
|
+
if session_id and span.name != "session.end":
|
|
433
443
|
with self._session_lock:
|
|
434
444
|
self._session_last_activity[session_id] = time.time()
|
|
435
445
|
|
|
@@ -567,21 +577,23 @@ class TracerWrapper(object):
|
|
|
567
577
|
return self.__tracer_provider.get_tracer(TRACER_NAME)
|
|
568
578
|
|
|
569
579
|
def record_agent_execution(self, agent_name: str, success: bool):
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
580
|
+
with self._agent_execution_counts_lock:
|
|
581
|
+
counts = self._agent_execution_counts.setdefault(agent_name, [0, 0])
|
|
582
|
+
if success:
|
|
583
|
+
counts[0] += 1 # success count
|
|
584
|
+
counts[1] += 1 # total count
|
|
574
585
|
|
|
575
586
|
def _observe_agent_execution_success_rate(self, observer):
|
|
576
587
|
measurements = []
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
588
|
+
with self._agent_execution_counts_lock:
|
|
589
|
+
for agent_name, (
|
|
590
|
+
success_count,
|
|
591
|
+
total_count,
|
|
592
|
+
) in self._agent_execution_counts.items():
|
|
593
|
+
rate = (success_count / total_count) if total_count > 0 else 0.0
|
|
594
|
+
measurements.append(
|
|
595
|
+
Observation(value=rate, attributes={"agent_name": agent_name})
|
|
596
|
+
)
|
|
585
597
|
return measurements
|
|
586
598
|
|
|
587
599
|
|
|
@@ -4,7 +4,7 @@ ioa_observe/sdk/instruments.py,sha256=cA5Yq1BYFovMrYUNYQXua-JXsMtMOa_YOn6yiJZNwL
|
|
|
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=
|
|
7
|
+
ioa_observe/sdk/client/client.py,sha256=i4ZlYrhFcXfg5BTROLAKxhfAAsb-7RS0ch9gucsWXd0,2480
|
|
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
|
|
@@ -35,15 +35,15 @@ ioa_observe/sdk/tracing/content_allow_list.py,sha256=1fAkpIwUQ7vDwCTkIVrqeltWQtr
|
|
|
35
35
|
ioa_observe/sdk/tracing/context_manager.py,sha256=O0JEXYa9h8anhW78R8KKBuqS0j4by1E1KXxNIMPnLr8,400
|
|
36
36
|
ioa_observe/sdk/tracing/context_utils.py,sha256=-sYS9vPLI87davV9ubneq5xqbV583CC_c0SmOQS1TAs,2933
|
|
37
37
|
ioa_observe/sdk/tracing/manual.py,sha256=KS6WN-zw9vAACzXYmnMoJm9d1fenYMfvzeK1GrGDPDE,1937
|
|
38
|
-
ioa_observe/sdk/tracing/tracing.py,sha256=
|
|
38
|
+
ioa_observe/sdk/tracing/tracing.py,sha256=ywvT5Z1DcaLYZQvUP30YKz6m6v76Idlvb2qszupcxS4,50774
|
|
39
39
|
ioa_observe/sdk/tracing/transform_span.py,sha256=XTApi_gJxum7ynvhtcoCfDyK8VVOj91Q1DT6hAeLHA8,8419
|
|
40
40
|
ioa_observe/sdk/utils/__init__.py,sha256=UPn182U-UblF_XwXaFpx8F-TmQTbm1LYf9y89uSp5Hw,704
|
|
41
41
|
ioa_observe/sdk/utils/const.py,sha256=d67dUTAH9UpWvUV9GLBUqn1Sc2knJ55dy-e6YoLrvSo,1318
|
|
42
42
|
ioa_observe/sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
|
|
43
43
|
ioa_observe/sdk/utils/json_encoder.py,sha256=g4NQ0tTqgWssY6I1D7r4zo0G6PiUo61jhofTAw5-jno,639
|
|
44
44
|
ioa_observe/sdk/utils/package_check.py,sha256=1d1MjxhwoEZIx9dumirT2pRsEWgn-m-SI4npDeEalew,576
|
|
45
|
-
ioa_observe_sdk-1.0.
|
|
46
|
-
ioa_observe_sdk-1.0.
|
|
47
|
-
ioa_observe_sdk-1.0.
|
|
48
|
-
ioa_observe_sdk-1.0.
|
|
49
|
-
ioa_observe_sdk-1.0.
|
|
45
|
+
ioa_observe_sdk-1.0.28.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
|
|
46
|
+
ioa_observe_sdk-1.0.28.dist-info/METADATA,sha256=xEbYDkX8xS0u72TgyF-DvEM9R7BxEA8wp3khv4MWHAE,7997
|
|
47
|
+
ioa_observe_sdk-1.0.28.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
48
|
+
ioa_observe_sdk-1.0.28.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
|
|
49
|
+
ioa_observe_sdk-1.0.28.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|