Skip to content

Commit 790e4c0

Browse files
authored
Merge pull request #271 from launchdarkly-labs/cko_2025
Cko 2025
2 parents 7b67117 + 65c1f92 commit 790e4c0

File tree

5 files changed

+95
-36
lines changed

5 files changed

+95
-36
lines changed

Diff for: .github/workflows/demo_provisioning_scripts/DemoBuilder.py

+56-27
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ def build(self):
3939
self.update_add_userid_to_flags()
4040
self.setup_release_pipeline()
4141
self.create_ai_config()
42-
self.create_and_run_holdout()
43-
self.create_and_run_layer()
4442
self.create_and_run_experiments()
43+
self.create_and_run_layer()
44+
self.create_and_run_holdout()
4545
self.project_settings()
4646
self.setup_template_environment()
4747

@@ -144,7 +144,7 @@ def create_flags(self):
144144
self.flag_togglebank_api_guarded_release()
145145
print(" - B1 - Release: New Database (Guarded Release)")
146146
self.flag_database_guarded_release()
147-
print(" - B2 - Release: New API (Guarded Release)")
147+
print(" - B2 - Release: New API (Guarded Release)")
148148
self.flag_api_guarded_release()
149149
#print(" - C1 - Experiment: AI Models for Chatbot")
150150
#self.flag_exp_chatbot_ai_models()
@@ -156,6 +156,8 @@ def create_flags(self):
156156
self.flag_exp_new_search_engine()
157157
print(" - D4 - Funnel Experiment: New Shorten Collection Page")
158158
self.flag_exp_shorten_collections_page()
159+
print(" - Flag to Create Q4 Increase Total Price Holdout")
160+
self.flag_holdout_q4_increase_incart_price()
159161
print(" - E1 - Migration: Database (Migration Tool)")
160162
self.flag_database_migration()
161163

@@ -294,8 +296,7 @@ def run_ecommerce_collection_banner_funnel_experiment(self):
294296
)
295297
print(" - (Bayesian) Funnel Experiment: New Collection Promotion Banner")
296298
self.create_ecommerce_collection_banner_funnel_experiment()
297-
self.ldproject.start_exp_iteration("new-collection-promotion-banner", "production")
298-
print("Done")
299+
print("Done creating experiment")
299300
self.experiment_created = True
300301

301302
def create_ecommerce_collection_banner_funnel_experiment(self):
@@ -311,6 +312,7 @@ def create_ecommerce_collection_banner_funnel_experiment(self):
311312
"If we adjust the header text to better copy we can drive greater attention into the stores in question, and greater conversion of checkout activities.",
312313
metrics=metrics,
313314
primary_key="store-purchases",
315+
attributes=["device", "location", "tier", "operating_system"]
314316
)
315317

316318
def run_ecommerce_upsell_component_feature_experiment(self):
@@ -326,8 +328,6 @@ def run_ecommerce_upsell_component_feature_experiment(self):
326328
)
327329
print(" - (Bayesian) Feature Experiment: Suggested Items Carousel")
328330
self.create_ecommerce_upsell_component_feature_experiment()
329-
self.ldproject.start_exp_iteration("suggested-items-carousel", "production")
330-
print("Done")
331331
self.experiment_created = True
332332

333333
def create_ecommerce_upsell_component_feature_experiment(self):
@@ -343,8 +343,9 @@ def create_ecommerce_upsell_component_feature_experiment(self):
343343
"If we enable the new cart suggested items feature, we can drive greater upsell conversion.",
344344
metrics=metrics,
345345
primary_key="in-cart-total-items",
346+
attributes=["device", "location", "tier", "operating_system"]
346347
)
347-
#####
348+
348349
def run_ecommerce_shorten_collection_funnel_experiment(self):
349350
if not self.metric_groups_created:
350351
print("Error: Metric groups not created")
@@ -375,6 +376,9 @@ def create_ecommerce_shorten_collection_funnel_experiment(self):
375376
"We would want to reduce the collection page to the top three items to reduce customer decision fatigue in order to increase checkout and overall revenue.",
376377
metrics=metrics,
377378
primary_key="shorten-collection-page-metric-group",
379+
methodology="frequentist",
380+
analysisConfig={"significanceThreshold": "5", "testDirection": "two-sided"},
381+
attributes=["device", "location", "tier", "operating_system"]
378382
)
379383

