Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -46,7 +46,7 @@ LOCAL_LOG_DIR.mkdir(exist_ok=True)
|
|
| 46 |
IMAGE_PATH = "beer_game_diagram.png"
|
| 47 |
LEADERBOARD_FILE = "leaderboard.json"
|
| 48 |
|
| 49 |
-
#
|
| 50 |
EXPERIMENT_SETTINGS = [
|
| 51 |
('human_like', 'local'),
|
| 52 |
('human_like', 'full'),
|
|
@@ -55,6 +55,7 @@ EXPERIMENT_SETTINGS = [
|
|
| 55 |
]
|
| 56 |
# --- API & Secrets Configuration ---
|
| 57 |
try:
|
|
|
|
| 58 |
client = openai.OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
|
| 59 |
HF_TOKEN = st.secrets.get("HF_TOKEN")
|
| 60 |
HF_REPO_ID = st.secrets.get("HF_REPO_ID")
|
|
@@ -104,6 +105,7 @@ def init_game_state(llm_personality: str, info_sharing: str, participant_id: str
|
|
| 104 |
# ==============================================================================
|
| 105 |
def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
| 106 |
# This function remains correct.
|
|
|
|
| 107 |
if not client: return 8, "NO_API_KEY_DEFAULT"
|
| 108 |
with st.spinner(f"Getting AI decision for {echelon_name}..."):
|
| 109 |
try:
|
|
@@ -539,6 +541,7 @@ def save_logs_and_upload(state: dict):
|
|
| 539 |
logs_df = pd.json_normalize(state['logs'])
|
| 540 |
safe_participant_id = re.sub(r'[^a-zA-Z0-9_-]', '_', participant_id)
|
| 541 |
fname = LOCAL_LOG_DIR / f"log_{safe_participant_id}_{int(time.time())}.csv"
|
|
|
|
| 542 |
logs_df['experiment_end_timestamp'] = datetime.utcnow().isoformat() + "Z"
|
| 543 |
if st.session_state.get('consent_timestamp'):
|
| 544 |
logs_df['consent_given_timestamp'] = st.session_state['consent_timestamp']
|
|
@@ -622,7 +625,7 @@ if st.session_state.get('initialization_error'):
|
|
| 622 |
|
| 623 |
# --- Consent Form Display Logic ---
|
| 624 |
elif not st.session_state['consent_given']:
|
| 625 |
-
st.header("📝 Participant Consent Form")
|
| 626 |
st.markdown("""
|
| 627 |
**Project Title:** Behavioural Contagion or Rational Alignment? Human Performance in LLM Supply Chains
|
| 628 |
|
|
@@ -689,6 +692,15 @@ else:
|
|
| 689 |
else:
|
| 690 |
# 默认值,如果未输入ID
|
| 691 |
llm_personality, info_sharing = ('human_like', 'local')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
|
| 693 |
# =============== MODIFIED: Start Game Button ===============
|
| 694 |
if st.button("🚀 Start Game", type="primary", disabled=(client is None)):
|
|
@@ -884,13 +896,15 @@ else:
|
|
| 884 |
st.rerun()
|
| 885 |
|
| 886 |
# --- Game Over Interface ---
|
| 887 |
-
|
| 888 |
st.header("🎉 Game Over!")
|
| 889 |
state = st.session_state.game_state
|
| 890 |
participant_id = state['participant_id'] # 获取ID
|
| 891 |
|
| 892 |
# --------------------------------------------------------------------------
|
| 893 |
-
# [
|
|
|
|
|
|
|
| 894 |
personalized_survey_url = f"{QUALTRICS_BASE_URL}?{PID_FIELD_NAME}={participant_id}"
|
| 895 |
|
| 896 |
st.markdown("---")
|
|
@@ -928,4 +942,48 @@ else:
|
|
| 928 |
if 'consent_timestamp' in st.session_state: del st.session_state['consent_timestamp']
|
| 929 |
if 'consent_given' in st.session_state: del st.session_state['consent_given']
|
| 930 |
del st.session_state.game_state
|
| 931 |
-
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
IMAGE_PATH = "beer_game_diagram.png"
|
| 47 |
LEADERBOARD_FILE = "leaderboard.json"
|
| 48 |
|
| 49 |
+
# --- 实验条件配置:新增常量 ---
|
| 50 |
EXPERIMENT_SETTINGS = [
|
| 51 |
('human_like', 'local'),
|
| 52 |
('human_like', 'full'),
|
|
|
|
| 55 |
]
|
| 56 |
# --- API & Secrets Configuration ---
|
| 57 |
try:
|
| 58 |
+
# 注意:在多Space部署中,每个Space的secrets中只放一个API KEY
|
| 59 |
client = openai.OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
|
| 60 |
HF_TOKEN = st.secrets.get("HF_TOKEN")
|
| 61 |
HF_REPO_ID = st.secrets.get("HF_REPO_ID")
|
|
|
|
| 105 |
# ==============================================================================
|
| 106 |
def get_llm_order_decision(prompt: str, echelon_name: str) -> (int, str):
|
| 107 |
# This function remains correct.
|
| 108 |
+
# 注意:client 现在是全局的,所有用户的API请求都通过它进行。
|
| 109 |
if not client: return 8, "NO_API_KEY_DEFAULT"
|
| 110 |
with st.spinner(f"Getting AI decision for {echelon_name}..."):
|
| 111 |
try:
|
|
|
|
| 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']
|
|
|
|
| 625 |
|
| 626 |
# --- Consent Form Display Logic ---
|
| 627 |
elif not st.session_state['consent_given']:
|
| 628 |
+
st.header("📝 Participant Consent Form (参与者知情同意书)")
|
| 629 |
st.markdown("""
|
| 630 |
**Project Title:** Behavioural Contagion or Rational Alignment? Human Performance in LLM Supply Chains
|
| 631 |
|
|
|
|
| 692 |
else:
|
| 693 |
# 默认值,如果未输入ID
|
| 694 |
llm_personality, info_sharing = ('human_like', 'local')
|
| 695 |
+
|
| 696 |
+
# [修改点 2.2]: 移除启动时显示实验条件的提示
|
| 697 |
+
# st.info(f"You will be automatically assigned to the condition: **{llm_personality.replace('_', ' ').title()} / {info_sharing.title()}**.")
|
| 698 |
+
# =================================================================
|
| 699 |
+
|
| 700 |
+
# 移除原有的 c1, c2 和 selectbox
|
| 701 |
+
# c1, c2 = st.columns(2)
|
| 702 |
+
# with c1:
|
| 703 |
+
# with c2:
|
| 704 |
|
| 705 |
# =============== MODIFIED: Start Game Button ===============
|
| 706 |
if st.button("🚀 Start Game", type="primary", disabled=(client is None)):
|
|
|
|
| 896 |
st.rerun()
|
| 897 |
|
| 898 |
# --- Game Over Interface ---
|
| 899 |
+
elif 'game_state' in st.session_state and not st.session_state.game_state.get('game_running', False) and st.session_state.game_state.get('week', 0) > WEEKS:
|
| 900 |
st.header("🎉 Game Over!")
|
| 901 |
state = st.session_state.game_state
|
| 902 |
participant_id = state['participant_id'] # 获取ID
|
| 903 |
|
| 904 |
# --------------------------------------------------------------------------
|
| 905 |
+
# [NEW SECTION]: Survey Link Generation
|
| 906 |
+
# --------------------------------------------------------------------------
|
| 907 |
+
# Your Qualtrics URL and PID field name
|
| 908 |
personalized_survey_url = f"{QUALTRICS_BASE_URL}?{PID_FIELD_NAME}={participant_id}"
|
| 909 |
|
| 910 |
st.markdown("---")
|
|
|
|
| 942 |
if 'consent_timestamp' in st.session_state: del st.session_state['consent_timestamp']
|
| 943 |
if 'consent_given' in st.session_state: del st.session_state['consent_given']
|
| 944 |
del st.session_state.game_state
|
| 945 |
+
st.rerun()
|
| 946 |
+
|
| 947 |
+
# --- Fallback: Game Setup (Default Screen) ---
|
| 948 |
+
else:
|
| 949 |
+
# This is the original Game Setup logic when game_state does not exist or is just cleared.
|
| 950 |
+
# This covers the scenarios: Before Week 1, or after a full game reset.
|
| 951 |
+
|
| 952 |
+
st.markdown("---")
|
| 953 |
+
st.header("⚙️ Game Configuration")
|
| 954 |
+
|
| 955 |
+
# =============== 1. NEW: Participant ID Input ===============
|
| 956 |
+
participant_id = st.text_input("Enter Your Name or Team ID:", key="participant_id_input", placeholder="e.g., Team A")
|
| 957 |
+
# =======================================================
|
| 958 |
+
|
| 959 |
+
# =============== 1. MODIFIED: 自动分配逻辑 (移除手动选择) ===============
|
| 960 |
+
if participant_id:
|
| 961 |
+
setting_index = hash(participant_id) % len(EXPERIMENT_SETTINGS)
|
| 962 |
+
llm_personality, info_sharing = EXPERIMENT_SETTINGS[setting_index]
|
| 963 |
+
else:
|
| 964 |
+
llm_personality, info_sharing = ('human_like', 'local')
|
| 965 |
+
|
| 966 |
+
# 移除原有的 c1, c2 和 selectbox
|
| 967 |
+
|
| 968 |
+
# =============== MODIFIED: Start Game Button ===============
|
| 969 |
+
if st.button("🚀 Start Game", type="primary", disabled=(client is None)):
|
| 970 |
+
if not participant_id:
|
| 971 |
+
st.error("Please enter a Name or Team ID to start!")
|
| 972 |
+
else:
|
| 973 |
+
existing_data = load_leaderboard_data()
|
| 974 |
+
if participant_id in existing_data:
|
| 975 |
+
if st.session_state.get('last_id_warning') == participant_id:
|
| 976 |
+
st.session_state.pop('last_id_warning', None)
|
| 977 |
+
init_game_state(llm_personality, info_sharing, participant_id)
|
| 978 |
+
st.rerun()
|
| 979 |
+
else:
|
| 980 |
+
st.session_state['last_id_warning'] = participant_id
|
| 981 |
+
st.warning(f"ID '{participant_id}' already exists! Your score will be overwritten. Click 'Start Game' again to confirm.")
|
| 982 |
+
else:
|
| 983 |
+
if 'last_id_warning' in st.session_state:
|
| 984 |
+
del st.session_state['last_id_warning']
|
| 985 |
+
init_game_state(llm_personality, info_sharing, participant_id)
|
| 986 |
+
st.rerun()
|
| 987 |
+
# ===========================================================
|
| 988 |
+
|
| 989 |
+
show_leaderboard_ui()
|