iii-sdk 0.14.0.dev2__tar.gz → 0.15.0.dev1__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 (65) hide show
  1. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/PKG-INFO +2 -2
  2. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/pyproject.toml +5 -2
  3. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/__init__.py +41 -18
  4. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/iii.py +4 -4
  5. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/iii_constants.py +1 -20
  6. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_baggage_span_processor.py +1 -1
  7. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_context_propagation.py +1 -2
  8. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_hold_process.py +6 -6
  9. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_http_external_functions_integration.py +2 -2
  10. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_iii_registration_dedup.py +7 -7
  11. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_init_api.py +3 -3
  12. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_logger_function_ids.py +1 -1
  13. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_logger_otel.py +8 -10
  14. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_payload.py +1 -1
  15. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_register_function_args.py +2 -2
  16. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_span_ops.py +1 -1
  17. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_sync_api.py +2 -2
  18. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_telemetry.py +6 -6
  19. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_telemetry_exporters.py +2 -2
  20. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_telemetry_types.py +1 -1
  21. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_trace_helpers.py +1 -1
  22. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/uv.lock +75 -3
  23. iii_sdk-0.14.0.dev2/src/iii/baggage_span_processor.py +0 -42
  24. iii_sdk-0.14.0.dev2/src/iii/logger.py +0 -184
  25. iii_sdk-0.14.0.dev2/src/iii/payload.py +0 -92
  26. iii_sdk-0.14.0.dev2/src/iii/span_ops.py +0 -38
  27. iii_sdk-0.14.0.dev2/src/iii/telemetry.py +0 -571
  28. iii_sdk-0.14.0.dev2/src/iii/telemetry_exporters.py +0 -457
  29. iii_sdk-0.14.0.dev2/src/iii/telemetry_types.py +0 -46
  30. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/.gitignore +0 -0
  31. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/README.md +0 -0
  32. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/channels.py +0 -0
  33. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/errors.py +0 -0
  34. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/format_utils.py +0 -0
  35. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/iii_types.py +0 -0
  36. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/otel_worker_gauges.py +0 -0
  37. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/state.py +0 -0
  38. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/stream.py +0 -0
  39. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/triggers.py +0 -0
  40. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/types.py +0 -0
  41. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/utils.py +0 -0
  42. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/src/iii/worker_metrics.py +0 -0
  43. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/conftest.py +0 -0
  44. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_api_triggers.py +0 -0
  45. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_async_api.py +0 -0
  46. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_bridge.py +0 -0
  47. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_channel_close_delay.py +0 -0
  48. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_data_channels.py +0 -0
  49. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_errors.py +0 -0
  50. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_format_utils.py +0 -0
  51. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_healthcheck.py +0 -0
  52. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_invocation_exception.py +0 -0
  53. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_middleware.py +0 -0
  54. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_pubsub.py +0 -0
  55. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_queue_integration.py +0 -0
  56. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_rbac_workers.py +0 -0
  57. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_state.py +0 -0
  58. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_stream_models.py +0 -0
  59. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_streams.py +0 -0
  60. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_streams_runtime_annotations.py +0 -0
  61. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_trigger_metadata.py +0 -0
  62. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_trigger_registration_error.py +0 -0
  63. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_utils.py +0 -0
  64. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_worker_metadata.py +0 -0
  65. {iii_sdk-0.14.0.dev2 → iii_sdk-0.15.0.dev1}/tests/test_worker_metrics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iii-sdk
3
- Version: 0.14.0.dev2
3
+ Version: 0.15.0.dev1
4
4
  Summary: III SDK for Python
5
5
  Project-URL: Homepage, https://github.com/iii-hq/iii
6
6
  Project-URL: Repository, https://github.com/iii-hq/iii
@@ -14,8 +14,8 @@ Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Requires-Python: >=3.10
17
+ Requires-Dist: iii-observability==0.13.0.dev1
17
18
  Requires-Dist: opentelemetry-api>=1.25
18
- Requires-Dist: opentelemetry-sdk>=1.25
19
19
  Requires-Dist: pydantic>=2.0
20
20
  Requires-Dist: websockets>=12.0
21
21
  Provides-Extra: dev
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "iii-sdk"
7
- version = "0.14.0.dev2"
7
+ version = "0.15.0.dev1"
8
8
  description = "III SDK for Python"
