ioa-observe-sdk 1.0.29__tar.gz → 1.0.30__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 (60) hide show
  1. {ioa_observe_sdk-1.0.29/ioa_observe_sdk.egg-info → ioa_observe_sdk-1.0.30}/PKG-INFO +1 -1
  2. ioa_observe_sdk-1.0.30/ioa_observe/sdk/instrumentations/slim.py +496 -0
  3. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30/ioa_observe_sdk.egg-info}/PKG-INFO +1 -1
  4. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/pyproject.toml +1 -1
  5. ioa_observe_sdk-1.0.29/ioa_observe/sdk/instrumentations/slim.py +0 -973
  6. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/LICENSE.md +0 -0
  7. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/README.md +0 -0
  8. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/__init__.py +0 -0
  9. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/__init__.py +0 -0
  10. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/client/__init__.py +0 -0
  11. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/client/client.py +0 -0
  12. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/client/http.py +0 -0
  13. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/config/__init__.py +0 -0
  14. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/connectors/__init__.py +0 -0
  15. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/connectors/slim.py +0 -0
  16. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/decorators/__init__.py +0 -0
  17. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/decorators/base.py +0 -0
  18. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/decorators/helpers.py +0 -0
  19. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/decorators/util.py +0 -0
  20. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/instrumentations/__init__.py +0 -0
  21. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/instrumentations/a2a.py +0 -0
  22. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/instrumentations/mcp.py +0 -0
  23. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/instrumentations/nats.py +0 -0
  24. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/instruments.py +0 -0
  25. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/logging/__init__.py +0 -0
  26. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/logging/logging.py +0 -0
  27. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/__init__.py +0 -0
  28. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agent.py +0 -0
  29. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agents/__init__.py +0 -0
  30. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agents/agent_connections.py +0 -0
  31. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agents/availability.py +0 -0
  32. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agents/heuristics.py +0 -0
  33. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agents/recovery_tracker.py +0 -0
  34. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agents/tool_call_tracker.py +0 -0
  35. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/agents/tracker.py +0 -0
  36. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/metrics/metrics.py +0 -0
  37. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/telemetry.py +0 -0
  38. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/tracing/__init__.py +0 -0
  39. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/tracing/content_allow_list.py +0 -0
  40. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/tracing/context_manager.py +0 -0
  41. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/tracing/context_utils.py +0 -0
  42. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/tracing/manual.py +0 -0
  43. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/tracing/tracing.py +0 -0
  44. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/tracing/transform_span.py +0 -0
  45. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/utils/__init__.py +0 -0
  46. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/utils/const.py +0 -0
  47. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/utils/in_memory_span_exporter.py +0 -0
  48. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/utils/json_encoder.py +0 -0
  49. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/utils/package_check.py +0 -0
  50. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe/sdk/version.py +0 -0
  51. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe_sdk.egg-info/SOURCES.txt +0 -0
  52. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe_sdk.egg-info/dependency_links.txt +0 -0
  53. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe_sdk.egg-info/requires.txt +0 -0
  54. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/ioa_observe_sdk.egg-info/top_level.txt +0 -0
  55. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/setup.cfg +0 -0
  56. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/tests/test_client.py +0 -0
  57. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/tests/test_instrumentor.py +0 -0
  58. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/tests/test_manual_instrumentation.py +0 -0
  59. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/tests/test_transform_span.py +0 -0
  60. {ioa_observe_sdk-1.0.29 → ioa_observe_sdk-1.0.30}/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.29
3
+ Version: 1.0.30
4
4
  Summary: IOA Observability SDK
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -0,0 +1,496 @@
1
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """SLIM v1.x Instrumentation for OpenTelemetry tracing."""
5
+
6
+ from typing import Collection
7
+ import functools
8
+ import json
9
+ import base64
10
+ import threading
11
+
12
+ from opentelemetry import baggage, context
13
+ from opentelemetry.baggage.propagation import W3CBaggagePropagator
14
+ from opentelemetry.context import get_value
15
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
16
+ from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
17
+
18
+ from ioa_observe.sdk import TracerWrapper
19
+ from ioa_observe.sdk.client import kv_store
20
+ from ioa_observe.sdk.tracing import set_session_id, get_current_traceparent
21
+
22
+ _instruments = ("slim-bindings >= 1.0.0",)
23
+ _global_tracer = None
24
+ _kv_lock = threading.RLock()
25
+
26
+
27
+ def _get_session_id(session):
28
+ """Get session ID from session object (v1.x uses .session_id() method)."""
29
+ if hasattr(session, "session_id") and callable(session.session_id):
30
+ try:
31
+ return str(session.session_id())
32
+ except Exception:
33
+ pass
34
+ return str(session.id) if hasattr(session, "id") else None
35
+
36
+
37
+ def _process_received_message(raw_message):
38
+ """Process received message, extract tracing context, return cleaned payload."""
39
+ if raw_message is None:
40
+ return raw_message
41
+
42
+ try:
43
+ if isinstance(raw_message, bytes):
44
+ message_dict = json.loads(raw_message.decode())
45
+ elif isinstance(raw_message, str):
46
+ message_dict = json.loads(raw_message)
47
+ elif isinstance(raw_message, dict):
48
+ message_dict = raw_message
49
+ else:
50
+ return raw_message
51
+
52
+ headers = message_dict.get("headers", {})
53
+ traceparent = headers.get("traceparent")
54
+ session_id = headers.get("session_id")
55
+
56
+ # Restore trace context
57
+ if traceparent:
58
+ carrier = {
59
+ k.lower(): v
60
+ for k, v in headers.items()
61
+ if k.lower() in ["traceparent", "baggage"]
62
+ }
63
+ if carrier:
64
+ ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
65
+ ctx = W3CBaggagePropagator().extract(carrier=carrier, context=ctx)
66
+ context.attach(ctx)
67
+
68
+ if session_id and session_id != "None":
69
+ set_session_id(session_id, traceparent=traceparent)
70
+ with _kv_lock:
71
+ kv_store.set(f"execution.{traceparent}", session_id)
72
+
73
+ # Clean headers
74
+ cleaned = message_dict.copy()
75
+ if "headers" in cleaned:
76
+ h = cleaned["headers"].copy()
77
+ for k in ["traceparent", "session_id", "slim_session_id"]:
78
+ h.pop(k, None)
79
+ if h:
80
+ cleaned["headers"] = h
81
+ else:
82
+ cleaned.pop("headers", None)
83
+
84
+ # Extract payload
85
+ if len(cleaned) == 1 and "payload" in cleaned:
86
+ payload = cleaned["payload"]
87
+ if isinstance(payload, str):
88
+ try:
89
+ return json.dumps(json.loads(payload)).encode("utf-8")
90
+ except json.JSONDecodeError:
91
+ return payload.encode("utf-8")
92
+ elif isinstance(payload, (dict, list)):
93
+ return json.dumps(payload).encode("utf-8")
94
+ return payload
95
+ return json.dumps(cleaned).encode("utf-8")
96
+
97
+ except Exception:
98
+ return raw_message
99
+
100
+
101
+ def _wrap_message_with_headers(message, headers):
102
+ """Wrap message with tracing headers."""
103
+ if isinstance(message, bytes):
104
+ try:
105
+ decoded = message.decode("utf-8")
106
+ try:
107
+ original = json.loads(decoded)
108
+ if isinstance(original, dict):
109
+ wrapped = original.copy()
110
+ wrapped["headers"] = {**wrapped.get("headers", {}), **headers}
111
+ else:
112
+ wrapped = {"headers": headers, "payload": original}
113
+ except json.JSONDecodeError:
114
+ wrapped = {"headers": headers, "payload": decoded}
115
+ except UnicodeDecodeError:
116
+ wrapped = {
117
+ "headers": headers,
118
+ "payload": base64.b64encode(message).decode("utf-8"),
119
+ }
120
+ elif isinstance(message, str):
121
+ try:
122
+ original = json.loads(message)
123
+ if isinstance(original, dict):
124
+ wrapped = original.copy()
125
+ wrapped["headers"] = {**wrapped.get("headers", {}), **headers}
126
+ else:
127
+ wrapped = {"headers": headers, "payload": original}
128
+ except json.JSONDecodeError:
129
+ wrapped = {"headers": headers, "payload": message}
130
+ elif isinstance(message, dict):
131
+ wrapped = message.copy()
132
+ wrapped["headers"] = {**wrapped.get("headers", {}), **headers}
133
+ else:
134
+ wrapped = {"headers": headers, "payload": json.dumps(message)}
135
+
136
+ return wrapped
137
+
138
+
139
+ class SLIMInstrumentor(BaseInstrumentor):
140
+ def __init__(self):
141
+ super().__init__()
142
+ global _global_tracer
143
+ _global_tracer = TracerWrapper().get_tracer()
144
+
145
+ def instrumentation_dependencies(self) -> Collection[str]:
146
+ return _instruments
147
+
148
+ def _instrument(self, **kwargs):
149
+ try:
150
+ import slim_bindings
151
+ except ImportError:
152
+ raise ImportError(
153
+ "No module named 'slim_bindings'. Please install it first."
154
+ )
155
+
156
+ self._instrument_service(slim_bindings)
157
+ self._instrument_app(slim_bindings)
158
+ self._instrument_sessions(slim_bindings)
159
+
160
+ def _instrument_service(self, slim_bindings):
161
+ """Instrument SLIM v1.x Service class."""
162
+ if not hasattr(slim_bindings, "Service"):
163
+ return
164
+
165
+ Service = slim_bindings.Service
166
+
167
+ # connect_async
168
+ if hasattr(Service, "connect_async"):
169
+ original_connect_async = Service.connect_async
170
+
171
+ @functools.wraps(original_connect_async)
172
+ async def wrapped_connect_async(self, config, *args, **kwargs):
173
+ if _global_tracer:
174
+ with _global_tracer.start_as_current_span(
175
+ "slim.service.connect"
176
+ ) as span:
177
+ result = await original_connect_async(
178
+ self, config, *args, **kwargs
179
+ )
180
+ span.set_attribute(
181
+ "slim.connection.id", str(result) if result else "unknown"
182
+ )
183
+ return result
184
+ return await original_connect_async(self, config, *args, **kwargs)
185
+
186
+ Service.connect_async = wrapped_connect_async
187
+
188
+ # run_server_async
189
+ if hasattr(Service, "run_server_async"):
190
+ original_run_server_async = Service.run_server_async
191
+
192
+ @functools.wraps(original_run_server_async)
193
+ async def wrapped_run_server_async(self, config, *args, **kwargs):
194
+ if _global_tracer:
195
+ with _global_tracer.start_as_current_span(
196
+ "slim.service.run_server"
197
+ ) as span:
198
+ if hasattr(config, "endpoint"):
199
+ span.set_attribute("slim.server.endpoint", config.endpoint)
200
+ result = await original_run_server_async(
201
+ self, config, *args, **kwargs
202
+ )
203
+ return result
204
+ return await original_run_server_async(self, config, *args, **kwargs)
205
+
206
+ Service.run_server_async = wrapped_run_server_async
207
+
208
+ def _instrument_app(self, slim_bindings):
209
+ """Instrument SLIM v1.x App class."""
210
+ if not hasattr(slim_bindings, "App"):
211
+ return
212
+
213
+ App = slim_bindings.App
214
+
215
+ # create_session_async
216
+ if hasattr(App, "create_session_async"):
217
+ original_create_session_async = App.create_session_async
218
+
219
+ @functools.wraps(original_create_session_async)
220
+ async def wrapped_create_session_async(
221
+ self, config, dest=None, *args, **kwargs
222
+ ):
223
+ if _global_tracer:
224
+ with _global_tracer.start_as_current_span(
225
+ "slim.app.create_session"
226
+ ) as span:
227
+ ctx = await original_create_session_async(
228
+ self, config, dest, *args, **kwargs
229
+ )
230
+ if hasattr(ctx, "session"):
231
+ sid = _get_session_id(ctx.session)
232
+ if sid:
233
+ span.set_attribute("slim.session.id", sid)
234
+ return ctx
235
+ return await original_create_session_async(
236
+ self, config, dest, *args, **kwargs
237
+ )
238
+
239
+ App.create_session_async = wrapped_create_session_async
240
+
241
+ # create_session_and_wait_async
242
+ if hasattr(App, "create_session_and_wait_async"):
243
+ original_create_session_and_wait_async = App.create_session_and_wait_async
244
+
245
+ @functools.wraps(original_create_session_and_wait_async)
246
+ async def wrapped_create_session_and_wait_async(
247
+ self, config, dest=None, *args, **kwargs
248
+ ):
249
+ if _global_tracer:
250
+ with _global_tracer.start_as_current_span(
251
+ "slim.app.create_session"
252
+ ) as span:
253
+ session = await original_create_session_and_wait_async(
254
+ self, config, dest, *args, **kwargs
255
+ )
256
+ sid = _get_session_id(session)
257
+ if sid:
258
+ span.set_attribute("slim.session.id", sid)
259
+ return session
260
+ return await original_create_session_and_wait_async(
261
+ self, config, dest, *args, **kwargs
262
+ )
263
+
264
+ App.create_session_and_wait_async = wrapped_create_session_and_wait_async
265
+
266
+ # subscribe_async
267
+ if hasattr(App, "subscribe_async"):
268
+ original_subscribe_async = App.subscribe_async
269
+
270
+ @functools.wraps(original_subscribe_async)
271
+ async def wrapped_subscribe_async(self, name, conn_id, *args, **kwargs):
272
+ if _global_tracer:
273
+ with _global_tracer.start_as_current_span(
274
+ "slim.app.subscribe"
275
+ ) as span:
276
+ if hasattr(name, "organization"):
277
+ span.set_attribute(
278
+ "slim.name",
279
+ f"{name.organization}/{name.namespace}/{name.app}",
280
+ )
281
+ return await original_subscribe_async(
282
+ self, name, conn_id, *args, **kwargs
283
+ )
284
+ return await original_subscribe_async(
285
+ self, name, conn_id, *args, **kwargs
286
+ )
287
+
288
+ App.subscribe_async = wrapped_subscribe_async
289
+
290
+ # set_route_async
291
+ if hasattr(App, "set_route_async"):
292
+ original_set_route_async = App.set_route_async
293
+
294
+ @functools.wraps(original_set_route_async)
295
+ async def wrapped_set_route_async(self, name, conn_id, *args, **kwargs):
296
+ if _global_tracer:
297
+ with _global_tracer.start_as_current_span(
298
+ "slim.app.set_route"
299
+ ) as span:
300
+ if hasattr(name, "organization"):
301
+ span.set_attribute(
302
+ "slim.route",
303
+ f"{name.organization}/{name.namespace}/{name.app}",
304
+ )
305
+ return await original_set_route_async(
306
+ self, name, conn_id, *args, **kwargs
307
+ )
308
+ return await original_set_route_async(
309
+ self, name, conn_id, *args, **kwargs
310
+ )
311
+
312
+ App.set_route_async = wrapped_set_route_async
313
+
314
+ # listen_for_session - this is a blocking call, not async
315
+ if hasattr(App, "listen_for_session"):
316
+ original_listen_for_session = App.listen_for_session
317
+
318
+ @functools.wraps(original_listen_for_session)
319
+ def wrapped_listen_for_session(self, *args, **kwargs):
320
+ if _global_tracer:
321
+ with _global_tracer.start_as_current_span(
322
+ "slim.app.listen_for_session"
323
+ ):
324
+ return original_listen_for_session(self, *args, **kwargs)
325
+ return original_listen_for_session(self, *args, **kwargs)
326
+
327
+ App.listen_for_session = wrapped_listen_for_session
328
+
329
+ def _instrument_sessions(self, slim_bindings):
330
+ """Instrument session classes for v1.x."""
331
+ session_classes = set()
332
+
333
+ # Find session classes
334
+ for name in ["Session", "P2PSession", "GroupSession"]:
335
+ if hasattr(slim_bindings, name):
336
+ session_classes.add(getattr(slim_bindings, name))
337
+
338
+ # Find any class with session-like methods
339
+ for name in dir(slim_bindings):
340
+ cls = getattr(slim_bindings, name)
341
+ if isinstance(cls, type) and any(
342
+ hasattr(cls, m) for m in ["get_message_async", "publish_async"]
343
+ ):
344
+ session_classes.add(cls)
345
+
346
+ for session_class in session_classes:
347
+ if hasattr(session_class, "get_message_async"):
348
+ self._wrap_get_message(session_class)
349
+
350
+ if hasattr(session_class, "publish_async"):
351
+ self._wrap_publish(session_class, "publish_async")
352
+
353
+ if hasattr(session_class, "publish_to_async"):
354
+ self._wrap_publish(session_class, "publish_to_async", msg_idx=1)
355
+
356
+ def _wrap_get_message(self, session_class):
357
+ """Wrap get_message_async to extract tracing context."""
358
+ orig = session_class.get_message_async
359
+
360
+ @functools.wraps(orig)
361
+ async def wrapped(self, *args, **kwargs):
362
+ result = await orig(self, *args, **kwargs)
363
+ if result is None:
364
+ return result
365
+
366
+ # v1.x returns ReceivedMessage with .context and .payload
367
+ if hasattr(result, "payload"):
368
+ raw = result.payload
369
+ elif isinstance(result, tuple) and len(result) == 2:
370
+ raw = result[1]
371
+ else:
372
+ raw = result
373
+
374
+ processed = _process_received_message(raw)
375
+
376
+ if hasattr(result, "payload"):
377
+ try:
378
+ result.payload = processed
379
+ except AttributeError:
380
+
381
+ class Processed:
382
+ def __init__(self, ctx, payload):
383
+ self.context = ctx
384
+ self.payload = payload
385
+
386
+ return Processed(result.context, processed)
387
+ return result
388
+ elif isinstance(result, tuple) and len(result) == 2:
389
+ return (result[0], processed)
390
+ return processed
391
+
392
+ session_class.get_message_async = wrapped
393
+
394
+ def _wrap_publish(self, session_class, method_name, msg_idx=0):
395
+ """Wrap publish methods to inject tracing headers."""
396
+ orig = getattr(session_class, method_name)
397
+
398
+ @functools.wraps(orig)
399
+ async def wrapped(self, *args, **kwargs):
400
+ traceparent = get_current_traceparent()
401
+ session_id = None
402
+
403
+ if traceparent:
404
+ with _kv_lock:
405
+ session_id = kv_store.get(f"execution.{traceparent}")
406
+ if not session_id:
407
+ session_id = get_value("session.id")
408
+ if session_id:
409
+ kv_store.set(f"execution.{traceparent}", session_id)
410
+
411
+ slim_session_id = _get_session_id(self)
412
+
413
+ if _global_tracer:
414
+ with _global_tracer.start_as_current_span(
415
+ f"session.{method_name}"
416
+ ) as span:
417
+ if slim_session_id:
418
+ span.set_attribute("slim.session.id", slim_session_id)
419
+
420
+ if args and len(args) > msg_idx and (traceparent or session_id):
421
+ headers = {
422
+ "session_id": session_id,
423
+ "traceparent": traceparent,
424
+ "slim_session_id": slim_session_id,
425
+ }
426
+ if traceparent and session_id:
427
+ baggage.set_baggage(f"execution.{traceparent}", session_id)
428
+
429
+ args_list = list(args)
430
+ wrapped_msg = _wrap_message_with_headers(
431
+ args_list[msg_idx], headers
432
+ )
433
+ args_list[msg_idx] = (
434
+ json.dumps(wrapped_msg).encode("utf-8")
435
+ if isinstance(wrapped_msg, dict)
436
+ else wrapped_msg
437
+ )
438
+ args = tuple(args_list)
439
+
440
+ return await orig(self, *args, **kwargs)
441
+ else:
442
+ if args and len(args) > msg_idx and (traceparent or session_id):
443
+ headers = {
444
+ "session_id": session_id,
445
+ "traceparent": traceparent,
446
+ "slim_session_id": slim_session_id,
447
+ }
448
+ args_list = list(args)
449
+ wrapped_msg = _wrap_message_with_headers(
450
+ args_list[msg_idx], headers
451
+ )
452
+ args_list[msg_idx] = (
453
+ json.dumps(wrapped_msg).encode("utf-8")
454
+ if isinstance(wrapped_msg, dict)
455
+ else wrapped_msg
456
+ )
457
+ args = tuple(args_list)
458
+
459
+ return await orig(self, *args, **kwargs)
460
+
461
+ setattr(session_class, method_name, wrapped)
462
+
463
+ def _uninstrument(self, **kwargs):
464
+ try:
465
+ import slim_bindings
466
+ except ImportError:
467
+ return
468
+
469
+ def restore(obj, methods):
470
+ for m in methods:
471
+ if hasattr(obj, m):
472
+ orig = getattr(obj, m)
473
+ if hasattr(orig, "__wrapped__"):
474
+ setattr(obj, m, orig.__wrapped__)
475
+
476
+ if hasattr(slim_bindings, "Service"):
477
+ restore(slim_bindings.Service, ["connect_async", "run_server_async"])
478
+
479
+ if hasattr(slim_bindings, "App"):
480
+ restore(
481
+ slim_bindings.App,
482
+ [
483
+ "create_session_async",
484
+ "create_session_and_wait_async",
485
+ "subscribe_async",
486
+ "set_route_async",
487
+ "listen_for_session",
488
+ ],
489
+ )
490
+
491
+ # Restore session methods
492
+ session_methods = ["publish_async", "publish_to_async", "get_message_async"]
493
+ for name in dir(slim_bindings):
494
+ cls = getattr(slim_bindings, name)
495
+ if isinstance(cls, type):
496
+ restore(cls, session_methods)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ioa-observe-sdk
3
- Version: 1.0.29
3
+ Version: 1.0.30
4
4
  Summary: IOA Observability SDK
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "ioa-observe-sdk"
8
- version = "1.0.29"
8
+ version = "1.0.30"
9
9
  license = "Apache-2.0"
10
10
  description = "IOA Observability SDK"
11
11
  readme = "README.md"