Skip to content

Commit 334d4eb

Browse files
committed
fix: capture enriches with local eval when enabled
Captured events now use local evaluation results when `send_feature_flags` is `True` and local evaluation is enabled.
1 parent c253e41 commit 334d4eb

File tree

2 files changed

+113
-2
lines changed

2 files changed

+113
-2
lines changed

posthog/client.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,28 @@ def capture(
622622
if flag_options["should_send"]:
623623
try:
624624
if flag_options["only_evaluate_locally"] is True:
625-
# Only use local evaluation
625+
# Local evaluation explicitly requested
626+
feature_variants = self.get_all_flags(
627+
distinct_id,
628+
groups=(groups or {}),
629+
person_properties=flag_options["person_properties"],
630+
group_properties=flag_options["group_properties"],
631+
disable_geoip=disable_geoip,
632+
only_evaluate_locally=True,
633+
flag_keys_to_evaluate=flag_options["flag_keys_filter"],
634+
)
635+
elif flag_options["only_evaluate_locally"] is False:
636+
# Remote evaluation explicitly requested
637+
feature_variants = self.get_feature_variants(
638+
distinct_id,
639+
groups,
640+
person_properties=flag_options["person_properties"],
641+
group_properties=flag_options["group_properties"],
642+
disable_geoip=disable_geoip,
643+
flag_keys_to_evaluate=flag_options["flag_keys_filter"],
644+
)
645+
elif self.feature_flags:
646+
# Local flags available, prefer local evaluation
626647
feature_variants = self.get_all_flags(
627648
distinct_id,
628649
groups=(groups or {}),
@@ -633,7 +654,7 @@ def capture(
633654
flag_keys_to_evaluate=flag_options["flag_keys_filter"],
634655
)
635656
else:
636-
# Default behavior - use remote evaluation
657+
# Fall back to remote evaluation
637658
feature_variants = self.get_feature_variants(
638659
distinct_id,
639660
groups,

posthog/test/test_client.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,96 @@ def test_basic_capture_with_feature_flags_switched_off_doesnt_send_them(
752752

753753
self.assertEqual(patch_flags.call_count, 0)
754754

755+
@mock.patch("posthog.client.flags")
756+
def test_capture_with_send_feature_flags_true_and_local_evaluation_uses_local_flags(
757+
self, patch_flags
758+
):
759+
"""Test that send_feature_flags=True with local evaluation enabled uses local flags without API call"""
760+
patch_flags.return_value = {"featureFlags": {"remote-flag": "remote-variant"}}
761+
762+
multivariate_flag = {
763+
"id": 1,
764+
"name": "Beta Feature",
765+
"key": "beta-feature-local",
766+
"active": True,
767+
"rollout_percentage": 100,
768+
"filters": {
769+
"groups": [
770+
{
771+
"rollout_percentage": 100,
772+
},
773+
],
774+
"multivariate": {
775+
"variants": [
776+
{
777+
"key": "first-variant",
778+
"name": "First Variant",
779+
"rollout_percentage": 50,
780+
},
781+
{
782+
"key": "second-variant",
783+
"name": "Second Variant",
784+
"rollout_percentage": 50,
785+
},
786+
]
787+
},
788+
},
789+
}
790+
simple_flag = {
791+
"id": 2,
792+
"name": "Simple Flag",
793+
"key": "simple-flag",
794+
"active": True,
795+
"filters": {
796+
"groups": [
797+
{
798+
"rollout_percentage": 100,
799+
}
800+
],
801+
},
802+
}
803+
804+
with mock.patch("posthog.client.batch_post") as mock_post:
805+
client = Client(
806+
FAKE_TEST_API_KEY,
807+
on_error=self.set_fail,
808+
personal_api_key=FAKE_TEST_API_KEY,
809+
sync_mode=True,
810+
)
811+
client.feature_flags = [multivariate_flag, simple_flag]
812+
813+
msg_uuid = client.capture(
814+
"python test event",
815+
distinct_id="distinct_id",
816+
send_feature_flags=True,
817+
)
818+
self.assertIsNotNone(msg_uuid)
819+
self.assertFalse(self.failed)
820+
821+
# Get the enqueued message from the mock
822+
mock_post.assert_called_once()
823+
batch_data = mock_post.call_args[1]["batch"]
824+
msg = batch_data[0]
825+
826+
self.assertEqual(msg["event"], "python test event")
827+
self.assertEqual(msg["distinct_id"], "distinct_id")
828+
829+
# Verify local flags are included in the event
830+
self.assertIn("$feature/beta-feature-local", msg["properties"])
831+
self.assertIn("$feature/simple-flag", msg["properties"])
832+
self.assertEqual(msg["properties"]["$feature/simple-flag"], True)
833+
834+
# Verify active feature flags are set correctly
835+
active_flags = msg["properties"]["$active_feature_flags"]
836+
self.assertIn("beta-feature-local", active_flags)
837+
self.assertIn("simple-flag", active_flags)
838+
839+
# The remote flag should NOT be included since we used local evaluation
840+
self.assertNotIn("$feature/remote-flag", msg["properties"])
841+
842+
# CRITICAL: Verify the /flags API was NOT called
843+
self.assertEqual(patch_flags.call_count, 0)
844+
755845
@mock.patch("posthog.client.flags")
756846
def test_capture_with_send_feature_flags_options_only_evaluate_locally_true(
757847
self, patch_flags

0 commit comments

Comments
 (0)