piphi-runtime-kit-python 0.4.2__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,
@@ -109,6 +120,10 @@ __all__ = [
109
120
  "CoreUnavailableError",
110
121
  "CoreUnexpectedResponseError",
111
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",
112
127
  "DEFAULT_RUNTIME_CONTAINER_ID_ENV_NAME",
113
128
  "DEFAULT_RUNTIME_INTERNAL_TOKEN_ENV_NAME",
114
129
  "RUNTIME_CONTAINER_ID_HEADER_NAME",
@@ -169,6 +184,7 @@ __all__ = [
169
184
  "format_discovery_attempt_log",
170
185
  "format_event_log",
171
186
  "format_runtime_auth_sync_log",
187
+ "load_runtime_config_snapshot",
172
188
  "mask_token",
173
189
  "MqttBrokerConfig",
174
190
  "MqttJsonClient",
@@ -176,6 +192,8 @@ __all__ = [
176
192
  "normalize_event_payload",
177
193
  "redact_config_secrets",
178
194
  "reconcile_config_ids",
195
+ "resolve_core_base_url",
196
+ "resolve_runtime_config_snapshot_path",
179
197
  "runtime_lifespan",
180
198
  "schedule_event_delivery",
181
199
  "schedule_telemetry_delivery",
@@ -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
  *,
@@ -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
  ),
@@ -35,12 +35,34 @@ class RuntimeConfig(BaseModel):
35
35
 
36
36
  class RuntimeConfigSnapshot(BaseModel):
37
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
+ )
38
44
  container_id: str
39
45
  integration_id: str | None = None
40
46
  driver_pid: int | None = None
41
47
  reason: str | None = None
42
48
  generation: int | None = None
49
+ updated_at: str | None = Field(
50
+ default=None,
51
+ validation_alias=AliasChoices("updated_at", "updatedAt"),
52
+ )
43
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
+ )
44
66
 
45
67
 
46
68
  class RuntimeConfigSyncResponse(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: piphi-runtime-kit-python
3
- Version: 0.4.2
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>
@@ -1,21 +1,21 @@
1
- piphi_runtime_kit_python-0.4.2.dist-info/METADATA,sha256=th_AhsVfZ_at7LDyKQv7ZA1EVxiasOUwoSdTxQvdulI,30849
2
- piphi_runtime_kit_python-0.4.2.dist-info/WHEEL,sha256=Z36eTX6lG3PITRleSd5hAZHCcz52yg3c0JQVxKBbLW0,90
3
- piphi_runtime_kit_python-0.4.2.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- piphi_runtime_kit_python-0.4.2.dist-info/licenses/LICENSE,sha256=pS6szQI4X7qAin1ODdoDp-XwZvsFy6ZZ28-JAwGHgzo,1070
5
- piphi_runtime_kit_python/__init__.py,sha256=P8yx6C1P88xuXWuf3D58d5zhUD-pprhBhbVeyNbDaJQ,6013
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
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
19
  piphi_runtime_kit_python/runtime/identity.py,sha256=cth0_QHR9R5LTdp-6DPD1IhM8cYJT5HB1OUQYW7kLGs,2762
20
20
  piphi_runtime_kit_python/runtime/lifespan.py,sha256=mVSdZZAS66xlcnlzAwkztFjzw_l2pyLrJqULJsb0Ezk,2850
21
21
  piphi_runtime_kit_python/runtime/mqtt.py,sha256=JKMN5zxg1BcOidu7TaYnuRD3HuoAJ_WcvXMTTSlAteo,6069
@@ -24,5 +24,5 @@ piphi_runtime_kit_python/runtime/starter.py,sha256=j5C4GOOcRHNLJ-HYu8fmf3bE00baD
24
24
  piphi_runtime_kit_python/runtime/state.py,sha256=4xvQGTT59DF9rXHAtM3kDyV_UGg2a9tLPvGiCp_PFMc,674
25
25
  piphi_runtime_kit_python/runtime/tasks.py,sha256=gMd-2K96jMU8tCPvI12xdWxuyNt5gsLDss2QWOoM94k,1241
26
26
  piphi_runtime_kit_python/runtime/telemetry.py,sha256=RN6SFrhmAZ7aJLyioQMBzJEFsxQgOjjA1pV5qNeaR4E,4279
27
- piphi_runtime_kit_python/schemas.py,sha256=LGz53OHmWwUEQb_nHbVBgRDGVGowJPT53S2P7otP3lo,9073
28
- piphi_runtime_kit_python-0.4.2.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,,