380384
def run_ecommerce_new_search_engine_feature_experiment(self):
@@ -391,7 +395,6 @@ def run_ecommerce_new_search_engine_feature_experiment(self):
391395
print(" - (Frequentist) Feature Experiment: New Search Engine")
392396
self.create_ecommerce_new_search_engine_feature_experiment()
393397
self.ldproject.start_exp_iteration("new-search-engine", "production")
394-
print("Done")
395398
self.experiment_created = True
396399

397400
def create_ecommerce_new_search_engine_feature_experiment(self):
@@ -407,6 +410,9 @@ def create_ecommerce_new_search_engine_feature_experiment(self):
407410
hypothesis="We want to a new search engine that is more ranks search results diffrently and have an Add To Cart button built inside the component in order to increase ease of adding items to cart and increasing revenue.",
408411
metrics=metrics,
409412
primary_key="search-engine-add-to-cart",
413+
methodology="frequentist",
414+
analysisConfig={"significanceThreshold": "5", "testDirection": "two-sided"},
415+
attributes=["device", "location", "tier", "operating_system"]
410416
)
411417

412418
def run_togglebank_ai_config_experiment(self):
@@ -422,7 +428,6 @@ def run_togglebank_ai_config_experiment(self):
422428
)
423429
self.create_togglebank_ai_config_experiment()
424430
self.ldproject.start_exp_iteration("ai-config-experiment", "production")
425-
print("Done")
426431
self.experiment_created = True
427432

428433
def create_togglebank_ai_config_experiment(self):
@@ -438,6 +443,7 @@ def create_togglebank_ai_config_experiment(self):
438443
"Which AI Models are providing best experiences to customers and delivering best responses",
439444
metrics=metrics,
440445
primary_key="ai-chatbot-positive-feedback",
446+
attributes=["device", "location", "tier", "operating_system"]
441447
)
442448

443449
############################################################################################################
@@ -451,15 +457,15 @@ def create_togglebank_ai_config_experiment(self):
451457
# Create all the experiment holdouts
452458

453459
def create_and_run_holdout(self):
454-
print("Creating holdout: ")
460+
print(" - Creating holdout: ")
455461
self.run_q4_increase_incart_price_holdout()
456462

457463
def run_q4_increase_incart_price_holdout(self):
458464
metrics = [
459465
{
460466
"key": "in-cart-total-price",
461467
"isGroup": False,
462-
"primarySingleMetricKey": True
468+
"primary": True
463469
}
464470
]
465471
res = self.ldproject.create_holdout(
@@ -471,7 +477,7 @@ def run_q4_increase_incart_price_holdout(self):
471477
primary_metric_key= "in-cart-total-price",
472478
randomization_unit="users",
473479
attributes=["tier"],
474-
prerequisiteflagkey="release-new-search-engine"
480+
prerequisiteflagkey="q-4-increase-average-total-in-cart-price-ld-holdout"
475481
)
476482
############################################################################################################
477483

@@ -481,19 +487,24 @@ def run_q4_increase_incart_price_holdout(self):
481487
# Each layer is defined in its own function below
482488

483489
##################################################
484-
# Create all the experiment layers
490+
# Create all the experiment layers
485491

486492
def create_and_run_layer(self):
487-
print("Creating checkout_experiment layer: ")
493+
print(" - Creating checkout_experiment layer: ")
488494
self.run_checkout_experiment_layer()
489-
print("Updating checkout_experiment layer with experiments: ")
495+
print(" - Updating checkout_experiment layer with experiments: ")
490496
self.update_checkout_experiment_layer()
497+
print(" - Done updating layer")
498+
print(" - Start running suggested-items-carousel experiment: ")
499+
self.ldproject.start_exp_iteration("suggested-items-carousel", "production")
500+
print(" - Start running new-collection-promotion-banner: ")
501+
self.ldproject.start_exp_iteration("new-collection-promotion-banner", "production")
491502
print("Done")
492503

