Skip to content

feat: report reflex subpackage versions in telemetry#6610

Merged
masenf merged 3 commits into
mainfrom
claude/intelligent-volta-OCFlQ
Jun 5, 2026
Merged

feat: report reflex subpackage versions in telemetry#6610
masenf merged 3 commits into
mainfrom
claude/intelligent-volta-OCFlQ

Conversation

@masenf

@masenf masenf commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

What

Adds a reflex_package_version field to anonymous telemetry: a dict mapping each first-party Reflex subpackage to its installed version, e.g.

{
  "reflex-base": "0.9.4",
  "reflex-components-core": "0.9.0",
  "reflex-components-radix": "0.9.2",
  "reflex-hosting-cli": "0.1.66"
}

Why

Now that Reflex is split across many independently-versioned workspace packages (reflex-base, the reflex-components-* family, reflex-hosting-cli), the single reflex_version field no longer describes the full install. The existing reflex_version and reflex_enterprise_version fields are unchanged.

How

get_reflex_package_versions() derives the reported set from the reflex distribution's own declared dependencies (importlib.metadata.requires("reflex")), then looks up each one's installed version:

  • Only the first-party subpackages distributed from this repository are reported — the set is reflex's own reflex-* dependencies, so an unrelated third-party reflex-* package a user happens to have installed is never reported.
  • The main reflex package stays in reflex_version; reflex-enterprise (separate repo) stays in reflex_enterprise_version and is not duplicated here.
  • A declared subpackage that isn't installed is skipped; if reflex's metadata is unavailable, the field is an empty dict.
  • Result is sorted for stable output.

Testing

  • test_get_reflex_package_versions_real_environment — asserts the first-party subpackages are discovered with string versions.
  • test_get_reflex_package_versions_reports_only_first_party — asserts third-party reflex-* packages and non-reflex deps are excluded, and uninstalled declared deps are skipped.
  • test_get_reflex_package_versions_handles_missing_reflex_metadata — covers the missing-metadata fallback.

All tests/units/test_telemetry.py pass; ruff and pyright are clean.

https://claude.ai/code/session_016xJwZ48c1napKaCCkyARtC


Generated by Claude Code

Now that reflex is split across many independently-versioned packages,
the single reflex_version field no longer describes the full install.
Add a reflex_package_version telemetry property mapping each installed
reflex-* subpackage (reflex-base, the reflex-components-* family,
reflex-hosting-cli, etc.) to its version, discovered dynamically from
installed distribution metadata.

https://claude.ai/code/session_016xJwZ48c1napKaCCkyARtC
@masenf masenf requested a review from a team as a code owner June 4, 2026 22:44
@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a reflex_package_version field to the anonymous telemetry payload: a dict mapping every installed reflex-* distribution to its version, enabling version tracking across the now-split Reflex workspace packages. The existing reflex_version and reflex_enterprise_version fields are unchanged.

  • get_reflex_package_versions() iterates importlib.metadata.distributions(), canonicalizes names via packaging.utils.canonicalize_name, de-duplicates with setdefault (first-seen wins), and returns a sorted dict — correctly excluding the top-level reflex package.
  • The result is computed inside _get_event_defaults(), which is wrapped by the @once_unless_none-cached get_event_defaults(), so the filesystem scan happens at most once per process.
  • Two new tests cover both the real environment (asserts reflex-base is present, all values are strings) and a fully mocked scenario (filtering, canonicalization, de-duplication).

Confidence Score: 5/5

Safe to merge. The change is additive — it appends a new field to the telemetry payload without touching any existing fields or logic.

The new function is well-guarded (None-name check, canonicalization, de-duplication) and only runs once thanks to the existing @once_unless_none cache. Tests cover both a real-environment smoke check and a thorough mocked scenario. No existing behavior is altered.

No files require special attention.

Important Files Changed

Filename Overview
reflex/utils/telemetry.py Adds get_reflex_package_versions() using importlib.metadata.distributions() with canonicalization and de-duplication; result wired into _get_event_defaults() and typed via _Properties. Logic is correct and the @once_unless_none cache on get_event_defaults prevents repeated filesystem scans.
tests/units/test_telemetry.py Adds a real-environment integration test and a fully-mocked unit test covering filtering, canonicalization, and de-duplication; event_defaults fixture updated to include the new field. Coverage is thorough.

Reviews (1): Last reviewed commit: "docs: add news fragment for reflex_packa..." | Re-trigger Greptile

@masenf masenf left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can't just report any package version that is prefixed with reflex-, those could be third party packages and that's invasive... only report on subpackages that we distribute from this repository

@codspeed-hq

codspeed-hq Bot commented Jun 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 28 untouched benchmarks


Comparing claude/intelligent-volta-OCFlQ (942ce5d) with main (2585d80)

Open in CodSpeed

Derive the reported subpackages from reflex's own declared dependencies
(importlib.metadata.requires) instead of scanning every installed
reflex-* distribution. This reports only the subpackages distributed
from this repository (reflex-base, the reflex-components-* family,
reflex-hosting-cli) and never an unrelated third-party reflex-* package
the user happens to have installed.

https://claude.ai/code/session_016xJwZ48c1napKaCCkyARtC

@masenf masenf left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@masenf masenf merged commit 00fdeaf into main Jun 5, 2026
106 checks passed
@masenf masenf deleted the claude/intelligent-volta-OCFlQ branch June 5, 2026 18:53
masenf pushed a commit that referenced this pull request Jun 5, 2026
The "Update branch" merge of main combined `import importlib.metadata` (#6610)
with this branch's `import uuid` but left a blank line splitting the stdlib
import group, which ruff's isort rejected in CI pre-commit. Remove it.

https://claude.ai/code/session_0162Wc1GmkskbgCRs7fjg9Cy
masenf added a commit that referenced this pull request Jun 5, 2026
…6611)

* fix(telemetry): send distinct ids as UUID strings to preserve uniqueness

distinct_id and distinct_app_id were reported as 128-bit integers. PostHog
coerces large JSON numbers to float64, discarding all but ~16 significant
digits, so two distinct users or apps could collapse onto the same truncated
value and have their events incorrectly correlated.

Encode both identifiers as canonical UUID hex strings before sending. A UUID
holds the same 128 bits, so str(UUID(int=existing_id)) is a lossless
re-encoding: the value is unchanged, only its wire form differs. Existing
installs derive their UUID from the stored integer (never regenerated), so
post-migration events stay linkable to their pre-migration history. New
installs now generate a real uuid4, persisted as its integer form to keep the
installation_id and reflex.json files readable by older Reflex versions.

https://claude.ai/code/session_0162Wc1GmkskbgCRs7fjg9Cy

* feat(telemetry): alias legacy numeric distinct_id to its UUID in PostHog

Re-encoding distinct_id as a UUID string makes PostHog treat the new UUID
identity and the old (float-truncated) numeric identity as separate persons,
breaking continuity with pre-migration events.

On the first telemetry send of a process, emit a one-time PostHog
$create_alias event linking the new UUID distinct_id to the legacy numeric id.
The legacy id is sent as a JSON number so PostHog coerces it to the same lossy
float as the historic events, merging the two persons. The attempt is
best-effort and runs exactly once: a flag in reflex.json records that it ran
(set even when the alias does not match, since the lossy legacy id may not),
and it is written with a merging update so it survives Reflex downgrades and
upgrades. Brand-new UUID-native projects preset the flag during init since
they have no legacy numeric telemetry to alias.

https://claude.ai/code/session_0162Wc1GmkskbgCRs7fjg9Cy

* refactor(telemetry): store distinct_id alias marker per-machine, not per-app

The alias links the per-machine installation distinct_id, so gating it on a
per-app reflex.json flag was the wrong scope. Replace that flag with a marker
file next to the installation id in the Reflex dir, recording that the install
uses v0.9.5 UUID distinct_id semantics.

- New installs write the marker when the id is first generated
  (ensure_reflex_installation_id), so they never attempt a pointless alias.
- Legacy installs (id present, marker absent) attempt the one-time
  $create_alias, then write the marker regardless of outcome so it is not
  retried on every run.

The marker lives in the per-user Reflex dir, which no Reflex version clears,
so it persists across downgrades and upgrades. reflex.json is no longer
touched for telemetry.

https://claude.ai/code/session_0162Wc1GmkskbgCRs7fjg9Cy

* chore: add changelog news fragment for telemetry UUID distinct_id PR

https://claude.ai/code/session_0162Wc1GmkskbgCRs7fjg9Cy

* style: drop stray blank line in test_telemetry imports after main merge

The "Update branch" merge of main combined `import importlib.metadata` (#6610)
with this branch's `import uuid` but left a blank line splitting the stdlib
import group, which ruff's isort rejected in CI pre-commit. Remove it.

https://claude.ai/code/session_0162Wc1GmkskbgCRs7fjg9Cy

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants