Lilli98 commited on
Commit
b8b4d9e
·
verified ·
1 Parent(s): 5497367

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -21
app.py CHANGED
@@ -71,11 +71,11 @@ else:
71
  def get_customer_demand(week: int) -> int:
72
  return 4 if week <= 4 else 8
73
 
74
- # =============== MODIFIED Initialization (v4.21 logic + v4.23 bugfix) ===============
75
  def init_game_state(llm_personality: str, info_sharing: str, participant_id: str):
76
  roles = ["Retailer", "Wholesaler", "Distributor", "Factory"]
77
  human_role = "Distributor" # Role is fixed
78
-
79
  st.session_state.game_state = {
80
  'game_running': True,
81
  'participant_id': participant_id,
@@ -86,7 +86,8 @@ def init_game_state(llm_personality: str, info_sharing: str, participant_id: str
86
  'decision_step': 'initial_order',
87
  'human_initial_order': None,
88
  'current_ai_suggestion': None, # v4.23 Bugfix: 用于存储AI建议
89
- 'last_week_orders': {name: 0 for name in roles} # v4.21 Logic: 初始化为0
 
90
  }
91
  for i, name in enumerate(roles):
92
  upstream = roles[i + 1] if i + 1 < len(roles) else None
@@ -240,7 +241,7 @@ def get_llm_prompt(echelon_state_decision_point: dict, week: int, llm_personalit
240
  A 'rational' player would account for their {supply_line_desc} (which is {supply_line} units), but you're too busy panicking to trust that.
241
  **Your 'Panic' Calculation (Ignoring the Supply Line):**
242
  1. **Anchor on Demand:** You just got an order for **{anchor_demand}** units. You'll order *at least* that.
243
- 2. **Correct for Stock:** Your desired 'safe' inventory is {DESIRED_INVENTORY}. Your current net inventory is {net_inventory}. You need to order **{stock_correction}** more units to feel safe again.
244
  3. **Ignore Supply Line:** You'll ignore the **{supply_line} units** already in your pipeline.
245
  **Final Panic Order:** (Your Incoming Order) + (Your Stock Correction)
246
  * Order = {panicky_order_calc} = **{panicky_order} units**.
@@ -274,7 +275,7 @@ def get_llm_prompt(echelon_state_decision_point: dict, week: int, llm_personalit
274
  **Your 'Panic' Calculation (Ignoring Supply Line, Averaging Anchors):**
275
  1. **Anchor on Conflict:** (Your Incoming Order + End-Customer Demand) / 2
276
  * Anchor = ({local_anchor} + {global_anchor}) / 2 = **{anchor_demand}** units.
277
- 2. **Correct for *Your* Stock:** Your desired 'safe' inventory is {DESIRED_INVENTORY}. Your current net inventory is {net_inventory}. You need to order **{stock_correction}** more units.
278
  3. **Ignore *Your* Supply Line:** You'll ignore the **{supply_line} units** in your own pipeline ({supply_line_desc}).
279
  **Final Panic Order:** (Conflicted Anchor) + (Your Stock Correction)
280
  * Order = {panicky_order_calc} = **{panicky_order} units**.
@@ -540,6 +541,13 @@ def save_logs_and_upload(state: dict):
540
  logs_df = pd.json_normalize(state['logs'])
541
  safe_participant_id = re.sub(r'[^a-zA-Z0-9_-]', '_', participant_id)
542
  fname = LOCAL_LOG_DIR / f"log_{safe_participant_id}_{int(time.time())}.csv"
 
 
 
 
 
 
 
543
  for col in logs_df.select_dtypes(include=['object']).columns: logs_df[col] = logs_df[col].astype(str)
544
  logs_df.to_csv(fname, index=False)
545
  st.success(f"Log successfully saved locally: `{fname}`")
@@ -580,7 +588,13 @@ def save_logs_and_upload(state: dict):
580
  'total_chain_cost': float(total_chain_cost), # 新增: 总成本
581
  'order_std_dev': float(order_std_dev) if pd.notna(order_std_dev) else 0.0
582
  }
583
-
 
 
 
 
 
 
584
  leaderboard_data = load_leaderboard_data()
585
  leaderboard_data[participant_id] = new_entry
586
  save_leaderboard_data(leaderboard_data)
@@ -596,13 +610,17 @@ st.title("🍺 The Beer Game: A Human-AI Collaboration Challenge")
596
  # --- NEW: Check for Consent ---
597
  if 'consent_given' not in st.session_state:
598
  st.session_state['consent_given'] = False
 
 
 
599
 
600
  # --- Initialization Check ---
601
  if st.session_state.get('initialization_error'):
602
  st.error(st.session_state.initialization_error)
 
603
  # --- Consent Form Display Logic ---
604
  elif not st.session_state['consent_given']:
605
- st.header("📝 Participant Consent Form")
606
  st.markdown("""
607
  **Project Title:** Behavioural Contagion or Rational Alignment? Human Performance in LLM Supply Chains
608
 
@@ -626,20 +644,29 @@ elif not st.session_state['consent_given']:
626
 
627
  st.markdown("---")
628
 
629
- consent_choice = st.radio(
630
- "**Do you agree to take part in this study?**",
631
- ('Yes, I agree to participate in this study.', 'No, I do not agree to participate in this study.'),
632
- index=None
633
- )
634
-
635
- if st.button("Continue"):
636
- if consent_choice == 'Yes, I agree to participate in this study.':
637
- st.session_state['consent_given'] = True
638
- st.rerun()
639
- elif consent_choice == 'No, I do not agree to participate in this study.':
640
- st.error("Thank you for your time. Since you declined participation, the experiment will not proceed.")
641
- else:
642
- st.warning("Please select an option to continue.")
 
 
 
 
 
 
 
 
 
643
 
644
  # --- Game Setup / Main Interface Logic ---
645
  else:
@@ -703,7 +730,13 @@ else:
703
  state = st.session_state.game_state
704
  week, human_role, echelons, info_sharing = state['week'], state['human_role'], state['echelons'], state['info_sharing']
705
  echelon_order = ["Retailer", "Wholesaler", "Distributor", "Factory"] # Define here for UI
 
 
 
 
 
706
  st.header(f"Week {week} / {WEEKS}")
 
707
  st.subheader(f"Your Role: **{human_role}** ({state['participant_id']})")
708
  st.markdown("---")
709
  st.subheader("Supply Chain Status (Start of Week State)")
@@ -880,5 +913,8 @@ else:
880
  st.error(f"Error generating final report: {e}")
881
  show_leaderboard_ui()
882
  if st.button("✨ Start a New Game"):
 
 
 
883
  del st.session_state.game_state
884
  st.rerun()
 
71
  def get_customer_demand(week: int) -> int:
72
  return 4 if week <= 4 else 8
73
 
74
+ # =============== MODIFIED Initialization (记录开始时间) ===============
75
  def init_game_state(llm_personality: str, info_sharing: str, participant_id: str):
76
  roles = ["Retailer", "Wholesaler", "Distributor", "Factory"]
77
  human_role = "Distributor" # Role is fixed
78
+
79
  st.session_state.game_state = {
80
  'game_running': True,
81
  'participant_id': participant_id,
 
86
  'decision_step': 'initial_order',
87
  'human_initial_order': None,
88
  'current_ai_suggestion': None, # v4.23 Bugfix: 用于存储AI建议
89
+ 'last_week_orders': {name: 0 for name in roles}, # v4.21 Logic: 初始化为0
90
+ 'start_timestamp': datetime.utcnow().isoformat() + "Z", # [修改点 3]: 记录实验开始时间
91
  }
92
  for i, name in enumerate(roles):
93
  upstream = roles[i + 1] if i + 1 < len(roles) else None
 
241
  A 'rational' player would account for their {supply_line_desc} (which is {supply_line} units), but you're too busy panicking to trust that.
242
  **Your 'Panic' Calculation (Ignoring the Supply Line):**
243
  1. **Anchor on Demand:** You just got an order for **{anchor_demand}** units. You'll order *at least* that.
244
+ 2. **Correct for Stock:** Your desired 'safe' inventory is {DESIRED_INVENTORY}. Your current net inventory is {net_inventory}. You need to order an extra **{stock_correction}** more units to feel safe again.
245
  3. **Ignore Supply Line:** You'll ignore the **{supply_line} units** already in your pipeline.
246
  **Final Panic Order:** (Your Incoming Order) + (Your Stock Correction)
247
  * Order = {panicky_order_calc} = **{panicky_order} units**.
 
275
  **Your 'Panic' Calculation (Ignoring Supply Line, Averaging Anchors):**
276
  1. **Anchor on Conflict:** (Your Incoming Order + End-Customer Demand) / 2
277
  * Anchor = ({local_anchor} + {global_anchor}) / 2 = **{anchor_demand}** units.
278
+ 2. **Correct for *Your* Stock:** Your desired 'safe' inventory is {DESIRED_INVENTORY}. Your current net inventory is {net_inventory}. You need to order an extra **{stock_correction}** more units.
279
  3. **Ignore *Your* Supply Line:** You'll ignore the **{supply_line} units** in your own pipeline ({supply_line_desc}).
280
  **Final Panic Order:** (Conflicted Anchor) + (Your Stock Correction)
281
  * Order = {panicky_order_calc} = **{panicky_order} units**.
 
541
  logs_df = pd.json_normalize(state['logs'])
542
  safe_participant_id = re.sub(r'[^a-zA-Z0-9_-]', '_', participant_id)
543
  fname = LOCAL_LOG_DIR / f"log_{safe_participant_id}_{int(time.time())}.csv"
544
+ # [修改点 4]: 在 logs_df 中添加实验结束时间戳和同意时间戳
545
+ logs_df['experiment_end_timestamp'] = datetime.utcnow().isoformat() + "Z"
546
+ if st.session_state.get('consent_timestamp'):
547
+ logs_df['consent_given_timestamp'] = st.session_state['consent_timestamp']
548
+ else:
549
+ logs_df['consent_given_timestamp'] = "N/A"
550
+
551
  for col in logs_df.select_dtypes(include=['object']).columns: logs_df[col] = logs_df[col].astype(str)
552
  logs_df.to_csv(fname, index=False)
553
  st.success(f"Log successfully saved locally: `{fname}`")
 
588
  'total_chain_cost': float(total_chain_cost), # 新增: 总成本
589
  'order_std_dev': float(order_std_dev) if pd.notna(order_std_dev) else 0.0
590
  }
591
+
592
+ # [修改点 3]: 将实验开始时间戳添加到 Leaderboard entry 中 (仅用于追溯,不参与排名)
593
+ if state.get('start_timestamp'):
594
+ new_entry['start_timestamp'] = state['start_timestamp']
595
+ if st.session_state.get('consent_timestamp'):
596
+ new_entry['consent_timestamp'] = st.session_state['consent_timestamp']
597
+
598
  leaderboard_data = load_leaderboard_data()
599
  leaderboard_data[participant_id] = new_entry
600
  save_leaderboard_data(leaderboard_data)
 
610
  # --- NEW: Check for Consent ---
611
  if 'consent_given' not in st.session_state:
612
  st.session_state['consent_given'] = False
613
+ # --- NEW: Storage for Consent Timestamp ---
614
+ if 'consent_timestamp' not in st.session_state:
615
+ st.session_state['consent_timestamp'] = None
616
 
617
  # --- Initialization Check ---
618
  if st.session_state.get('initialization_error'):
619
  st.error(st.session_state.initialization_error)
620
+
621
  # --- Consent Form Display Logic ---
622
  elif not st.session_state['consent_given']:
623
+ st.header("📝 Participant Consent Form (参与者知情同意书)")
624
  st.markdown("""
625
  **Project Title:** Behavioural Contagion or Rational Alignment? Human Performance in LLM Supply Chains
626
 
 
644
 
645
  st.markdown("---")
646
 
647
+ # 使用 st.form 确保按钮点击后不会清空 st.radio 的值
648
+ with st.form("consent_form"):
649
+ consent_choice = st.radio(
650
+ "**Do you agree to take part in this study?**",
651
+ ('Yes, I agree to participate in this study.', 'No, I do not agree to participate in this study.'),
652
+ index=None,
653
+ key='consent_radio'
654
+ )
655
+ submit_button = st.form_submit_button("Continue / 提交")
656
+
657
+ if submit_button:
658
+ if consent_choice == 'Yes, I agree to participate in this study.':
659
+ # [修正点 2]: 记录时间戳并允许进入
660
+ st.session_state['consent_given'] = True
661
+ st.session_state['consent_timestamp'] = datetime.utcnow().isoformat() + "Z"
662
+ st.rerun()
663
+ elif consent_choice == 'No, I do not agree to participate in this study.':
664
+ # [修正点 1]: 选择 No 后显示感谢信息并停止流程
665
+ st.error("Thank you for your time. Since you declined participation, the experiment will not proceed. You may now close this page.")
666
+ st.session_state['consent_given'] = False # 保持为 False,防止意外进入
667
+ else:
668
+ st.warning("Please select an option to continue.")
669
+
670
 
671
  # --- Game Setup / Main Interface Logic ---
672
  else:
 
730
  state = st.session_state.game_state
731
  week, human_role, echelons, info_sharing = state['week'], state['human_role'], state['echelons'], state['info_sharing']
732
  echelon_order = ["Retailer", "Wholesaler", "Distributor", "Factory"] # Define here for UI
733
+
734
+ # [修改点 1.1]: 移除 st.columns,恢复主内容到左侧,侧边栏不变
735
+ # col_main, col_sidebar_image = st.columns([4, 1]) 移除
736
+
737
+ # with col_main: 移除
738
  st.header(f"Week {week} / {WEEKS}")
739
+ # [修改点 2.3]: 移除 subheader 中 AI Mode 和 Information 的提示
740
  st.subheader(f"Your Role: **{human_role}** ({state['participant_id']})")
741
  st.markdown("---")
742
  st.subheader("Supply Chain Status (Start of Week State)")
 
913
  st.error(f"Error generating final report: {e}")
914
  show_leaderboard_ui()
915
  if st.button("✨ Start a New Game"):
916
+ # 在重置游戏时清除所有与实验相关的时间戳
917
+ if 'consent_timestamp' in st.session_state: del st.session_state['consent_timestamp']
918
+ if 'consent_given' in st.session_state: del st.session_state['consent_given']
919
  del st.session_state.game_state
920
  st.rerun()