493504
def run_checkout_experiment_layer(self):
494505
res = self.ldproject.create_layer(
495-
layer_key= "checkout-experiment-layer",
496-
layer_name="Checkout Experiment Layer",
506+
# layer_key= "checkout-experiment-layer",
507+
# layer_name="Checkout Experiment Layer",
497508
description="This layer is to allow having two experiments that affect the checkout cart running at the same time.",
498509
)
499510

@@ -567,7 +578,7 @@ def add_userid_to_flags(self):
567578
res = self.ldproject.add_maintainer_to_flag("debuggingModeForDevelopers")
568579
res = self.ldproject.add_maintainer_to_flag("release-new-search-engine")
569580
res = self.ldproject.add_maintainer_to_flag("release-new-shorten-collections-page")
570-
581+
res = self.ldproject.add_maintainer_to_flag("q-4-increase-average-total-in-cart-price-ld-holdout")
571582
# ############################################################################################################
572583

573584
# Update project settings
@@ -1189,7 +1200,7 @@ def flag_database_guarded_release(self):
11891200
def flag_api_guarded_release(self):
11901201
res = self.ldproject.create_flag(
11911202
"release-new-investment-stock-api",
1192-
"B2 - Release: New API (Guarded Release) - Investment",
1203+
"B2 - Release: New API (Guarded Release) - Investment",
11931204
"Release new API for stocks component",
11941205
[
11951206
{
@@ -1322,10 +1333,28 @@ def flag_exp_new_search_engine(self):
13221333
},
13231334
],
13241335
tags=["experiment", "ecommerce"],
1336+
on_variation=0,
1337+
off_variation=1,
1338+
)
1339+
1340+
def flag_holdout_q4_increase_incart_price(self):
1341+
res = self.ldproject.create_flag(
1342+
"q-4-increase-average-total-in-cart-price-ld-holdout",
1343+
"Flag to Create Q4 Increase Total Price Holdout",
1344+
"Description: Flag to create holdout for q4 increase incart price",
1345+
[
1346+
{
1347+
"value": True,
1348+
"name": "In holdout"
1349+
},
1350+
{
1351+
"value": False,
1352+
"name": "Not In holdout"
1353+
},
1354+
],
13251355
purpose="holdout",
13261356
on_variation=0,
13271357
off_variation=1,
1328-
# maintainerId=self.user_id
13291358
)
13301359

13311360

@@ -1907,7 +1936,7 @@ def rp_detailed_spending_insights_reports(self):
19071936
if not self.phase_ids:
19081937
self.phase_ids = self.ldproject.get_pipeline_phase_ids("togglebank-v2-pipeline")
19091938
self.ldproject.advance_flag_phase("detailedSpendingInsightsReports", "active", self.phase_ids["test"])
1910-
self.ldproject.advance_flag_phase("detailedSpendingInsightsReports", "active", self.phase_ids["guard"])
1939+
self.ldproject.advance_flag_phase("detailedSpendingInsightsReports", "active", self.phase_ids["guard"], guarded=True)
19111940
self.ldproject.advance_flag_phase("detailedSpendingInsightsReports", "active", self.phase_ids["ga"])
19121941

19131942
def rp_scheduled_bill_payments(self):
@@ -1916,7 +1945,7 @@ def rp_scheduled_bill_payments(self):
19161945
if not self.phase_ids:
19171946
self.phase_ids = self.ldproject.get_pipeline_phase_ids("togglebank-v2-pipeline")
19181947
self.ldproject.advance_flag_phase("scheduledBillPayments", "active", self.phase_ids["test"])
1919-
self.ldproject.advance_flag_phase("scheduledBillPayments", "active", self.phase_ids["guard"])
1948+
self.ldproject.advance_flag_phase("scheduledBillPayments", "active", self.phase_ids["guard"], guarded=True)
19201949
self.ldproject.advance_flag_phase("scheduledBillPayments", "active", self.phase_ids["ga"])
19211950

19221951
def rp_cross_border_payment_simplification(self):
@@ -1925,23 +1954,23 @@ def rp_cross_border_payment_simplification(self):
19251954
if not self.phase_ids:
19261955
self.phase_ids = self.ldproject.get_pipeline_phase_ids("togglebank-v2-pipeline")
19271956
self.ldproject.advance_flag_phase("crossBorderPaymentSimplification", "active", self.phase_ids["test"])
1928-
self.ldproject.advance_flag_phase("crossBorderPaymentSimplification", "active", self.phase_ids["guard"])
1957+
self.ldproject.advance_flag_phase("crossBorderPaymentSimplification", "active", self.phase_ids["guard"], guarded=True)
19291958

19301959
def rp_merchant_rewards_integration(self):
19311960
res = self.ldproject.add_pipeline_flag("merchantRewardsIntegration", "togglebank-v2-pipeline")
19321961
self.ldproject.attach_metric_to_flag("merchantRewardsIntegration", ["stocks-api-latency","stocks-api-error-rates"])
19331962
if not self.phase_ids:
19341963
self.phase_ids = self.ldproject.get_pipeline_phase_ids("togglebank-v2-pipeline")
19351964
self.ldproject.advance_flag_phase("merchantRewardsIntegration", "active", self.phase_ids["test"])
1936-
self.ldproject.advance_flag_phase("merchantRewardsIntegration", "active", self.phase_ids["guard"])
1965+
self.ldproject.advance_flag_phase("merchantRewardsIntegration", "active", self.phase_ids["guard"], guarded=True)
19371966

19381967
def rp_virtual_card_issuance(self):
19391968
res = self.ldproject.add_pipeline_flag("virtualCardIssuance", "togglebank-v2-pipeline")
19401969
self.ldproject.attach_metric_to_flag("virtualCardIssuance", ["stocks-api-latency","stocks-api-error-rates"])
19411970
if not self.phase_ids:
19421971
self.phase_ids = self.ldproject.get_pipeline_phase_ids("togglebank-v2-pipeline")
19431972
self.ldproject.advance_flag_phase("virtualCardIssuance", "active", self.phase_ids["test"])
1944-
self.ldproject.advance_flag_phase("virtualCardIssuance", "active", self.phase_ids["guard"])
1973+
self.ldproject.advance_flag_phase("virtualCardIssuance", "active", self.phase_ids["guard"], guarded=True)
19451974
self.ldproject.advance_flag_phase("virtualCardIssuance", "active", self.phase_ids["ga"])
19461975

19471976
def rp_api_support_for_third_party_applications(self):
@@ -1950,7 +1979,7 @@ def rp_api_support_for_third_party_applications(self):
19501979
if not self.phase_ids:
19511980
self.phase_ids = self.ldproject.get_pipeline_phase_ids("togglebank-v2-pipeline")
19521981
self.ldproject.advance_flag_phase("apiSupportForThirdPartyApplications", "active", self.phase_ids["test"])
1953-
self.ldproject.advance_flag_phase("apiSupportForThirdPartyApplications", "active", self.phase_ids["guard"])
1982+
self.ldproject.advance_flag_phase("apiSupportForThirdPartyApplications", "active", self.phase_ids["guard"], guarded=True)
19541983
self.ldproject.advance_flag_phase("apiSupportForThirdPartyApplications", "active", self.phase_ids["ga"])
19551984

19561985
############################################################################################################

Diff for: .github/workflows/demo_provisioning_scripts/LDPlatform.py

+31-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import requests
22
import json
33
import time
4+
import uuid
45

56

67
class LDPlatform:
@@ -585,6 +586,8 @@ def create_experiment(
585586
attributes=None,
586587
randomization_unit="user",
587588
custom_treatment_names=None,
589+
methodology="bayesian",
590+
analysisConfig={"bayesianThreshold": "95"}
588591
):
589592
if self.experiment_exists(exp_key, exp_env):
590593
return
@@ -608,6 +611,8 @@ def create_experiment(
608611
},
609612
"randomizationUnit": randomization_unit,
610613
},
614+
"methodology": methodology,
615+
"analysisConfig": analysisConfig,
611616
}
612617

613618
if self.metric_group_exists(primary_key):
@@ -661,11 +666,12 @@ def create_holdout(
661666
"description": description,
662667
"randomizationunit": randomization_unit,
663668
"attributes": attributes,
664-
"holdoutamount": "10",
669+
"holdoutamount": "5",
665670
"primarymetrickey": primary_metric_key,
666671
"metrics": metrics,
667672
"prerequisiteflagkey": prerequisiteflagkey,
668-
673+
"analysisConfig": {"significanceThreshold": "5", "testDirection": "two-sided"},
674+
"methodology": "frequentist"
669675
}
670676

671677
headers = {
@@ -696,8 +702,8 @@ def create_holdout(
696702

697703
def create_layer(
698704
self,
699-
layer_key,
700-
layer_name,
705+
# layer_key,
706+
# layer_name,
701707
description,
702708
):
703709

@@ -1619,9 +1625,10 @@ def attach_metric_to_flag(self, flag_key, metric_keys=[]):
16191625
##################################################
16201626
# Advance a flag to the next phase
16211627
##################################################
1622-
def advance_flag_phase(self, flag_key, status, pipeline_phase_id):
1628+
def advance_flag_phase(self, flag_key, status, pipeline_phase_id, guarded=False):
16231629
counter = 0
16241630
status_code = 0
1631+
payload = {}
16251632
while status_code != 200:
16261633
counter += 1
16271634
url = (
@@ -1632,15 +1639,31 @@ def advance_flag_phase(self, flag_key, status, pipeline_phase_id):
16321639
+ "/release/phases/"
16331640
+ pipeline_phase_id
16341641
)
1635-
1642+
1643+
if guarded == True:
1644+
payload = {
1645+
"status": status,
1646+
"audiences": [
1647+
{
1648+
"audienceId": str(uuid.uuid4()),
1649+
"releaseGuardianConfiguration": {
1650+
"randomizationUnit": "user"
1651+
}
1652+
}
1653+
]
1654+
}
1655+
1656+
else:
1657+
payload = {
1658+
"status": status,
1659+
}
1660+
16361661
headers = {
16371662
"Content-Type": "application/json",
16381663
"Authorization": self.api_key,
16391664
"LD-API-Version": "beta",
16401665
}
16411666

1642-
payload = {"status": status}
1643-
16441667
response = requests.put(url, json=payload, headers=headers)
16451668
status_code = response.status_code
16461669
if counter > 8:

Diff for: components/ContextProvider.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import CryptoJS from 'crypto-js';
55
import { setCookie } from "cookies-next";
66
import { LD_CONTEXT_COOKIE_KEY } from "@/utils/constants";
77
import { isAndroid, isIOS, isBrowser, isMobile, isMacOs, isWindows } from 'react-device-detect';
8+
import { platform } from 'os';
89

910
const ContextProvider = ({ children }: { children: React.ReactNode }) => {
1011
const [LDProvider, setLDProvider] = useState<any>(null);
@@ -19,6 +20,9 @@ const ContextProvider = ({ children }: { children: React.ReactNode }) => {
1920
user: {
2021
anonymous: true,
2122
key: uuidv4().slice(0, 10),
23+
device: device,
24+
operating_system: operatingSystem,
25+
location: Intl.DateTimeFormat().resolvedOptions().timeZone,
2226
},
2327
device: {
2428
key: device,

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "multi-industry-demo",
3-
"version": "2.1.1",
3+
"version": "2.2",
44
"private": true,
55
"scripts": {
66
"dev": "next dev",

0 commit comments

Comments
 (0)