fix(telemetry): stitch PostHog identities on MemoryClient init
The Python sync/async MemoryClient and the TS MemoryClient were silently switching their PostHog distinct_id from an anonymous on-disk UUID to the user's email post-ping, without sending the merge instruction. PostHog ended up tracking each user as 2-3 disconnected personas (OSS UUID, CLI anon UUID, email), and only ~4 $create_alias events fired across ~300K users. The Python CLI does this correctly today; this change ports the same pattern to MemoryClient. - mem0/memory/setup.py: make setup_config() idempotent so it backfills a top-level user_id for CLI-first users (the CLI writes telemetry.anonymous_id, not user_id, which left OSS telemetry silently dropped). Add read_anon_ids() and mark_aliased() helpers. - mem0/memory/telemetry.py: add AnonymousTelemetry.capture_identify() that posts a PostHog $identify event with $anon_distinct_id. Add "$identify" to _LIFECYCLE_EVENTS so it bypasses the 90% sampling drop. - mem0/client/main.py: at the end of __init__ for both MemoryClient and AsyncMemoryClient, fire $identify for each unaliased anon ID and persist telemetry.aliased_to so the alias only happens once per (anon_id, email) pair, ever. All telemetry calls swallow exceptions. - mem0-ts/src/client/telemetry.ts: mirror with captureIdentify(). - mem0-ts/src/client/config.ts (new): node-only readMem0AnonIds() and markMem0Aliased() using async dynamic imports of fs/path/os, gated on process.versions.node so browsers no-op cleanly. - mem0-ts/src/client/mem0.ts: call _maybeAliasAnonToEmail() inside _initializeClient after ping resolves the email. Tests cover the matrix (OSS-only, CLI-only, both, already-aliased) plus failure modes (malformed JSON, read-only fs, posthog errors, browser env). Telemetry never raises.
This commit is contained in:
@@ -67,6 +67,15 @@ class UnifiedTelemetry implements TelemetryClient {
|
||||
}
|
||||
}
|
||||
|
||||
async captureIdentify(anonId: string, email: string) {
|
||||
if (!MEM0_TELEMETRY) return;
|
||||
if (!anonId || !email || anonId === email) return;
|
||||
await this.captureEvent(email, "$identify", {
|
||||
$anon_distinct_id: anonId,
|
||||
client_source: "typescript",
|
||||
});
|
||||
}
|
||||
|
||||
async shutdown() {
|
||||
// No shutdown needed for direct API calls
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user