iii-sdk 0.14.0.dev1__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.dev1 → iii_sdk-0.15.0.dev1}/PKG-INFO +2 -2
  2. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/pyproject.toml +5 -2
  3. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/__init__.py +41 -26
  4. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/iii.py +21 -43
  5. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/iii_constants.py +1 -20
  6. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/iii_types.py +0 -139
  7. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/types.py +0 -3
  8. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_baggage_span_processor.py +1 -1
  9. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_bridge.py +4 -4
  10. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_context_propagation.py +1 -2
  11. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_hold_process.py +6 -6
  12. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_http_external_functions_integration.py +2 -2
  13. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_iii_registration_dedup.py +7 -7
  14. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_init_api.py +3 -3
  15. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_logger_function_ids.py +1 -1
  16. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_logger_otel.py +8 -10
  17. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_payload.py +1 -1
  18. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_rbac_workers.py +4 -5
  19. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_register_function_args.py +2 -2
  20. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_span_ops.py +1 -1
  21. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_sync_api.py +3 -25
  22. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_telemetry.py +18 -18
  23. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_telemetry_exporters.py +2 -2
  24. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_telemetry_types.py +1 -1
  25. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_trace_helpers.py +1 -1
  26. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_trigger_metadata.py +2 -18
  27. iii_sdk-0.15.0.dev1/tests/test_trigger_registration_error.py +57 -0
  28. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/uv.lock +1152 -1080
  29. iii_sdk-0.14.0.dev1/src/iii/baggage_span_processor.py +0 -42
  30. iii_sdk-0.14.0.dev1/src/iii/logger.py +0 -184
  31. iii_sdk-0.14.0.dev1/src/iii/payload.py +0 -92
  32. iii_sdk-0.14.0.dev1/src/iii/span_ops.py +0 -38
  33. iii_sdk-0.14.0.dev1/src/iii/telemetry.py +0 -571
  34. iii_sdk-0.14.0.dev1/src/iii/telemetry_exporters.py +0 -457
  35. iii_sdk-0.14.0.dev1/src/iii/telemetry_types.py +0 -46
  36. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/.gitignore +0 -0
  37. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/README.md +0 -0
  38. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/channels.py +0 -0
  39. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/errors.py +0 -0
  40. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/format_utils.py +0 -0
  41. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/otel_worker_gauges.py +0 -0
  42. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/state.py +0 -0
  43. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/stream.py +0 -0
  44. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/triggers.py +0 -0
  45. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/utils.py +0 -0
  46. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/src/iii/worker_metrics.py +0 -0
  47. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/conftest.py +0 -0
  48. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_api_triggers.py +0 -0
  49. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_async_api.py +0 -0
  50. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_channel_close_delay.py +0 -0
  51. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_data_channels.py +0 -0
  52. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_errors.py +0 -0
  53. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_format_utils.py +0 -0
  54. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_healthcheck.py +0 -0
  55. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_invocation_exception.py +0 -0
  56. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_middleware.py +0 -0
  57. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_pubsub.py +0 -0
  58. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_queue_integration.py +0 -0
  59. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_state.py +0 -0
  60. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_stream_models.py +0 -0
  61. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_streams.py +0 -0
  62. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_streams_runtime_annotations.py +0 -0
  63. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_utils.py +0 -0
  64. {iii_sdk-0.14.0.dev1 → iii_sdk-0.15.0.dev1}/tests/test_worker_metadata.py +0 -0
  65. {iii_sdk-0.14.0.dev1 → 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.dev1
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.dev1"
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,16 +1,41 @@
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,
12
38
  EnqueueResult,
13
- FunctionInfo,
14
39
  HttpAuthConfig,
15
40
  HttpInvocationConfig,
16
41
  MessageType,
@@ -23,7 +48,6 @@ from .iii_types import (
23
48
  OnTriggerTypeRegistrationResult,
24
49
  RegisterFunctionFormat,
25
50
  RegisterFunctionMessage,
26
- RegisterServiceInput,
27
51
  RegisterTriggerInput,
28
52
  RegisterTriggerMessage,
29
53
  RegisterTriggerTypeInput,
@@ -31,22 +55,7 @@ from .iii_types import (
31
55
  StreamChannelRef,
32
56
  TriggerActionEnqueue,
33
57
  TriggerActionVoid,
34
- TriggerInfo,
35
58
  TriggerRequest,
36
- TriggerTypeInfo,
37
- )
38
- from .logger import Logger
39
- from .payload import (
40
- REDACTED_PLACEHOLDER,
41
- redact,
42
- redact_and_truncate,
43
- resolve_max_bytes_from_env,
44
- )
45
- from .span_ops import (
46
- current_span_is_recording,
47
- record_span_event,
48
- set_current_span_attribute,
49
- set_current_span_error,
50
59
  )
51
60
  from .stream import (
52
61
  IStream,
@@ -57,7 +66,6 @@ from .stream import (
57
66
  StreamJoinLeaveTriggerConfig,
58
67
  StreamTriggerConfig,
59
68
  )
60
- from .telemetry_types import OtelConfig
61
69
  from .triggers import Trigger, TriggerConfig, TriggerHandler, TriggerTypeRef
62
70
  from .types import (
63
71
  ApiRequest,
@@ -76,13 +84,24 @@ __all__ = [
76
84
  "BaggageSpanProcessor",
77
85
  "DEFAULT_ALLOWLIST",
78
86
  "REDACTED_PLACEHOLDER",
87
+ "current_span_id",
79
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",
80
97
  "record_span_event",
81
- "set_current_span_attribute",
82
- "set_current_span_error",
83
98
  "redact",
84
99
  "redact_and_truncate",
85
100
  "resolve_max_bytes_from_env",
101
+ "set_current_span_attribute",
102
+ "set_current_span_error",
103
+ "shutdown_otel",
104
+ "with_span",
86
105
  # Channels
87
106
  "ChannelReader",
88
107
  "ChannelWriter",
@@ -110,13 +129,11 @@ __all__ = [
110
129
  "OnTriggerTypeRegistrationResult",
111
130
  # Message types
112
131
  "EnqueueResult",
113
- "FunctionInfo",
114
132
  "HttpAuthConfig",
115
133
  "HttpInvocationConfig",
116
134
  "MessageType",
117
135
  "RegisterFunctionFormat",
118
136
  "RegisterFunctionMessage",
119
- "RegisterServiceInput",
120
137
  "RegisterTriggerInput",
121
138
  "RegisterTriggerMessage",
122
139
  "RegisterTriggerTypeInput",
@@ -124,9 +141,7 @@ __all__ = [
124
141
  "StreamChannelRef",
125
142
  "TriggerActionEnqueue",
126
143
  "TriggerActionVoid",
127
- "TriggerInfo",
128
144
  "TriggerRequest",
129
- "TriggerTypeInfo",
130
145
  # Logger
131
146
  "Logger",
132
147
  # Triggers
@@ -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
@@ -34,8 +35,6 @@ from .iii_types import (
34
35
  RegisterFunctionFormat,
35
36
  RegisterFunctionInput,
36
37
  RegisterFunctionMessage,
37
- RegisterServiceInput,
38
- RegisterServiceMessage,
39
38
  RegisterTriggerInput,
40
39
  RegisterTriggerMessage,
41
40
  RegisterTriggerTypeInput,
@@ -56,7 +55,6 @@ from .stream import (
56
55
  StreamListInput,
57
56
  StreamSetInput,
58
57
  )
59
- from .telemetry_types import OtelConfig
60
58
  from .triggers import Trigger, TriggerConfig, TriggerHandler, TriggerTypeRef
61
59
  from .types import Channel, RemoteFunctionData, RemoteTriggerTypeData, is_channel_ref
62
60
 
@@ -152,7 +150,6 @@ class III:
152
150
  self._options = options or InitOptions()
153
151
  self._ws: ClientConnection | None = None
154
152
  self._functions: dict[str, RemoteFunctionData] = {}
155
- self._services: dict[str, RegisterServiceMessage] = {}
156
153
  self._pending: dict[str, _PendingInvocation] = {}
157
154
  self._triggers: dict[str, RegisterTriggerMessage] = {}
158
155
  self._trigger_types: dict[str, RemoteTriggerTypeData] = {}
@@ -227,7 +224,7 @@ class III:
227
224
  from an async context.
228
225
  """
229
226
  self._running = True
230
- from .telemetry import attach_event_loop, init_otel
227
+ from iii_observability.telemetry import attach_event_loop, init_otel
231
228
 
232
229
  loop = asyncio.get_running_loop()
233
230
  otel_cfg: OtelConfig | None = None
@@ -283,7 +280,7 @@ class III:
283
280
 
284
281
  self._set_connection_state("disconnected")
285
282
 
286
- from .telemetry import shutdown_otel_async
283
+ from iii_observability.telemetry import shutdown_otel_async
287
284
 
288
285
  await shutdown_otel_async()
289
286
 
@@ -344,8 +341,6 @@ class III:
344
341
  # Re-register all (snapshot to avoid mutation from caller thread)
345
342
  for trigger_type_data in list(self._trigger_types.values()):
346
343
  await self._send(trigger_type_data.message)
347
- for svc in list(self._services.values()):
348
- await self._send(svc)
349
344
  for function_data in list(self._functions.values()):
350
345
  await self._send(function_data.message)
351
346
  for trigger in list(self._triggers.values()):
@@ -443,6 +438,8 @@ class III:
443
438
  )
444
439
  elif msg_type == MessageType.REGISTER_TRIGGER.value:
445
440
  asyncio.create_task(self._handle_trigger_registration(data))
441
+ elif msg_type == MessageType.TRIGGER_REGISTRATION_RESULT.value:
442
+ self._handle_trigger_registration_result(data)
446
443
  elif msg_type == MessageType.WORKER_REGISTERED.value:
447
444
  worker_id = data.get("worker_id", "")
448
445
  self._worker_id = worker_id
@@ -502,7 +499,7 @@ class III:
502
499
  tracer = trace.get_tracer("iii-python-sdk")
503
500
  import os
504
501
 
505
- from .payload import redact_and_truncate, resolve_max_bytes_from_env
502
+ from iii_observability import redact_and_truncate, resolve_max_bytes_from_env
506
503
 
507
504
  trace_payloads = os.environ.get("III_DISABLE_TRACE_PAYLOADS", "").lower() not in (
508
505
  "1",
@@ -706,6 +703,21 @@ class III:
706
703
  }
707
704
  )
708
705
 
706
+ def _handle_trigger_registration_result(self, data: dict[str, Any]) -> None:
707
+ error = data.get("error")
708
+ if not error:
709
+ return
710
+
711
+ trigger_id = data.get("id", "")
712
+ trigger_type = data.get("trigger_type", "")
713
+ message = error.get("message", "")
714
+ log.error(
715
+ "[iii] Trigger registration failed for %r (%s): %s",
716
+ trigger_id,
717
+ trigger_type,
718
+ message,
719
+ )
720
+
709
721
  # Connection state management
710
722
 
711
723
  def _set_connection_state(self, state: IIIConnectionState) -> None:
@@ -1014,40 +1026,6 @@ class III:
1014
1026
 
1015
1027
  return FunctionRef(id=func_id, unregister=unregister)
1016
1028
 
1017
- def register_service(self, service: RegisterServiceInput | dict[str, Any]) -> None:
1018
- """Register a logical service grouping with the engine.
1019
-
1020
- Services provide an organisational hierarchy for functions. A
1021
- service can optionally reference a ``parent_service_id`` to form
1022
- a tree visible in the engine dashboard.
1023
-
1024
- Note:
1025
- Services are organizational groupings visible in the engine
1026
- dashboard. They do not affect function invocation behavior.
1027
-
1028
- Args:
1029
- service: A ``RegisterServiceInput`` or dict with ``id`` and
1030
- optional ``name``, ``description``, ``parent_service_id``.
1031
-
1032
- Examples:
1033
- >>> iii.register_service({"id": "payments", "description": "Payment processing"})
1034
- >>> iii.register_service({
1035
- ... "id": "payments::refunds",
1036
- ... "description": "Refund sub-service",
1037
- ... "parent_service_id": "payments",
1038
- ... })
1039
- """
1040
- if isinstance(service, dict):
1041
- service = RegisterServiceInput(**service)
1042
- msg = RegisterServiceMessage(
1043
- id=service.id,
1044
- name=service.name or service.id,
1045
- description=service.description,
1046
- parent_service_id=service.parent_service_id,
1047
- )
1048
- self._services[service.id] = msg
1049
- self._send_if_connected(msg)
1050
-
1051
1029
  def trigger(self, request: "dict[str, Any] | TriggerRequest") -> Any:
1052
1030
  """Invoke a remote function.
1053
1031
 
@@ -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
 
@@ -148,25 +148,6 @@ class RegisterTriggerInput(BaseModel):
148
148
  )
149
149
 
150
150
 
151
- class RegisterServiceInput(BaseModel):
152
- """Input for registering a service (matches Node SDK's RegisterServiceInput).
153
-
154
- Attributes:
155
- id: Unique service identifier.
156
- name: Human-readable service name.
157
- description: Description of the service.
158
- parent_service_id: ID of the parent service for hierarchical grouping.
159
- """
160
-
161
- id: str = Field(description="Unique service identifier.")
162
- name: str | None = Field(default=None, description="Human-readable service name.")
163
- description: str | None = Field(default=None, description="Description of the service.")
164
- parent_service_id: str | None = Field(
165
- default=None,
166
- description="ID of the parent service for hierarchical grouping.",
167
- )
168
-
169
-
170
151
  class RegisterTriggerMessage(BaseModel):
171
152
  model_config = ConfigDict(populate_by_name=True)
172
153
 
@@ -178,16 +159,6 @@ class RegisterTriggerMessage(BaseModel):
178
159
  message_type: MessageType = Field(default=MessageType.REGISTER_TRIGGER, alias="type")
179
160
 
180
161
 
181
- class RegisterServiceMessage(BaseModel):
182
- model_config = ConfigDict(populate_by_name=True)
183
-
184
- id: str
185
- name: str | None = None
186
- description: str | None = None
187
- parent_service_id: str | None = Field(default=None)
188
- message_type: MessageType = Field(default=MessageType.REGISTER_SERVICE, alias="type")
189
-
190
-
191
162
  class RegisterFunctionFormat(BaseModel):
192
163
  """Format definition for function parameters.
193
164
 
@@ -533,115 +504,6 @@ class UnregisterFunctionMessage(BaseModel):
533
504
  message_type: MessageType = Field(default=MessageType.UNREGISTER_FUNCTION, alias="type")
534
505
 
535
506
 
536
- class FunctionInfo(BaseModel):
537
- """Information about a registered function.
538
-
539
- Attributes:
540
- function_id: Unique identifier of the function.
541
- description: Human-readable description.
542
- request_format: Schema describing expected input (JSON Schema or custom format).
543
- response_format: Schema describing expected output (JSON Schema or custom format).
544
- metadata: Arbitrary metadata attached to the function.
545
- """
546
-
547
- function_id: str = Field(description="Unique identifier of the function.")
548
- description: str | None = Field(default=None, description="Human-readable description.")
549
- request_format: dict[str, Any] | None = Field(
550
- default=None, description="Schema describing expected input (JSON Schema or custom format)."
551
- )
552
- response_format: dict[str, Any] | None = Field(
553
- default=None, description="Schema describing expected output (JSON Schema or custom format)."
554
- )
555
- metadata: dict[str, Any] | None = Field(
556
- default=None, description="Arbitrary metadata attached to the function."
557
- )
558
-
559
-
560
- class TriggerInfo(BaseModel):
561
- """Information about a registered trigger.
562
-
563
- Attributes:
564
- id: Unique trigger identifier.
565
- trigger_type: Type of trigger (e.g. ``http``, ``queue``, ``cron``).
566
- function_id: ID of the function this trigger invokes.
567
- config: Trigger-type-specific configuration.
568
- metadata: Arbitrary metadata attached to the trigger.
569
- """
570
-
571
- id: str = Field(description="Unique trigger identifier.")
572
- trigger_type: str = Field(description="Type of trigger (e.g. ``http``, ``queue``, ``cron``).")
573
- function_id: str = Field(description="ID of the function this trigger invokes.")
574
- config: Any = Field(default=None, description="Trigger-type-specific configuration.")
575
- metadata: dict[str, Any] | None = Field(
576
- default=None, description="Arbitrary metadata attached to the trigger."
577
- )
578
-
579
-
580
- class TriggerTypeInfo(BaseModel):
581
- """Information about a registered trigger type.
582
-
583
- Attributes:
584
- id: Trigger type identifier (e.g. ``http``, ``cron``, ``queue``).
585
- description: Human-readable description of the trigger type.
586
- trigger_request_format: JSON Schema for the trigger configuration.
587
- call_request_format: JSON Schema for the call request payload.
588
- """
589
-
590
- id: str = Field(description="Trigger type identifier.")
591
- description: str = Field(description="Human-readable description.")
592
- trigger_request_format: Any | None = Field(
593
- default=None, description="JSON Schema for trigger configuration."
594
- )
595
- call_request_format: Any | None = Field(
596
- default=None, description="JSON Schema for the call request payload."
597
- )
598
-
599
-
600
- WorkerStatus = Literal["connected", "available", "busy", "disconnected"]
601
-
602
-
603
- class WorkerInfo(BaseModel):
604
- """Information about a connected worker.
605
-
606
- Attributes:
607
- id: Engine-assigned unique worker ID.
608
- name: Worker name from InitOptions.
609
- runtime: SDK runtime (``python``, ``node``, ``rust``).
610
- version: SDK version string.
611
- os: Operating system identifier.
612
- ip_address: Worker's IP address as seen by the engine.
613
- status: Current status (``connected``, ``available``, ``busy``, ``disconnected``).
614
- connected_at_ms: Connection timestamp in milliseconds since epoch.
615
- function_count: Number of registered functions.
616
- functions: List of registered function IDs.
617
- active_invocations: Number of currently executing invocations.
618
- """
619
-
620
- id: str = Field(description="Engine-assigned unique worker ID.")
621
- name: str | None = Field(default=None, description="Worker name from InitOptions.")
622
- runtime: str | None = Field(
623
- default=None,
624
- description="SDK runtime (``python``, ``node``, ``rust``).",
625
- )
626
- version: str | None = Field(default=None, description="SDK version string.")
627
- os: str | None = Field(default=None, description="Operating system identifier.")
628
- ip_address: str | None = Field(
629
- default=None,
630
- description="Worker's IP address as seen by the engine.",
631
- )
632
- status: WorkerStatus = Field(
633
- description="Current status (``connected``, ``available``, ``busy``, ``disconnected``)."
634
- )
635
- connected_at_ms: int = Field(description="Connection timestamp in milliseconds since epoch.")
636
- function_count: int = Field(description="Number of registered functions.")
637
- functions: list[str] = Field(description="List of registered function IDs.")
638
- active_invocations: int = Field(description="Number of currently executing invocations.")
639
- isolation: str | None = Field(
640
- default=None,
641
- description="Self-reported isolation context (e.g. ``libkrun``, ``docker``, ``k8s``).",
642
- )
643
-
644
-
645
507
  class StreamChannelRef(BaseModel):
646
508
  """Reference to a streaming channel for worker-to-worker data transfer.
647
509
 
@@ -678,7 +540,6 @@ IIIMessage = (
678
540
  | UnregisterFunctionMessage
679
541
  | InvokeFunctionMessage
680
542
  | InvocationResultMessage
681
- | RegisterServiceMessage
682
543
  | RegisterTriggerMessage
683
544
  | RegisterTriggerTypeMessage
684
545
  | UnregisterTriggerMessage
@@ -12,7 +12,6 @@ from pydantic import BaseModel, ConfigDict, Field
12
12
  from .iii_types import (
13
13
  HttpInvocationConfig,
14
14
  RegisterFunctionMessage,
15
- RegisterServiceInput,
16
15
  RegisterTriggerInput,
17
16
  RegisterTriggerTypeInput,
18
17
  RegisterTriggerTypeMessage,
@@ -82,8 +81,6 @@ class IIIClient(Protocol):
82
81
 
83
82
  def register_trigger(self, trigger: RegisterTriggerInput | dict[str, Any]) -> Trigger: ...
84
83
 
85
- def register_service(self, service: RegisterServiceInput | dict[str, Any]) -> None: ...
86
-
87
84
  def register_function(
88
85
  self,
89
86
  function_id: str,
@@ -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
  )
@@ -4,7 +4,7 @@ import asyncio
4
4
 
5
5
  import pytest
6
6
 
7
- from iii import FunctionInfo, TriggerAction
7
+ from iii import TriggerAction
8
8
  from iii.iii import III
9
9
 
10
10
 
@@ -22,7 +22,7 @@ async def wait_for(condition, timeout=5.0, interval=0.1):
22
22
  async def test_connect_successfully(iii_client: III):
23
23
  """SDK connects to the engine and can list functions."""
24
24
  result = iii_client.trigger({"function_id": "engine::functions::list", "payload": {}})
25
- functions = [FunctionInfo(**f) for f in result.get("functions", [])]
25
+ functions = result.get("functions", [])
26
26
  assert isinstance(functions, list)
27
27
 
28
28
 
@@ -93,8 +93,8 @@ async def test_list_registered_functions(iii_client: III):
93
93
 
94
94
  try:
95
95
  result = iii_client.trigger({"function_id": "engine::functions::list", "payload": {}})
96
- functions = [FunctionInfo(**f) for f in result.get("functions", [])]
97
- function_ids = [f.function_id for f in functions]
96
+ functions = result.get("functions", [])
97
+ function_ids = [f["function_id"] for f in functions]
98
98
 
99
99
  assert "test.bridge.py.list.func1" in function_ids
100
100
  assert "test.bridge.py.list.func2" in function_ids
@@ -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")