9
9
  authors = [{ name = "III" }]
10
10
  license = { text = "Apache-2.0" }
@@ -23,13 +23,16 @@ dependencies = [
23
23
  "websockets>=12.0",
24
24
  "pydantic>=2.0",
25
25
  "opentelemetry-api>=1.25",
26
- "opentelemetry-sdk>=1.25",
26
+ "iii-observability==0.13.0.dev1",
27
27
  ]
28
28
 
29
29
  [project.urls]
30
30
  Homepage = "https://github.com/iii-hq/iii"
31
31
  Repository = "https://github.com/iii-hq/iii"
32
32
 
33
+ [tool.uv.sources]
34
+ iii-observability = { path = "../observability", editable = true }
35
+
33
36
  [project.optional-dependencies]
34
37
  dev = [
35
38
  "pytest>=8.0",
@@ -1,11 +1,37 @@
1
1
  """III SDK for Python."""
2
2
 
3
- from .baggage_span_processor import DEFAULT_ALLOWLIST, BaggageSpanProcessor
3
+ from iii_observability import (
4
+ DEFAULT_ALLOWLIST,
5
+ REDACTED_PLACEHOLDER,
6
+ BaggageSpanProcessor,
7
+ Logger,
8
+ OtelConfig,
9
+ ReconnectionConfig,
10
+ current_span_id,
11
+ current_span_is_recording,
12
+ current_trace_id,
13
+ execute_traced_request,
14
+ extract_baggage,
15
+ extract_traceparent,
16
+ flush_otel,
17
+ init_otel,
18
+ inject_baggage,
19
+ inject_traceparent,
20
+ record_span_event,
21
+ redact,
22
+ redact_and_truncate,
23
+ resolve_max_bytes_from_env,
24
+ set_current_span_attribute,
25
+ set_current_span_error,
26
+ shutdown_otel,
27
+ with_span,
28
+ )
29
+
4
30
  from .channels import ChannelReader, ChannelWriter
5
31
  from .errors import IIIForbiddenError, IIIInvocationError, IIITimeoutError
6
32
  from .format_utils import extract_request_format, extract_response_format, python_type_to_format
7
33
  from .iii import TriggerAction, register_worker
8
- from .iii_constants import FunctionRef, InitOptions, ReconnectionConfig, TelemetryOptions
34
+ from .iii_constants import FunctionRef, InitOptions, TelemetryOptions
9
35
  from .iii_types import (
10
36
  AuthInput,
11
37
  AuthResult,
@@ -31,19 +57,6 @@ from .iii_types import (
31
57
  TriggerActionVoid,
32
58
  TriggerRequest,
33
59
  )
34
- from .logger import Logger
35
- from .payload import (
36
- REDACTED_PLACEHOLDER,
37
- redact,
38
- redact_and_truncate,
39
- resolve_max_bytes_from_env,
40
- )
41
- from .span_ops import (
42
- current_span_is_recording,
43
- record_span_event,
44
- set_current_span_attribute,
45
- set_current_span_error,
46
- )
47
60
  from .stream import (
48
61
  IStream,
49
62
  StreamChangeEvent,
@@ -53,7 +66,6 @@ from .stream import (
53
66
  StreamJoinLeaveTriggerConfig,
54
67
  StreamTriggerConfig,
55
68
  )
56
- from .telemetry_types import OtelConfig
57
69
  from .triggers import Trigger, TriggerConfig, TriggerHandler, TriggerTypeRef
58
70
  from .types import (
59
71
  ApiRequest,
@@ -72,13 +84,24 @@ __all__ = [
72
84
  "BaggageSpanProcessor",
73
85
  "DEFAULT_ALLOWLIST",
74
86
  "REDACTED_PLACEHOLDER",
87
+ "current_span_id",
75
88
  "current_span_is_recording",
89
+ "current_trace_id",
90
+ "execute_traced_request",
91
+ "extract_baggage",
92
+ "extract_traceparent",
93
+ "flush_otel",
94
+ "init_otel",
95
+ "inject_baggage",
96
+ "inject_traceparent",
76
97
  "record_span_event",
77
- "set_current_span_attribute",
78
- "set_current_span_error",
79
98
  "redact",
80
99
  "redact_and_truncate",
81
100
  "resolve_max_bytes_from_env",
101
+ "set_current_span_attribute",
102
+ "set_current_span_error",
103
+ "shutdown_otel",
104
+ "with_span",
82
105
  # Channels
83
106
  "ChannelReader",
84
107
  "ChannelWriter",
@@ -14,6 +14,7 @@ from importlib.metadata import version
14
14
  from typing import Any, Awaitable, Callable, Coroutine, TypeVar, cast
15
15
 
16
16
  import websockets
17
+ from iii_observability import OtelConfig
17
18
  from websockets.asyncio.client import ClientConnection
18
19
 
19
20
  from .channels import ChannelReader, ChannelWriter
@@ -54,7 +55,6 @@ from .stream import (
54
55
  StreamListInput,
55
56
  StreamSetInput,
56
57
  )
57
- from .telemetry_types import OtelConfig
58
58
  from .triggers import Trigger, TriggerConfig, TriggerHandler, TriggerTypeRef
59
59
  from .types import Channel, RemoteFunctionData, RemoteTriggerTypeData, is_channel_ref
60
60
 
@@ -224,7 +224,7 @@ class III:
224
224
  from an async context.
225
225
  """
226
226
  self._running = True
227
- from .telemetry import attach_event_loop, init_otel
227
+ from iii_observability.telemetry import attach_event_loop, init_otel
228
228
 
229
229
  loop = asyncio.get_running_loop()
230
230
  otel_cfg: OtelConfig | None = None
@@ -280,7 +280,7 @@ class III:
280
280
 
281
281
  self._set_connection_state("disconnected")
282
282
 
283
- from .telemetry import shutdown_otel_async
283
+ from iii_observability.telemetry import shutdown_otel_async
284
284
 
285
285
  await shutdown_otel_async()
286
286
 
@@ -499,7 +499,7 @@ class III:
499
499
  tracer = trace.get_tracer("iii-python-sdk")
500
500
  import os
501
501
 
502
- from .payload import redact_and_truncate, resolve_max_bytes_from_env
502
+ from iii_observability import redact_and_truncate, resolve_max_bytes_from_env
503
503
 
504
504
  trace_payloads = os.environ.get("III_DISABLE_TRACE_PAYLOADS", "").lower() not in (
505
505
  "1",
@@ -3,7 +3,7 @@
3
3
  from dataclasses import dataclass
4
4
  from typing import Any, Callable, Literal
5
5
 
6
- from .telemetry_types import OtelConfig
6
+ from iii_observability import OtelConfig, ReconnectionConfig
7
7
 
8
8
  IIIConnectionState = Literal["disconnected", "connecting", "connected", "reconnecting", "failed"]
9
9
 
@@ -13,25 +13,6 @@ DEFAULT_INVOCATION_TIMEOUT_MS = 30000
13
13
  MAX_QUEUE_SIZE = 1000
14
14
 
15
15
 
16
- @dataclass
17
- class ReconnectionConfig:
18
- """Configuration for WebSocket reconnection behavior.
19
-
20
- Attributes:
21
- initial_delay_ms: Starting delay in milliseconds. Default ``1000``.
22
- max_delay_ms: Maximum delay cap in milliseconds. Default ``30000``.
23
- backoff_multiplier: Exponential backoff multiplier. Default ``2.0``.
24
- jitter_factor: Random jitter factor (0--1). Default ``0.3``.
25
- max_retries: Maximum retry attempts. ``-1`` for infinite. Default ``-1``.
26
- """
27
-
28
- initial_delay_ms: int = 1000
29
- max_delay_ms: int = 30000
30
- backoff_multiplier: float = 2.0
31
- jitter_factor: float = 0.3
32
- max_retries: int = -1
33
-
34
-
35
16
  DEFAULT_RECONNECTION_CONFIG = ReconnectionConfig()
36
17
 
37
18
 
@@ -10,7 +10,7 @@ from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
10
10
  )
11
11
  from opentelemetry.sdk.trace.sampling import ALWAYS_OFF
12
12
 
13
- from iii.baggage_span_processor import (
13
+ from iii_observability import (
14
14
  DEFAULT_ALLOWLIST,
15
15
  BaggageSpanProcessor,
16
16
  )
@@ -7,8 +7,7 @@ import pytest
7
7
  import iii.iii as iii_module
8
8
  from iii.iii import III
9
9
  from iii.iii_constants import InitOptions
10
- from iii.telemetry import init_otel, shutdown_otel
11
- from iii.telemetry_types import OtelConfig
10
+ from iii_observability import OtelConfig, init_otel, shutdown_otel
12
11
 
13
12
 
14
13
  @pytest.fixture(autouse=True)
@@ -5,8 +5,8 @@ from iii.iii import III
5
5
 
6
6
  def test_background_thread_is_not_daemon(monkeypatch) -> None:
7
7
  """The background event-loop thread must be non-daemon so it keeps the process alive."""
8
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
9
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
8
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
9
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
10
10
 
11
11
  async def fake_do_connect(self: III) -> None:
12
12
  return None
@@ -23,8 +23,8 @@ def test_background_thread_is_not_daemon(monkeypatch) -> None:
23
23
 
24
24
  def test_shutdown_stops_background_thread(monkeypatch) -> None:
25
25
  """After shutdown(), the background thread should stop within a reasonable timeout."""
26
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
27
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
26
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
27
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
28
28
 
29
29
  async def fake_do_connect(self: III) -> None:
30
30
  return None
@@ -43,8 +43,8 @@ def test_shutdown_stops_background_thread(monkeypatch) -> None:
43
43
 
44
44
  def test_shutdown_async_stops_background_thread(monkeypatch) -> None:
45
45
  """After shutdown_async(), the background thread should also stop."""
46
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
47
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
46
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
47
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
48
48
 
49
49
  async def fake_do_connect(self: III) -> None:
50
50
  return None
@@ -157,8 +157,8 @@ def _make_fake_ws_env(monkeypatch: pytest.MonkeyPatch) -> list[dict[str, Any]]:
157
157
  return FakeWs()
158
158
 
159
159
  monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
160
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
161
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
160
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
161
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
162
162
  monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
163
163
  return sent
164
164
 
@@ -17,7 +17,7 @@ def reset_otel():
17
17
  # III.connect() calls init_otel() which sets global providers;
18
18
  # reset them so subsequent test files start with a clean slate.
19
19
  try:
20
- from iii.telemetry import shutdown_otel
20
+ from iii_observability import shutdown_otel
21
21
 
22
22
  shutdown_otel()
23
23
  except Exception:
@@ -73,8 +73,8 @@ def test_preconnect_registration_sent_once(monkeypatch: pytest.MonkeyPatch) -> N
73
73
  return ws
74
74
 
75
75
  monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
76
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
77
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
76
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
77
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
78
78
  monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
79
79
 
80
80
  client = III("ws://fake")
@@ -110,8 +110,8 @@ def test_reconnect_replays_durable_state_once_per_connection(
110
110
  return ws
111
111
 
112
112
  monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
113
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
114
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
113
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
114
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
115
115
  monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
116
116
 
117
117
  client = III("ws://fake")
@@ -153,8 +153,8 @@ def test_call_void_queued_while_disconnected_flushes_after_connect(
153
153
  return ws
154
154
 
155
155
  monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
156
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
157
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
156
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
157
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
158
158
  monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
159
159
 
160
160
  client = III("ws://fake")
@@ -11,8 +11,8 @@ def test_register_worker_returns_iii_instance(monkeypatch) -> None:
11
11
  async def fake_do_connect(self: III) -> None:
12
12
  return None
13
13
 
14
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
15
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
14
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
15
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
16
16
  monkeypatch.setattr(III, "_do_connect", fake_do_connect)
17
17
 
18
18
  client = register_worker("ws://fake")
@@ -28,7 +28,7 @@ def test_register_worker_is_sync() -> None:
28
28
 
29
29
 
30
30
  def test_connect_consumes_otel_from_init_options(monkeypatch) -> None:
31
- import iii.telemetry as telemetry
31
+ import iii_observability.telemetry as telemetry
32
32
 
33
33
  captured = {"config": None}
34
34
 
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- from iii.logger import Logger
3
+ from iii_observability import Logger
4
4
 
5
5
 
6
6
  def test_logger_uses_service_name() -> None:
@@ -7,7 +7,7 @@ import pytest
7
7
 
8
8
  @pytest.fixture(autouse=True)
9
9
  def reset_otel():
10
- from iii.telemetry import shutdown_otel
10
+ from iii_observability import shutdown_otel
11
11
 
12
12
  yield
13
13
  shutdown_otel()
@@ -49,9 +49,7 @@ def _setup_in_memory_log_provider():
49
49
 
50
50
  def test_logger_emits_otel_record_when_initialized():
51
51
  """Logger.info emits an OTel LogRecord with severity INFO when OTel is active."""
52
- from iii.logger import Logger
53
- from iii.telemetry import init_otel
54
- from iii.telemetry_types import OtelConfig
52
+ from iii_observability import Logger, OtelConfig, init_otel
55
53
 
56
54
  log_exporter = _setup_in_memory_log_provider()
57
55
  init_otel(OtelConfig(enabled=True, logs_enabled=False)) # skip EngineLogExporter
@@ -68,11 +66,11 @@ def test_logger_emits_otel_record_when_initialized():
68
66
  def test_logger_emits_warn_severity():
69
67
  from opentelemetry._logs import SeverityNumber
70
68
 
71
- from iii.logger import Logger
69
+ from iii_observability import Logger
72
70
 
73
71
  log_exporter = _setup_in_memory_log_provider()
74
72
 
75
- with patch("iii.logger._is_initialized", return_value=True):
73
+ with patch("iii_observability.logger._is_initialized", return_value=True):
76
74
  logger = Logger()
77
75
  logger.warn("watch out")
78
76
 
@@ -86,7 +84,7 @@ def test_logger_attaches_trace_context_from_active_span():
86
84
  from opentelemetry import trace
87
85
  from opentelemetry.sdk.trace import TracerProvider
88
86
 
89
- from iii.logger import Logger
87
+ from iii_observability import Logger
90
88
 
91
89
  log_exporter = _setup_in_memory_log_provider()
92
90
 
@@ -94,7 +92,7 @@ def test_logger_attaches_trace_context_from_active_span():
94
92
  trace.set_tracer_provider(tracer_provider)
95
93
  tracer = tracer_provider.get_tracer("test")
96
94
 
97
- with patch("iii.logger._is_initialized", return_value=True):
95
+ with patch("iii_observability.logger._is_initialized", return_value=True):
98
96
  with tracer.start_as_current_span("test-span") as span:
99
97
  logger = Logger(service_name="fn1")
100
98
  logger.info("inside span")
@@ -110,11 +108,11 @@ def test_logger_attaches_trace_context_from_active_span():
110
108
 
111
109
  def test_logger_no_trace_context_outside_span():
112
110
  """Logger emits zero trace_id/span_id when no active span."""
113
- from iii.logger import Logger
111
+ from iii_observability import Logger
114
112
 
115
113
  log_exporter = _setup_in_memory_log_provider()
116
114
 
117
- with patch("iii.logger._is_initialized", return_value=True):
115
+ with patch("iii_observability.logger._is_initialized", return_value=True):
118
116
  logger = Logger(service_name="fn1")
119
117
  logger.info("outside span")
120
118
 
@@ -1,6 +1,6 @@
1
1
  import os
2
2
 
3
- from iii.payload import (
3
+ from iii_observability import (
4
4
  REDACTED_PLACEHOLDER,
5
5
  redact,
6
6
  redact_and_truncate,
@@ -44,8 +44,8 @@ def _patch_ws(monkeypatch: pytest.MonkeyPatch) -> FakeWebSocket:
44
44
  return ws
45
45
 
46
46
  monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
47
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
48
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
47
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
48
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
49
49
  monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
50
50
  return ws
51
51
 
@@ -6,7 +6,7 @@ from opentelemetry.sdk.trace.export import SimpleSpanProcessor
6
6
  from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
7
7
  from opentelemetry.trace import StatusCode
8
8
 
9
- from iii.span_ops import (
9
+ from iii_observability import (
10
10
  current_span_is_recording,
11
11
  record_span_event,
12
12
  set_current_span_attribute,
@@ -46,8 +46,8 @@ def _patch_ws(monkeypatch: pytest.MonkeyPatch) -> FakeWebSocket:
46
46
  return ws
47
47
 
48
48
  monkeypatch.setattr(iii_module.websockets, "connect", fake_connect)
49
- monkeypatch.setattr("iii.telemetry.init_otel", lambda **kwargs: None)
50
- monkeypatch.setattr("iii.telemetry.attach_event_loop", lambda loop: None)
49
+ monkeypatch.setattr("iii_observability.telemetry.init_otel", lambda **kwargs: None)
50
+ monkeypatch.setattr("iii_observability.telemetry.attach_event_loop", lambda loop: None)
51
51
  monkeypatch.setattr(iii_module.III, "_register_worker_metadata", lambda self: None)
52
52
  return ws
53
53
 
@@ -4,8 +4,8 @@ import urllib.request
4
4
 
5
5
  import pytest
6
6
 
7
- from iii.telemetry import _get_tracer, _is_initialized, init_otel, shutdown_otel, shutdown_otel_async
8
- from iii.telemetry_types import OtelConfig
7
+ from iii_observability.telemetry import _get_tracer, _is_initialized, init_otel, shutdown_otel, shutdown_otel_async
8
+ from iii_observability import OtelConfig
9
9
 
10
10
  # URLLibInstrumentor patches OpenerDirector.open, not urlopen directly
11
11
  ORIGINAL_OPENER_OPEN = urllib.request.OpenerDirector.open
@@ -87,8 +87,8 @@ def test_shutdown_without_init_is_safe():
87
87
 
88
88
 
89
89
  def test_telemetry_apis_importable_from_submodules():
90
- from iii.telemetry import _get_tracer, _is_initialized, init_otel, shutdown_otel
91
- from iii.telemetry_types import OtelConfig
90
+ from iii_observability.telemetry import _get_tracer, _is_initialized, init_otel, shutdown_otel
91
+ from iii_observability import OtelConfig
92
92
 
93
93
  assert callable(init_otel)
94
94
  assert callable(shutdown_otel)
@@ -103,7 +103,7 @@ def test_init_configures_engine_span_exporter():
103
103
  from opentelemetry.sdk.trace import TracerProvider
104
104
  from opentelemetry.sdk.trace.export import BatchSpanProcessor
105
105
 
106
- from iii.telemetry_exporters import EngineSpanExporter
106
+ from iii_observability.telemetry_exporters import EngineSpanExporter
107
107
 
108
108
  init_otel(OtelConfig(enabled=True))
109
109
  provider = trace.get_tracer_provider()
@@ -139,7 +139,7 @@ def test_shutdown_closes_connection():
139
139
  import asyncio
140
140
  from unittest.mock import AsyncMock, patch
141
141
 
142
- from iii.telemetry_exporters import SharedEngineConnection
142
+ from iii_observability.telemetry_exporters import SharedEngineConnection
143
143
 
144
144
  with (
145
145
  patch.object(SharedEngineConnection, "start"),
@@ -6,7 +6,7 @@ from unittest.mock import MagicMock
6
6
 
7
7
  import pytest
8
8
 
9
- from iii.telemetry_exporters import EngineLogExporter, EngineSpanExporter, SharedEngineConnection
9
+ from iii_observability.telemetry_exporters import EngineLogExporter, EngineSpanExporter, SharedEngineConnection
10
10
 
11
11
 
12
12
  def _make_mock_connection():
@@ -184,7 +184,7 @@ def test_serialize_metrics_emits_empty_string_for_missing_scope_version():
184
184
  the Python OTel SDK. The serializer must emit "" (not JSON null) so the Rust
185
185
  engine's String deserializer doesn't reject the payload.
186
186
  """
187
- from iii.telemetry_exporters import _serialize_metrics
187
+ from iii_observability.telemetry_exporters import _serialize_metrics
188
188
  from opentelemetry.sdk.metrics import MeterProvider
189
189
  from opentelemetry.sdk.metrics.export import InMemoryMetricReader
190
190
  from opentelemetry.sdk.resources import Resource
@@ -1,6 +1,6 @@
1
1
  """Tests for OtelConfig dataclass."""
2
2
 
3
- from iii.telemetry_types import OtelConfig
3
+ from iii_observability import OtelConfig
4
4
 
5
5
 
6
6
  def test_otel_config_defaults():
@@ -1,7 +1,7 @@
1
1
  from opentelemetry import trace
2
2
  from opentelemetry.sdk.trace import TracerProvider
3
3
 
4
- from iii.telemetry import current_span_id, current_trace_id
4
+ from iii_observability import current_span_id, current_trace_id
5
5
 
6
6
 
7
7
  def test_trace_helpers_follow_the_active_span() -> None:
@@ -194,6 +194,15 @@ wheels = [
194
194
  { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
195
195
  ]
196
196
 
197
+ [[package]]
198
+ name = "certifi"
199
+ version = "2026.5.20"
200
+ source = { registry = "https://pypi.org/simple" }
201
+ sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
202
+ wheels = [
203
+ { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
204
+ ]
205
+
197
206
  [[package]]
198
207
  name = "colorama"
199
208
  version = "0.4.6"
@@ -489,6 +498,43 @@ wheels = [
489
498
  { url = "https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004, upload-time = "2026-02-09T19:09:40.561Z" },
490
499
  ]
491
500
 
501
+ [[package]]
502
+ name = "h11"
503
+ version = "0.16.0"
504
+ source = { registry = "https://pypi.org/simple" }
505
+ sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
506
+ wheels = [
507
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
508
+ ]
509
+
510
+ [[package]]
511
+ name = "httpcore"
512
+ version = "1.0.9"
513
+ source = { registry = "https://pypi.org/simple" }
514
+ dependencies = [
515
+ { name = "certifi" },
516
+ { name = "h11" },
517
+ ]
518
+ sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
519
+ wheels = [
520
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
521
+ ]
522
+
523
+ [[package]]
524
+ name = "httpx"
525
+ version = "0.28.1"
526
+ source = { registry = "https://pypi.org/simple" }
527
+ dependencies = [
528
+ { name = "anyio" },
529
+ { name = "certifi" },
530
+ { name = "httpcore" },
531
+ { name = "idna" },
532
+ ]
533
+ sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
534
+ wheels = [
535
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
536
+ ]
537
+
492
538
  [[package]]
493
539
  name = "idna"
494
540
  version = "3.11"
@@ -498,13 +544,39 @@ wheels = [
498
544
  { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
499
545
  ]
500
546
 
547
+ [[package]]
548
+ name = "iii-observability"
549
+ version = "0.13.0.dev1"
550
+ source = { editable = "../observability" }
551
+ dependencies = [
552
+ { name = "httpx" },
553
+ { name = "opentelemetry-api" },
554
+ { name = "opentelemetry-sdk" },
555
+ { name = "websockets" },
556
+ ]
557
+
558
+ [package.metadata]
559
+ requires-dist = [
560
+ { name = "httpx", specifier = ">=0.27" },
561
+ { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.8" },
562
+ { name = "opentelemetry-api", specifier = ">=1.25" },
563
+ { name = "opentelemetry-sdk", specifier = ">=1.25" },
564
+ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
565
+ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
566
+ { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0" },
567
+ { name = "pytest-httpx", marker = "extra == 'dev'", specifier = ">=0.30" },
568
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.2" },
569
+ { name = "websockets", specifier = ">=12.0" },
570
+ ]
571
+ provides-extras = ["dev"]
572
+
501
573
  [[package]]
502
574
  name = "iii-sdk"
503
- version = "0.12.0"
575
+ version = "0.13.0.dev1"
504
576
  source = { editable = "." }
505
577
  dependencies = [
578
+ { name = "iii-observability" },
506
579
  { name = "opentelemetry-api" },
507
- { name = "opentelemetry-sdk" },
508
580
  { name = "pydantic" },
509
581
  { name = "websockets" },
510
582
  ]
@@ -526,9 +598,9 @@ requires-dist = [
526
598
  { name = "aiohttp", marker = "extra == 'dev'", specifier = ">=3.9" },
527
599
  { name = "anyio", marker = "extra == 'dev'", specifier = ">=4.0" },
528
600
  { name = "griffe", marker = "extra == 'dev'", specifier = ">=1.0" },
601
+ { name = "iii-observability", editable = "../observability" },
529
602
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.8" },
530
603
  { name = "opentelemetry-api", specifier = ">=1.25" },
531
- { name = "opentelemetry-sdk", specifier = ">=1.25" },
532
604
  { name = "pydantic", specifier = ">=2.0" },
533
605
  { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
534
606
  { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },