piphi-runtime-kit-python 0.4.1__py3-none-any.whl → 0.4.3__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.
@@ -8,7 +8,18 @@ from .runtime.auth import (
8
8
  format_runtime_auth_sync_log,
9
9
  mask_token,
10
10
  )
11
- from .runtime.config_sync import ConfigSyncCoordinator, build_sync_response, reconcile_config_ids
11
+ from .runtime.config_sync import (
12
+ CORE_BASE_URL_ENV_NAME,
13
+ DEFAULT_RUNTIME_CONFIG_SNAPSHOT_FILENAME,
14
+ DEFAULT_RUNTIME_VOLUME_DIR,
15
+ RUNTIME_CONFIG_SNAPSHOT_PATH_ENV_NAME,
16
+ ConfigSyncCoordinator,
17
+ build_sync_response,
18
+ load_runtime_config_snapshot,
19
+ reconcile_config_ids,
20
+ resolve_core_base_url,
21
+ resolve_runtime_config_snapshot_path,
22
+ )
12
23
  from .runtime.configuration import (
13
24
  build_config_apply_response,
14
25
  build_config_remove_response,
@@ -49,6 +60,7 @@ from .runtime.events import (
49
60
  normalize_event_payload,
50
61
  )
51
62
  from .runtime.health import build_runtime_diagnostics_response, build_runtime_health_response
63
+ from .runtime.identity import build_runtime_identity, resolve_config_id, resolve_device_id
52
64
  from .runtime.lifespan import (
53
65
  DEFAULT_CORE_CLIENT_TIMEOUT_SECONDS,
54
66
  DEFAULT_RUNTIME_CONTAINER_ID_ENV_NAME,
@@ -80,6 +92,8 @@ from .schemas import (
80
92
  IntegrationCommandRequest,
81
93
  IntegrationDiscoveryRequest,
82
94
  IntegrationDiscoveryResponse,
95
+ RuntimeEntityCommandFieldResponse,
96
+ RuntimeEntityCommandResponse,
83
97
  RuntimeEntityDashboardResponse,
84
98
  RuntimeEntitiesResponse,
85
99
  RuntimeEntityResponse,
@@ -106,6 +120,10 @@ __all__ = [
106
120
  "CoreUnavailableError",
107
121
  "CoreUnexpectedResponseError",
108
122
  "DEFAULT_CORE_CLIENT_TIMEOUT_SECONDS",
123
+ "CORE_BASE_URL_ENV_NAME",
124
+ "DEFAULT_RUNTIME_CONFIG_SNAPSHOT_FILENAME",
125
+ "DEFAULT_RUNTIME_VOLUME_DIR",
126
+ "RUNTIME_CONFIG_SNAPSHOT_PATH_ENV_NAME",
109
127
  "DEFAULT_RUNTIME_CONTAINER_ID_ENV_NAME",
110
128
  "DEFAULT_RUNTIME_INTERNAL_TOKEN_ENV_NAME",
111
129
  "RUNTIME_CONTAINER_ID_HEADER_NAME",
@@ -113,6 +131,8 @@ __all__ = [
113
131
  "IntegrationCommandRequest",
114
132
  "IntegrationDiscoveryRequest",
115
133
  "IntegrationDiscoveryResponse",
134
+ "RuntimeEntityCommandFieldResponse",
135
+ "RuntimeEntityCommandResponse",
116
136
  "RuntimeEntityDashboardResponse",
117
137
  "RuntimeEntitiesResponse",
118
138
  "RuntimeEntityResponse",
@@ -144,6 +164,7 @@ __all__ = [
144
164
  "build_event_ingest_response",
145
165
  "build_event_list_response",
146
166
  "build_local_event_record",
167
+ "build_runtime_identity",
147
168
  "build_core_auth_headers",
148
169
  "build_config_apply_response",
149
170
  "build_config_remove_response",
@@ -163,6 +184,7 @@ __all__ = [
163
184
  "format_discovery_attempt_log",
164
185
  "format_event_log",
165
186
  "format_runtime_auth_sync_log",
187
+ "load_runtime_config_snapshot",
166
188
  "mask_token",
167
189
  "MqttBrokerConfig",
168
190
  "MqttJsonClient",
@@ -170,11 +192,15 @@ __all__ = [
170
192
  "normalize_event_payload",
171
193
  "redact_config_secrets",
172
194
  "reconcile_config_ids",
195
+ "resolve_core_base_url",
196
+ "resolve_runtime_config_snapshot_path",
173
197
  "runtime_lifespan",
174
198
  "schedule_event_delivery",
175
199
  "schedule_telemetry_delivery",
176
200
  "shutdown_background_tasks",
177
201
  "track_background_task",
202
+ "resolve_config_id",
203
+ "resolve_device_id",
178
204
  "validate_typed_config",
179
205
  "validate_typed_configs",
180
206
  "DEFAULT_SOURCE_TOPIC_PREFIX",
@@ -3,6 +3,9 @@ from __future__ import annotations
3
3
  """Helpers for basic config-sync orchestration and generation tracking."""
4
4
 
5
5
  from dataclasses import dataclass
6
+ import json
7
+ import os
8
+ from pathlib import Path
6
9
  from typing import Awaitable, Callable, TypeVar
7
10
 
8
11
  from ..schemas import RuntimeConfig, RuntimeConfigSnapshot, RuntimeConfigSyncResponse
@@ -10,6 +13,77 @@ from .state import RuntimeProcessState
10
13
 
11
14
  TConfig = TypeVar("TConfig", bound=RuntimeConfig)
12
15
 
16
+ CORE_BASE_URL_ENV_NAME = "PIPHI_CORE_BASE_URL"
17
+ RUNTIME_CONTAINER_ID_ENV_NAME = "PIPHI_CONTAINER_ID"
18
+ RUNTIME_CONFIG_SNAPSHOT_PATH_ENV_NAME = "PIPHI_CONFIG_SNAPSHOT_PATH"
19
+ DEFAULT_RUNTIME_VOLUME_DIR = "/.piphinetwork"
20
+ DEFAULT_RUNTIME_CONFIG_SNAPSHOT_FILENAME = "configs.json"
21
+
22
+
23
+ def resolve_core_base_url(default: str | None = None) -> str | None:
24
+ """Resolve Core's base URL from the managed-runtime environment."""
25
+ value = (os.getenv(CORE_BASE_URL_ENV_NAME) or "").strip().rstrip("/")
26
+ if value:
27
+ return value
28
+ return default.rstrip("/") if isinstance(default, str) and default.strip() else default
29
+
30
+
31
+ def resolve_runtime_config_snapshot_path(
32
+ *,
33
+ container_id: str | None = None,
34
+ volume_dir: str | Path = DEFAULT_RUNTIME_VOLUME_DIR,
35
+ ) -> Path:
36
+ """Resolve the local config snapshot path mounted by PiPhi Core."""
37
+ explicit_path = (os.getenv(RUNTIME_CONFIG_SNAPSHOT_PATH_ENV_NAME) or "").strip()
38
+ if explicit_path:
39
+ return Path(explicit_path)
40
+
41
+ resolved_container_id = (
42
+ str(container_id or "").strip()
43
+ or (os.getenv(RUNTIME_CONTAINER_ID_ENV_NAME) or "").strip()
44
+ )
45
+ if resolved_container_id:
46
+ return Path(volume_dir).joinpath(f"{resolved_container_id}.json")
47
+ return Path(volume_dir).joinpath(DEFAULT_RUNTIME_CONFIG_SNAPSHOT_FILENAME)
48
+
49
+
50
+ def load_runtime_config_snapshot(
51
+ *,
52
+ path: str | Path | None = None,
53
+ container_id: str | None = None,
54
+ volume_dir: str | Path = DEFAULT_RUNTIME_VOLUME_DIR,
55
+ ) -> RuntimeConfigSnapshot | None:
56
+ """Load Core's last known config snapshot from the mounted runtime volume.
57
+
58
+ Missing, unreadable, or invalid snapshots return ``None`` so integrations can
59
+ treat snapshot rehydration as a safe startup fallback rather than a crash
60
+ trigger.
61
+ """
62
+ snapshot_path = (
63
+ Path(path)
64
+ if path is not None
65
+ else resolve_runtime_config_snapshot_path(
66
+ container_id=container_id,
67
+ volume_dir=volume_dir,
68
+ )
69
+ )
70
+ try:
71
+ raw_payload = snapshot_path.read_text(encoding="utf-8")
72
+ except (FileNotFoundError, OSError):
73
+ return None
74
+
75
+ try:
76
+ payload = json.loads(raw_payload)
77
+ except json.JSONDecodeError:
78
+ return None
79
+ if not isinstance(payload, dict):
80
+ return None
81
+
82
+ try:
83
+ return RuntimeConfigSnapshot.model_validate(payload)
84
+ except ValueError:
85
+ return None
86
+
13
87
 
14
88
  def reconcile_config_ids(
15
89
  *,
@@ -2,8 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  """Helpers for background dispatch of telemetry and event delivery."""
4
4
 
5
+ import asyncio
5
6
  from typing import Any, Callable, Mapping
6
7
 
8
+ from .errors import CoreDeliveryError
7
9
  from .events import EventClient
8
10
  from .tasks import create_tracked_task
9
11
  from .telemetry import TelemetryClient
@@ -14,6 +16,9 @@ EventRecorder = Callable[[dict[str, Any]], Any]
14
16
  SkipHandler = Callable[[str, dict[str, Any]], Any]
15
17
  ErrorHandler = Callable[[Exception, dict[str, Any]], Any]
16
18
 
19
+ DEFAULT_RETRYABLE_DELIVERY_ATTEMPTS = 5
20
+ DEFAULT_RETRYABLE_DELIVERY_INITIAL_DELAY_SECONDS = 0.5
21
+
17
22
 
18
23
  def build_local_event_record(
19
24
  *,
@@ -36,6 +41,30 @@ def build_local_event_record(
36
41
  }
37
42
 
38
43
 
44
+ async def _run_with_retryable_core_delivery_backoff(
45
+ delivery: Callable[[], Any],
46
+ *,
47
+ max_attempts: int = DEFAULT_RETRYABLE_DELIVERY_ATTEMPTS,
48
+ initial_delay_seconds: float = DEFAULT_RETRYABLE_DELIVERY_INITIAL_DELAY_SECONDS,
49
+ ) -> Any:
50
+ """Retry transient Core delivery failures with a short exponential backoff."""
51
+
52
+ attempt = 1
53
+ delay_seconds = initial_delay_seconds
54
+ while True:
55
+ try:
56
+ result = delivery()
57
+ if asyncio.iscoroutine(result):
58
+ return await result
59
+ return result
60
+ except CoreDeliveryError as exc:
61
+ if not exc.retryable or attempt >= max_attempts:
62
+ raise
63
+ await asyncio.sleep(delay_seconds)
64
+ attempt += 1
65
+ delay_seconds *= 2
66
+
67
+
39
68
  async def dispatch_telemetry_delivery(
40
69
  *,
41
70
  telemetry_client: TelemetryClient,
@@ -59,13 +88,15 @@ async def dispatch_telemetry_delivery(
59
88
  return False
60
89
 
61
90
  try:
62
- await telemetry_client.send_metrics(
63
- device_id=device_id,
64
- metrics=metrics,
65
- auth_context=auth_context,
66
- container_id=resolved_container_id,
67
- units=units,
68
- timestamp=timestamp,
91
+ await _run_with_retryable_core_delivery_backoff(
92
+ lambda: telemetry_client.send_metrics(
93
+ device_id=device_id,
94
+ metrics=metrics,
95
+ auth_context=auth_context,
96
+ container_id=resolved_container_id,
97
+ units=units,
98
+ timestamp=timestamp,
99
+ )
69
100
  )
70
101
  return True
71
102
  except Exception as exc:
@@ -119,16 +150,18 @@ async def dispatch_event_delivery(
119
150
  ) -> bool:
120
151
  """Send one event to Core and return whether delivery succeeded."""
121
152
  try:
122
- event_sent = await event_client.send_event(
123
- event_type=event_type,
124
- integration_id=device.get("integration_id"),
125
- config_id=device.get("config_id") or device.get("device_id"),
126
- auth_context=auth_context,
127
- container_id=device.get("container_id"),
128
- device_id=device.get("device_id"),
129
- payload=payload,
130
- source=source,
131
- severity=severity,
153
+ event_sent = await _run_with_retryable_core_delivery_backoff(
154
+ lambda: event_client.send_event(
155
+ event_type=event_type,
156
+ integration_id=device.get("integration_id"),
157
+ config_id=device.get("config_id"),
158
+ auth_context=auth_context,
159
+ container_id=device.get("container_id"),
160
+ device_id=device.get("device_id"),
161
+ payload=payload,
162
+ source=source,
163
+ severity=severity,
164
+ )
132
165
  )
133
166
  if not event_sent and on_skipped is not None:
134
167
  on_skipped(
@@ -15,6 +15,7 @@ def _build_runtime_snapshot(runtime_context: RuntimeContext) -> dict[str, Any]:
15
15
  "core_auth_present": bool(runtime_context.auth.internal_token),
16
16
  "core_http_client_bound": process_state.core_http_client is not None,
17
17
  "current_generation": process_state.current_generation,
18
+ "config_generation": process_state.current_generation,
18
19
  "pending_task_count": len(
19
20
  [task for task in process_state.background_tasks if not task.done()]
20
21
  ),
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ """Helpers for keeping runtime config and device identity explicit."""
4
+
5
+ from typing import Any, Mapping
6
+
7
+
8
+ def _coerce_optional_string(value: Any) -> str | None:
9
+ if value is None:
10
+ return None
11
+ text = str(value).strip()
12
+ return text or None
13
+
14
+
15
+ def _first_present(payload: Mapping[str, Any], *keys: str) -> str | None:
16
+ for key in keys:
17
+ resolved = _coerce_optional_string(payload.get(key))
18
+ if resolved:
19
+ return resolved
20
+ return None
21
+
22
+
23
+ def resolve_config_id(payload: Mapping[str, Any] | Any) -> str:
24
+ """Resolve the Core-owned config id from a config-like payload."""
25
+
26
+ if isinstance(payload, Mapping):
27
+ return _first_present(payload, "config_id", "configId", "id") or "missing"
28
+ return (
29
+ _coerce_optional_string(getattr(payload, "config_id", None))
30
+ or _coerce_optional_string(getattr(payload, "configId", None))
31
+ or _coerce_optional_string(getattr(payload, "id", None))
32
+ or "missing"
33
+ )
34
+
35
+
36
+ def resolve_device_id(payload: Mapping[str, Any] | Any) -> str:
37
+ """Resolve the integration-owned device id from a config-like payload."""
38
+
39
+ if isinstance(payload, Mapping):
40
+ return _first_present(payload, "device_id", "deviceId", "id") or "missing"
41
+ return (
42
+ _coerce_optional_string(getattr(payload, "device_id", None))
43
+ or _coerce_optional_string(getattr(payload, "deviceId", None))
44
+ or _coerce_optional_string(getattr(payload, "id", None))
45
+ or "missing"
46
+ )
47
+
48
+
49
+ def build_runtime_identity(
50
+ payload: Mapping[str, Any] | Any,
51
+ *,
52
+ container_id: str | None = None,
53
+ integration_id: str | None = None,
54
+ ) -> dict[str, Any]:
55
+ """Build a canonical runtime identity record for one config-backed device."""
56
+
57
+ resolved = {
58
+ "config_id": resolve_config_id(payload),
59
+ "device_id": resolve_device_id(payload),
60
+ }
61
+ payload_container_id = (
62
+ _first_present(payload, "container_id", "containerId")
63
+ if isinstance(payload, Mapping)
64
+ else (
65
+ _coerce_optional_string(getattr(payload, "container_id", None))
66
+ or _coerce_optional_string(getattr(payload, "containerId", None))
67
+ )
68
+ )
69
+ payload_integration_id = (
70
+ _first_present(payload, "integration_id", "integrationId")
71
+ if isinstance(payload, Mapping)
72
+ else (
73
+ _coerce_optional_string(getattr(payload, "integration_id", None))
74
+ or _coerce_optional_string(getattr(payload, "integrationId", None))
75
+ )
76
+ )
77
+
78
+ resolved["container_id"] = _coerce_optional_string(container_id) or payload_container_id
79
+ resolved["integration_id"] = _coerce_optional_string(integration_id) or payload_integration_id
80
+ return resolved
@@ -50,18 +50,30 @@ class RuntimeRegistry(Generic[TEntry, TState, TEvent]):
50
50
  first_entry_id = next(iter(self.entries))
51
51
  return self.entries[first_entry_id]
52
52
 
53
- def update_state(self, entry_id: str, state: dict[str, Any]) -> dict[str, Any]:
53
+ def update_state(
54
+ self,
55
+ entry_id: str,
56
+ state: dict[str, Any],
57
+ *,
58
+ device_id: str | None = None,
59
+ ) -> dict[str, Any]:
54
60
  """Store the latest state snapshot and mirror it onto the active entry."""
55
61
  timestamp = dt.datetime.now(dt.timezone.utc).isoformat()
62
+ entry = self.entries.get(entry_id)
63
+ resolved_device_id = (
64
+ device_id
65
+ or (str(entry.get("device_id")) if entry and entry.get("device_id") else None)
66
+ or entry_id
67
+ )
56
68
  latest_state = {
57
- "device_id": entry_id,
69
+ "device_id": resolved_device_id,
58
70
  "state": state,
59
71
  "last_updated": timestamp,
60
72
  }
61
73
  self.state_snapshots[entry_id] = latest_state
62
- if entry_id in self.entries:
63
- self.entries[entry_id]["latest_state"] = state
64
- self.entries[entry_id]["last_updated"] = timestamp
74
+ if entry is not None:
75
+ entry["latest_state"] = state
76
+ entry["last_updated"] = timestamp
65
77
  return latest_state
66
78
 
67
79
  def append_event(self, event: TEvent) -> TEvent:
@@ -11,18 +11,58 @@ from pydantic import AliasChoices, BaseModel, ConfigDict, Field
11
11
 
12
12
  class RuntimeConfig(BaseModel):
13
13
  """Base runtime config model with a required config id and extensible fields."""
14
+
15
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
16
+
14
17
  id: str
15
- model_config = ConfigDict(extra="allow")
18
+ config_id: str | None = Field(
19
+ default=None,
20
+ validation_alias=AliasChoices("config_id", "configId"),
21
+ )
22
+ container_id: str | None = Field(
23
+ default=None,
24
+ validation_alias=AliasChoices("container_id", "containerId"),
25
+ )
26
+ integration_id: str | None = Field(
27
+ default=None,
28
+ validation_alias=AliasChoices("integration_id", "integrationId"),
29
+ )
30
+ device_id: str | None = Field(
31
+ default=None,
32
+ validation_alias=AliasChoices("device_id", "deviceId"),
33
+ )
16
34
 
17
35
 
18
36
  class RuntimeConfigSnapshot(BaseModel):
19
37
  """Snapshot payload sent from PiPhi Core to an integration runtime."""
38
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
39
+
40
+ schema_version: int | None = Field(
41
+ default=None,
42
+ validation_alias=AliasChoices("schema_version", "schemaVersion"),
43
+ )
20
44
  container_id: str
21
45
  integration_id: str | None = None
22
46
  driver_pid: int | None = None
23
47
  reason: str | None = None
24
48
  generation: int | None = None
49
+ updated_at: str | None = Field(
50
+ default=None,
51
+ validation_alias=AliasChoices("updated_at", "updatedAt"),
52
+ )
25
53
  configs: list[RuntimeConfig] = Field(default_factory=list)
54
+ deleted_config_ids: list[str] = Field(
55
+ default_factory=list,
56
+ validation_alias=AliasChoices("deleted_config_ids", "deletedConfigIds"),
57
+ )
58
+ config_hash: str | None = Field(
59
+ default=None,
60
+ validation_alias=AliasChoices("config_hash", "configHash"),
61
+ )
62
+ internal_token: str | None = Field(
63
+ default=None,
64
+ validation_alias=AliasChoices("internal_token", "internalToken"),
65
+ )
26
66
 
27
67
 
28
68
  class RuntimeConfigSyncResponse(BaseModel):
@@ -94,6 +134,44 @@ class RuntimeEntityDashboardResponse(BaseModel):
94
134
  metadata: dict[str, Any] = Field(default_factory=dict)
95
135
 
96
136
 
137
+ class RuntimeEntityCommandFieldResponse(BaseModel):
138
+ """Optional per-command argument metadata returned by a runtime entity."""
139
+
140
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
141
+
142
+ type: str
143
+ label: str | None = None
144
+ description: str | None = None
145
+ required: bool = False
146
+ default: Any | None = None
147
+ minimum: float | None = Field(
148
+ default=None,
149
+ validation_alias=AliasChoices("minimum", "min"),
150
+ )
151
+ maximum: float | None = Field(
152
+ default=None,
153
+ validation_alias=AliasChoices("maximum", "max"),
154
+ )
155
+ step: float | None = None
156
+ unit: str | None = None
157
+ options: list[Any] = Field(default_factory=list)
158
+
159
+
160
+ class RuntimeEntityCommandResponse(BaseModel):
161
+ """One device-scoped command exposed by a runtime entity."""
162
+
163
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
164
+
165
+ id: str
166
+ label: str | None = None
167
+ description: str | None = None
168
+ kind: str | None = None
169
+ args_schema: dict[str, RuntimeEntityCommandFieldResponse] = Field(
170
+ default_factory=dict,
171
+ validation_alias=AliasChoices("args_schema", "argsSchema"),
172
+ )
173
+
174
+
97
175
  class RuntimeEntityResponse(BaseModel):
98
176
  """One live runtime entity tied to a configured device/account."""
99
177
 
@@ -122,6 +200,15 @@ class RuntimeEntityResponse(BaseModel):
122
200
  default=None,
123
201
  validation_alias=AliasChoices("entity_type", "entityType"),
124
202
  )
203
+ available_commands: list[RuntimeEntityCommandResponse | str] = Field(
204
+ default_factory=list,
205
+ validation_alias=AliasChoices(
206
+ "available_commands",
207
+ "availableCommands",
208
+ "supported_commands",
209
+ "supportedCommands",
210
+ ),
211
+ )
125
212
  dashboard: RuntimeEntityDashboardResponse | None = None
126
213
  metadata: dict[str, Any] = Field(default_factory=dict)
127
214
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: piphi-runtime-kit-python
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: PiPhi Network runtime integration helpers
5
5
  Keywords: piphi,runtime,integration,iot
6
6
  Author-Email: KelvinSan <support@piphi.network>
@@ -362,15 +362,15 @@ async def health():
362
362
  async def config(payload: DemoConfig, request: Request):
363
363
  sync_runtime_auth_from_fastapi_payload(starter.runtime, request, payload)
364
364
  typed_payload = validate_typed_config(payload, DemoConfig)
365
+ identity = build_runtime_identity(typed_payload)
365
366
  starter.registry.set(
366
367
  typed_payload.id,
367
368
  {
368
- "config_id": typed_payload.config_id or typed_payload.id,
369
- "device_id": typed_payload.device_id or typed_payload.id,
369
+ **identity,
370
370
  "host": typed_payload.host,
371
371
  },
372
372
  )
373
- return build_config_apply_response(config_id=typed_payload.config_id or typed_payload.id)
373
+ return build_config_apply_response(config_id=identity["config_id"])
374
374
 
375
375
 
376
376
  @app.post("/telemetry/example")
@@ -1,27 +1,28 @@
1
- piphi_runtime_kit_python-0.4.1.dist-info/METADATA,sha256=ug1yodvcOA6KxVr4DkKDbOvp_s7GI2GF1tFtnVdVhPw,30934
2
- piphi_runtime_kit_python-0.4.1.dist-info/WHEEL,sha256=Z36eTX6lG3PITRleSd5hAZHCcz52yg3c0JQVxKBbLW0,90
3
- piphi_runtime_kit_python-0.4.1.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- piphi_runtime_kit_python-0.4.1.dist-info/licenses/LICENSE,sha256=pS6szQI4X7qAin1ODdoDp-XwZvsFy6ZZ28-JAwGHgzo,1070
5
- piphi_runtime_kit_python/__init__.py,sha256=_2HtSbXo2VN9267D-JFDBvX0Xvtzhhg4Y1d8KQ53W0Q,5692
1
+ piphi_runtime_kit_python-0.4.3.dist-info/METADATA,sha256=E79yjhZ5noVak87XO3vqqmXAPPcpwnnHCQ6ARARjN_U,30849
2
+ piphi_runtime_kit_python-0.4.3.dist-info/WHEEL,sha256=Z36eTX6lG3PITRleSd5hAZHCcz52yg3c0JQVxKBbLW0,90
3
+ piphi_runtime_kit_python-0.4.3.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ piphi_runtime_kit_python-0.4.3.dist-info/licenses/LICENSE,sha256=pS6szQI4X7qAin1ODdoDp-XwZvsFy6ZZ28-JAwGHgzo,1070
5
+ piphi_runtime_kit_python/__init__.py,sha256=PYHFtcA1h8JY3HaOAoPfTX4u9jgiYrXV25NStwQaVKk,6548
6
6
  piphi_runtime_kit_python/adapters/__init__.py,sha256=3jBhPZSBN3Qo7MKHlxAol2jPU3cK23BXHR2oTlircsw,361
7
7
  piphi_runtime_kit_python/adapters/fastapi.py,sha256=1QcOBYprg6AmQVrkI8g61NIelBB_7BTbdvNaMvG2-s8,2098
8
8
  piphi_runtime_kit_python/fastapi.py,sha256=OZ-ds2-g32kB4YkF90D5bM4vVVjpOWPtrNC-WdhDNyo,361
9
9
  piphi_runtime_kit_python/runtime/__init__.py,sha256=0WxNbgGodqba-uiJILOAuU3EwZhBtXeDyw6sYhduWzg,3717
10
10
  piphi_runtime_kit_python/runtime/auth.py,sha256=hL90c3K5pJgEpPdNGJ7ds6mqeN4tmjC3I8igfNam_L4,4648
11
- piphi_runtime_kit_python/runtime/config_sync.py,sha256=IzsH5AGxnISISWx8OYfNMTk2O20kbW4fBvBEGwO_cYg,5625
11
+ piphi_runtime_kit_python/runtime/config_sync.py,sha256=ellgkNQmEGLVFH81VdvxpleQdG_bEmmRdpBkmykdbA4,8078
12
12
  piphi_runtime_kit_python/runtime/configuration.py,sha256=4kkoJeENOhJHeNvC9QDu81ejDJUQJG7S5GmQG1sv3YQ,3631
13
13
  piphi_runtime_kit_python/runtime/context.py,sha256=CwXgrivciBd7eTREn0u0zjqkDPy6VthASW-gsoZqCTg,1109
14
14
  piphi_runtime_kit_python/runtime/discovery.py,sha256=uH6rJ4BnLVukTCxu2QwvN8yJhsI_fcT29VEat62oBcY,2274
15
- piphi_runtime_kit_python/runtime/dispatch.py,sha256=N0Fxqd9QLkareAOX4aOGBBzQpPU3-v5vDxbWEill0zI,6218
15
+ piphi_runtime_kit_python/runtime/dispatch.py,sha256=QcbA9B5izyyg0znlKHq94dbr-oqHVxKW2LCjrQKYfkY,7341
16
16
  piphi_runtime_kit_python/runtime/errors.py,sha256=_mjqmJLsdM5NfrTwb6YFkhheqPfn6Df-QtAKgl9U6QU,4120
17
17
  piphi_runtime_kit_python/runtime/events.py,sha256=JcvrTZlJ-vgY237jTDVOAVTbSxbcuh9u2QCdOGyuduA,6543
18
- piphi_runtime_kit_python/runtime/health.py,sha256=9YO0K_UCJKTe6uSFLgb1kStr5dAgtIUc8Ha0dnRi2vA,1880
18
+ piphi_runtime_kit_python/runtime/health.py,sha256=3pds8SYSUql2w2ijegbszwm0HK69VzfIrwz42bN97yk,1943
19
+ piphi_runtime_kit_python/runtime/identity.py,sha256=cth0_QHR9R5LTdp-6DPD1IhM8cYJT5HB1OUQYW7kLGs,2762
19
20
  piphi_runtime_kit_python/runtime/lifespan.py,sha256=mVSdZZAS66xlcnlzAwkztFjzw_l2pyLrJqULJsb0Ezk,2850
20
21
  piphi_runtime_kit_python/runtime/mqtt.py,sha256=JKMN5zxg1BcOidu7TaYnuRD3HuoAJ_WcvXMTTSlAteo,6069
21
- piphi_runtime_kit_python/runtime/registry.py,sha256=AOpd5NgwSAhN8Us8nLW6084GrMtitMMaQP916EpMZrk,3103
22
+ piphi_runtime_kit_python/runtime/registry.py,sha256=8rKLCWt1_0SlDm3TepEUGDliOhVEDTv_zYR0kxLpSHM,3373
22
23
  piphi_runtime_kit_python/runtime/starter.py,sha256=j5C4GOOcRHNLJ-HYu8fmf3bE00baDOzAfuq1MefT-Jg,4651
23
24
  piphi_runtime_kit_python/runtime/state.py,sha256=4xvQGTT59DF9rXHAtM3kDyV_UGg2a9tLPvGiCp_PFMc,674
24
25
  piphi_runtime_kit_python/runtime/tasks.py,sha256=gMd-2K96jMU8tCPvI12xdWxuyNt5gsLDss2QWOoM94k,1241
25
26
  piphi_runtime_kit_python/runtime/telemetry.py,sha256=RN6SFrhmAZ7aJLyioQMBzJEFsxQgOjjA1pV5qNeaR4E,4279
26
- piphi_runtime_kit_python/schemas.py,sha256=weCqTPFafourOVe1Me4282hezJ9jwttDtKLBHrEqlnU,7069
27
- piphi_runtime_kit_python-0.4.1.dist-info/RECORD,,
27
+ piphi_runtime_kit_python/schemas.py,sha256=9jlsfmvz-O2YeoB0SVopXY7NhFlb1zgynez0NXEZkVE,9849
28
+ piphi_runtime_kit_python-0.4.3.dist-info/RECORD,,