From b7d8332cb0a4b745561aa8b63426677afc4fde45 Mon Sep 17 00:00:00 2001 From: RockChinQ Date: Sat, 13 Jun 2026 11:13:18 -0400 Subject: [PATCH] feat(telemetry): include instance_create_ts in heartbeat payload Load the instance creation timestamp from data/labels/instance_id.json (backfilling+persisting it for instances created before the field existed), expose it as constants.instance_create_ts, and include it in the heartbeat payload so Space can anchor Time-To-Value / onboarding analytics on real install time rather than first-heartbeat. Verified: py_compile, ruff, pytest tests/unit_tests/telemetry/ (37 passed). --- src/langbot/pkg/core/stages/load_config.py | 10 ++++++++++ src/langbot/pkg/telemetry/heartbeat.py | 1 + src/langbot/pkg/utils/constants.py | 8 ++++++++ tests/unit_tests/telemetry/test_heartbeat.py | 1 + 4 files changed, 20 insertions(+) diff --git a/src/langbot/pkg/core/stages/load_config.py b/src/langbot/pkg/core/stages/load_config.py index 26f4a9e1..6fc890f1 100644 --- a/src/langbot/pkg/core/stages/load_config.py +++ b/src/langbot/pkg/core/stages/load_config.py @@ -202,6 +202,16 @@ class LoadConfigStage(stage.BootingStage): constants.instance_id = new_id constants.edition = ap.instance_config.data.get('system', {}).get('edition', 'community') + # Instance creation timestamp: sourced from data/labels/instance_id.json. + # Instances created before this field existed (or supplied via + # system.instance_id) won't have it, so backfill with the current time + # and persist it via the dump below — from then on it stays stable. + instance_create_ts = ap.instance_id.data.get('instance_create_ts', 0) + if not isinstance(instance_create_ts, int) or instance_create_ts <= 0: + instance_create_ts = int(time.time()) + ap.instance_id.data['instance_create_ts'] = instance_create_ts + constants.instance_create_ts = instance_create_ts + print(f'LangBot instance id: {constants.instance_id}') print(f'LangBot edition: {constants.edition}') diff --git a/src/langbot/pkg/telemetry/heartbeat.py b/src/langbot/pkg/telemetry/heartbeat.py index dd2a58d3..34b61673 100644 --- a/src/langbot/pkg/telemetry/heartbeat.py +++ b/src/langbot/pkg/telemetry/heartbeat.py @@ -109,6 +109,7 @@ async def build_heartbeat_payload(ap: core_app.Application) -> dict: 'query_id': '', 'version': constants.semantic_version, 'instance_id': constants.instance_id, + 'instance_create_ts': constants.instance_create_ts, 'edition': constants.edition, 'features': features, 'timestamp': datetime.now(timezone.utc).isoformat(), diff --git a/src/langbot/pkg/utils/constants.py b/src/langbot/pkg/utils/constants.py index fb520da9..2af16486 100644 --- a/src/langbot/pkg/utils/constants.py +++ b/src/langbot/pkg/utils/constants.py @@ -16,3 +16,11 @@ debug_mode = False edition = 'community' instance_id = '' + +instance_create_ts = 0 +"""Unix timestamp (seconds) of when this instance was first created. + +Sourced from ``data/labels/instance_id.json``. Backfilled to the current +time for instances created before this field existed, so it is always a +positive value once load_config has run. +""" diff --git a/tests/unit_tests/telemetry/test_heartbeat.py b/tests/unit_tests/telemetry/test_heartbeat.py index a5bd2e32..18d61f2d 100644 --- a/tests/unit_tests/telemetry/test_heartbeat.py +++ b/tests/unit_tests/telemetry/test_heartbeat.py @@ -62,6 +62,7 @@ class TestBuildHeartbeatPayload: assert payload['event_type'] == 'instance_heartbeat' assert payload['query_id'] == '' + assert 'instance_create_ts' in payload assert 'timestamp' in payload f = payload['features'] assert f['database'] == 'postgresql'