prolific ready
Browse files- scripts/check_prompt_format.py +74 -0
- src/app.py +1 -1
- src/data.py +15 -0
- src/lsp_wrappers.py +79 -7
- src/ui/components.py +37 -23
- src/ui/screens_preference.py +12 -3
- study_config.yaml +6 -6
scripts/check_prompt_format.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Print the full seller system prompt that the user study sends to the model
|
| 3 |
+
for a fake but realistic participant. No comparison, no training format,
|
| 4 |
+
just the prompt.
|
| 5 |
+
|
| 6 |
+
Usage:
|
| 7 |
+
cd /dfs/scratch1/echoi1/prolific_preferences
|
| 8 |
+
python3 scripts/print_prompt.py
|
| 9 |
+
"""
|
| 10 |
+
from src.lsp_wrappers import (
|
| 11 |
+
format_demographics,
|
| 12 |
+
build_seller_system_prompt_preference,
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
DEMOGRAPHICS = {
|
| 17 |
+
"age": "32",
|
| 18 |
+
"gender": "Female",
|
| 19 |
+
"geographic_region": "West",
|
| 20 |
+
"education_level": "College graduate/some postgrad",
|
| 21 |
+
"race": "White",
|
| 22 |
+
"us_citizen": "Yes",
|
| 23 |
+
"marital_status": "Single",
|
| 24 |
+
"religion": "Agnostic",
|
| 25 |
+
"religious_attendance": "Never",
|
| 26 |
+
"political_affiliation": "Independent",
|
| 27 |
+
"income": "$50,000-$75,000",
|
| 28 |
+
"political_views": "Moderate",
|
| 29 |
+
"household_size": "2",
|
| 30 |
+
"employment_status": "Full-time employment",
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
BACKGROUND = {
|
| 34 |
+
"movies_criteria": (
|
| 35 |
+
"I look for strong character development, an interesting plot, "
|
| 36 |
+
"and good cinematography."
|
| 37 |
+
),
|
| 38 |
+
"movies_enjoy": (
|
| 39 |
+
"I enjoy psychological thrillers and indie dramas."
|
| 40 |
+
),
|
| 41 |
+
"movies_avoid": (
|
| 42 |
+
"I avoid slasher horror and broad slapstick comedies."
|
| 43 |
+
),
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
PAIR = {
|
| 47 |
+
"pair_id": "test-pair-001",
|
| 48 |
+
"category": "movies",
|
| 49 |
+
"product_a": {
|
| 50 |
+
"title": "Eternal Sunshine of the Spotless Mind",
|
| 51 |
+
"description": ["A heartfelt sci-fi romance about memory and love."],
|
| 52 |
+
"features": [],
|
| 53 |
+
"price": "12.99",
|
| 54 |
+
},
|
| 55 |
+
"product_b": {
|
| 56 |
+
"title": "The Hangover",
|
| 57 |
+
"description": ["A wild bachelor party comedy in Las Vegas."],
|
| 58 |
+
"features": [],
|
| 59 |
+
"price": "9.99",
|
| 60 |
+
},
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
cfg = {
|
| 65 |
+
"prompt_variant": {
|
| 66 |
+
"personalization": True,
|
| 67 |
+
"include_bio": True,
|
| 68 |
+
},
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
demo_str = format_demographics(DEMOGRAPHICS, background=BACKGROUND, include_bio=True)
|
| 72 |
+
sys_prompt = build_seller_system_prompt_preference(PAIR, cfg, demo_str)
|
| 73 |
+
|
| 74 |
+
print(sys_prompt)
|
src/app.py
CHANGED
|
@@ -47,7 +47,7 @@ def _init_submodule() -> None:
|
|
| 47 |
|
| 48 |
# GitHub serves a tarball of any branch/tag/SHA at this URL.
|
| 49 |
# Pinned to a specific commit SHA so future lsp changes don't break us.
|
| 50 |
-
branch = "
|
| 51 |
tarball_url = f"https://api.github.com/repos/batu-el/lsp/tarball/{branch}"
|
| 52 |
tmp_tar = Path("/tmp/lsp.tar.gz")
|
| 53 |
tmp_extract = Path("/tmp/lsp_extract")
|
|
|
|
| 47 |
|
| 48 |
# GitHub serves a tarball of any branch/tag/SHA at this URL.
|
| 49 |
# Pinned to a specific commit SHA so future lsp changes don't break us.
|
| 50 |
+
branch = "74582acd911f81309ba8b22cef9286c2887dda18"
|
| 51 |
tarball_url = f"https://api.github.com/repos/batu-el/lsp/tarball/{branch}"
|
| 52 |
tmp_tar = Path("/tmp/lsp.tar.gz")
|
| 53 |
tmp_extract = Path("/tmp/lsp_extract")
|
src/data.py
CHANGED
|
@@ -503,6 +503,21 @@ def _assign_from_category(category: str, n: int, user_id: str, cfg: dict) -> lis
|
|
| 503 |
_expire_reservations(reservations)
|
| 504 |
_release_returned_reservations(reservations, cfg)
|
| 505 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
def is_reserved_by_other(i):
|
| 507 |
r = reservations.get(str(i))
|
| 508 |
return r is not None and r["user_id"] != user_id
|
|
|
|
| 503 |
_expire_reservations(reservations)
|
| 504 |
_release_returned_reservations(reservations, cfg)
|
| 505 |
|
| 506 |
+
# If this Prolific PID already has reservations (e.g. they refreshed
|
| 507 |
+
# the tab, got a new user_id, and came back), release the old ones
|
| 508 |
+
# before creating new ones. Prevents the same participant from
|
| 509 |
+
# accumulating multiple reservations.
|
| 510 |
+
if is_prolific:
|
| 511 |
+
stale = [
|
| 512 |
+
idx for idx, r in list(reservations.items())
|
| 513 |
+
if r.get("prolific_pid") == prolific_pid
|
| 514 |
+
]
|
| 515 |
+
for idx in stale:
|
| 516 |
+
del reservations[idx]
|
| 517 |
+
if stale:
|
| 518 |
+
print(f"[ASSIGN] Released {len(stale)} prior reservations "
|
| 519 |
+
f"for returning PID {prolific_pid}")
|
| 520 |
+
|
| 521 |
def is_reserved_by_other(i):
|
| 522 |
r = reservations.get(str(i))
|
| 523 |
return r is not None and r["user_id"] != user_id
|
src/lsp_wrappers.py
CHANGED
|
@@ -11,10 +11,82 @@ sys.path, so the imports succeed.
|
|
| 11 |
"""
|
| 12 |
|
| 13 |
|
| 14 |
-
# ββ Demographics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
|
| 20 |
# ββ Product text helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -63,6 +135,7 @@ def pair_overview(pair: dict) -> str:
|
|
| 63 |
|
| 64 |
|
| 65 |
# ββ Seller system prompt builders βββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 66 |
def build_seller_system_prompt_preference(
|
| 67 |
pair: dict, cfg: dict, demographics_str: str
|
| 68 |
) -> str:
|
|
@@ -71,7 +144,6 @@ def build_seller_system_prompt_preference(
|
|
| 71 |
a, b = pair["product_a"], pair["product_b"]
|
| 72 |
result = get_seller_system_prompt(
|
| 73 |
personalization=pv["personalization"],
|
| 74 |
-
detailed_instruction=pv["detailed_instruction"],
|
| 75 |
title=a.get("title"),
|
| 76 |
description=_desc_str(a),
|
| 77 |
features=_feat_str(a),
|
|
@@ -82,8 +154,9 @@ def build_seller_system_prompt_preference(
|
|
| 82 |
competitor_price=f"${b.get('price')}",
|
| 83 |
demographics=demographics_str,
|
| 84 |
)
|
| 85 |
-
print(f"[PROMPT] personalization={pv['personalization']}
|
| 86 |
-
|
|
|
|
| 87 |
return result
|
| 88 |
|
| 89 |
|
|
@@ -94,7 +167,6 @@ def build_seller_system_prompt_likelihood(
|
|
| 94 |
pv = cfg["prompt_variant"]
|
| 95 |
return get_seller_system_prompt(
|
| 96 |
personalization=pv["personalization"],
|
| 97 |
-
detailed_instruction=pv["detailed_instruction"],
|
| 98 |
title=product.get("title", ""),
|
| 99 |
description=_desc_str(product),
|
| 100 |
features=_feat_str(product),
|
|
|
|
| 11 |
"""
|
| 12 |
|
| 13 |
|
| 14 |
+
# ββ Demographics ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 15 |
+
#
|
| 16 |
+
# Key sets and field labels mirror lsp/src/data.py exactly so that a checkpoint
|
| 17 |
+
# trained on the lsp persona format sees identical strings at inference time.
|
| 18 |
+
# DO NOT reorder, re-spell, or relabel anything in here without making the same
|
| 19 |
+
# change in lsp/src/data.py first β the trained model is sensitive to the
|
| 20 |
+
# literal format ("Demographics: ...", "Their own words about their movie tastes:").
|
| 21 |
+
|
| 22 |
+
DEMOGRAPHIC_KEYS: tuple[str, ...] = (
|
| 23 |
+
"geographic_region",
|
| 24 |
+
"gender",
|
| 25 |
+
"age",
|
| 26 |
+
"education_level",
|
| 27 |
+
"race",
|
| 28 |
+
"us_citizen",
|
| 29 |
+
"marital_status",
|
| 30 |
+
"religion",
|
| 31 |
+
"religious_attendance",
|
| 32 |
+
"political_affiliation",
|
| 33 |
+
"income",
|
| 34 |
+
"political_views",
|
| 35 |
+
"household_size",
|
| 36 |
+
"employment_status",
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
# Maps (lsp BIO_KEY label) β (key in the user study's `background` dict).
|
| 40 |
+
# Movies-only for now; if a future variant trains on groceries with bios
|
| 41 |
+
# you'll need to extend this.
|
| 42 |
+
BIO_KEY_LABEL_TO_BACKGROUND_KEY: dict[str, str] = {
|
| 43 |
+
"What matters to you when picking a movie": "movies_criteria",
|
| 44 |
+
"Description of movies you tend to enjoy": "movies_enjoy",
|
| 45 |
+
"Description of movies you tend to avoid": "movies_avoid",
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def format_demographics(
|
| 50 |
+
demo: dict, background: dict | None = None, include_bio: bool = False
|
| 51 |
+
) -> str:
|
| 52 |
+
"""Render demographics (and optionally bio answers) the same way training does.
|
| 53 |
+
|
| 54 |
+
With include_bio=False (default), produces just the legacy single-line
|
| 55 |
+
"k: v, k: v, ..." string used by older user-study runs.
|
| 56 |
+
|
| 57 |
+
With include_bio=True, produces the multi-line block that matches
|
| 58 |
+
lsp/src/data.py:format_demographics:
|
| 59 |
|
| 60 |
+
Demographics: gender: Male, age: 30, ...
|
| 61 |
+
Their own words about their movie tastes:
|
| 62 |
+
- What matters to you when picking a movie: ...
|
| 63 |
+
- Description of movies you tend to enjoy: ...
|
| 64 |
+
- Description of movies you tend to avoid: ...
|
| 65 |
+
|
| 66 |
+
Empty/missing bio answers are silently skipped, matching training.
|
| 67 |
+
"""
|
| 68 |
+
if not include_bio:
|
| 69 |
+
# Legacy path: single comma-joined line. Untouched for old checkpoints.
|
| 70 |
+
return ", ".join(f"{k}: {v}" for k, v in demo.items())
|
| 71 |
+
|
| 72 |
+
demo_pairs = ", ".join(
|
| 73 |
+
f"{k}: {demo[k]}" for k in DEMOGRAPHIC_KEYS if k in demo
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
bio_lines: list[str] = []
|
| 77 |
+
if background:
|
| 78 |
+
for label, bg_key in BIO_KEY_LABEL_TO_BACKGROUND_KEY.items():
|
| 79 |
+
value = background.get(bg_key)
|
| 80 |
+
if value not in (None, ""):
|
| 81 |
+
bio_lines.append(f"- {label}: {value}")
|
| 82 |
+
|
| 83 |
+
parts: list[str] = []
|
| 84 |
+
if demo_pairs:
|
| 85 |
+
parts.append(f"Demographics: {demo_pairs}")
|
| 86 |
+
if bio_lines:
|
| 87 |
+
parts.append("Their own words about their movie tastes:")
|
| 88 |
+
parts.extend(bio_lines)
|
| 89 |
+
return "\n".join(parts)
|
| 90 |
|
| 91 |
|
| 92 |
# ββ Product text helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 135 |
|
| 136 |
|
| 137 |
# ββ Seller system prompt builders βββββββββββββββββββββββββββββββββββββββββββββ
|
| 138 |
+
|
| 139 |
def build_seller_system_prompt_preference(
|
| 140 |
pair: dict, cfg: dict, demographics_str: str
|
| 141 |
) -> str:
|
|
|
|
| 144 |
a, b = pair["product_a"], pair["product_b"]
|
| 145 |
result = get_seller_system_prompt(
|
| 146 |
personalization=pv["personalization"],
|
|
|
|
| 147 |
title=a.get("title"),
|
| 148 |
description=_desc_str(a),
|
| 149 |
features=_feat_str(a),
|
|
|
|
| 154 |
competitor_price=f"${b.get('price')}",
|
| 155 |
demographics=demographics_str,
|
| 156 |
)
|
| 157 |
+
print(f"[PROMPT] personalization={pv['personalization']}, "
|
| 158 |
+
f"include_bio={pv.get('include_bio', False)}")
|
| 159 |
+
print(f"[PROMPT] system_prompt[:300]: {result[:300]}")
|
| 160 |
return result
|
| 161 |
|
| 162 |
|
|
|
|
| 167 |
pv = cfg["prompt_variant"]
|
| 168 |
return get_seller_system_prompt(
|
| 169 |
personalization=pv["personalization"],
|
|
|
|
| 170 |
title=product.get("title", ""),
|
| 171 |
description=_desc_str(product),
|
| 172 |
features=_feat_str(product),
|
src/ui/components.py
CHANGED
|
@@ -23,39 +23,40 @@ def inject_css() -> None:
|
|
| 23 |
/* ββ Product cards βββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 24 |
.product-card {
|
| 25 |
border-radius: 10px; padding: 1rem 1.25rem; margin-bottom: 0.75rem;
|
|
|
|
| 26 |
}
|
| 27 |
-
.product-card-a { border: 2px solid #2563eb; background: #eff6ff; }
|
| 28 |
-
.product-card-b { border: 2px solid #9333ea; background: #faf5ff; }
|
| 29 |
-
.product-card-single { border: 2px solid #0891b2; background: #ecfeff; }
|
| 30 |
|
| 31 |
.pc-header {
|
| 32 |
display: flex; justify-content: space-between;
|
| 33 |
align-items: flex-start; margin-bottom: 0.6rem; gap: 1rem;
|
| 34 |
}
|
| 35 |
-
.pc-title { font-size: 1.05rem; font-weight: 700; color: #1a1a2e; line-height: 1.35; flex: 1; }
|
| 36 |
-
.pc-price { font-size: 1.2rem; font-weight: 800; white-space: nowrap; color: #16a34a; }
|
| 37 |
|
| 38 |
.pc-label {
|
| 39 |
display: inline-block; font-size: 0.8rem; font-weight: 700;
|
| 40 |
padding: 0.2rem 0.6rem; border-radius: 99px; margin-bottom: 0.4rem;
|
| 41 |
}
|
| 42 |
-
.pc-label-a { background: #dbeafe; color: #1e40af; }
|
| 43 |
-
.pc-label-b { background: #ede9fe; color: #6b21a8; }
|
| 44 |
-
.pc-label-single { background: #cffafe; color: #155e75; }
|
| 45 |
|
| 46 |
.pc-category-badge {
|
| 47 |
display: inline-block; font-size: 0.7rem; font-weight: 600;
|
| 48 |
padding: 0.12rem 0.5rem; border-radius: 99px; margin-left: 0.4rem;
|
| 49 |
-
background: #f1f5f9; color: #475569;
|
| 50 |
}
|
| 51 |
.pc-section { margin-top: 0.5rem; }
|
| 52 |
.pc-section-title {
|
| 53 |
-
font-weight: 600; font-size: 0.82rem; color: #64748b;
|
| 54 |
text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 0.3rem;
|
| 55 |
}
|
| 56 |
-
.pc-desc { font-size: 0.92rem; color: #334155; line-height: 1.6; }
|
| 57 |
-
.pc-list { margin: 0; padding-left: 1.2rem; font-size: 0.92rem; color: #334155; line-height: 1.5; }
|
| 58 |
-
.pc-list li { margin-bottom: 0.25rem; }
|
| 59 |
|
| 60 |
/* ββ VS divider ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 61 |
.vs-divider {
|
|
@@ -70,10 +71,29 @@ def inject_css() -> None:
|
|
| 70 |
|
| 71 |
/* ββ Chat bubbles ββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 72 |
.chat-wrap { max-height: 480px; overflow-y: auto; margin-bottom: 1rem; padding-right: 4px; }
|
| 73 |
-
.bubble {
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
/* ββ Section headings on background page ββββββββββββββββββββββββββββββ */
|
| 79 |
hr.section-divider { border: none; border-top: 2px solid #e2e8f0; margin: 1.5rem 0 1rem 0; }
|
|
@@ -239,12 +259,6 @@ def render_chat_history(turns: list, study_type: str) -> None:
|
|
| 239 |
)
|
| 240 |
html += "</div>"
|
| 241 |
st.markdown(html, unsafe_allow_html=True)
|
| 242 |
-
st.components.v1.html("""
|
| 243 |
-
<script>
|
| 244 |
-
const chatWraps = window.parent.document.querySelectorAll('.chat-wrap');
|
| 245 |
-
chatWraps.forEach(el => el.scrollTop = el.scrollHeight);
|
| 246 |
-
</script>
|
| 247 |
-
""", height=0)
|
| 248 |
|
| 249 |
|
| 250 |
# ββ Rating / familiarity helpers ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 23 |
/* ββ Product cards βββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 24 |
.product-card {
|
| 25 |
border-radius: 10px; padding: 1rem 1.25rem; margin-bottom: 0.75rem;
|
| 26 |
+
color: #1a1a2e !important; /* force dark text regardless of theme */
|
| 27 |
}
|
| 28 |
+
.product-card-a { border: 2px solid #2563eb; background: #eff6ff !important; }
|
| 29 |
+
.product-card-b { border: 2px solid #9333ea; background: #faf5ff !important; }
|
| 30 |
+
.product-card-single { border: 2px solid #0891b2; background: #ecfeff !important; }
|
| 31 |
|
| 32 |
.pc-header {
|
| 33 |
display: flex; justify-content: space-between;
|
| 34 |
align-items: flex-start; margin-bottom: 0.6rem; gap: 1rem;
|
| 35 |
}
|
| 36 |
+
.pc-title { font-size: 1.05rem; font-weight: 700; color: #1a1a2e !important; line-height: 1.35; flex: 1; }
|
| 37 |
+
.pc-price { font-size: 1.2rem; font-weight: 800; white-space: nowrap; color: #16a34a !important; }
|
| 38 |
|
| 39 |
.pc-label {
|
| 40 |
display: inline-block; font-size: 0.8rem; font-weight: 700;
|
| 41 |
padding: 0.2rem 0.6rem; border-radius: 99px; margin-bottom: 0.4rem;
|
| 42 |
}
|
| 43 |
+
.pc-label-a { background: #dbeafe !important; color: #1e40af !important; }
|
| 44 |
+
.pc-label-b { background: #ede9fe !important; color: #6b21a8 !important; }
|
| 45 |
+
.pc-label-single { background: #cffafe !important; color: #155e75 !important; }
|
| 46 |
|
| 47 |
.pc-category-badge {
|
| 48 |
display: inline-block; font-size: 0.7rem; font-weight: 600;
|
| 49 |
padding: 0.12rem 0.5rem; border-radius: 99px; margin-left: 0.4rem;
|
| 50 |
+
background: #f1f5f9 !important; color: #475569 !important;
|
| 51 |
}
|
| 52 |
.pc-section { margin-top: 0.5rem; }
|
| 53 |
.pc-section-title {
|
| 54 |
+
font-weight: 600; font-size: 0.82rem; color: #64748b !important;
|
| 55 |
text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 0.3rem;
|
| 56 |
}
|
| 57 |
+
.pc-desc { font-size: 0.92rem; color: #334155 !important; line-height: 1.6; }
|
| 58 |
+
.pc-list { margin: 0; padding-left: 1.2rem; font-size: 0.92rem; color: #334155 !important; line-height: 1.5; }
|
| 59 |
+
.pc-list li { margin-bottom: 0.25rem; color: #334155 !important; }
|
| 60 |
|
| 61 |
/* ββ VS divider ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 62 |
.vs-divider {
|
|
|
|
| 71 |
|
| 72 |
/* ββ Chat bubbles ββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 73 |
.chat-wrap { max-height: 480px; overflow-y: auto; margin-bottom: 1rem; padding-right: 4px; }
|
| 74 |
+
.bubble {
|
| 75 |
+
padding: 0.65rem 0.9rem; border-radius: 12px; margin-bottom: 0.55rem;
|
| 76 |
+
font-size: 0.93rem; line-height: 1.55;
|
| 77 |
+
color: #1a1a2e !important; /* force dark text regardless of theme */
|
| 78 |
+
}
|
| 79 |
+
.bubble-ai {
|
| 80 |
+
background: #eff6ff !important;
|
| 81 |
+
border: 1px solid #93c5fd;
|
| 82 |
+
margin-right: 8%;
|
| 83 |
+
color: #1a1a2e !important;
|
| 84 |
+
}
|
| 85 |
+
.bubble-user {
|
| 86 |
+
background: #f0fdf4 !important;
|
| 87 |
+
border: 1px solid #86efac;
|
| 88 |
+
margin-left: 8%;
|
| 89 |
+
text-align: right;
|
| 90 |
+
color: #1a1a2e !important;
|
| 91 |
+
}
|
| 92 |
+
.bubble-meta {
|
| 93 |
+
font-size: 0.73rem;
|
| 94 |
+
color: #64748b !important;
|
| 95 |
+
margin-bottom: 0.15rem;
|
| 96 |
+
}
|
| 97 |
|
| 98 |
/* ββ Section headings on background page ββββββββββββββββββββββββββββββ */
|
| 99 |
hr.section-divider { border: none; border-top: 2px solid #e2e8f0; margin: 1.5rem 0 1rem 0; }
|
|
|
|
| 259 |
)
|
| 260 |
html += "</div>"
|
| 261 |
st.markdown(html, unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
|
| 263 |
|
| 264 |
# ββ Rating / familiarity helpers ββββββββββββββββββββββββββββββββββββββββββββββ
|
src/ui/screens_preference.py
CHANGED
|
@@ -95,17 +95,26 @@ def screen_pair_intro(s: dict, cfg: dict) -> None:
|
|
| 95 |
fam_b = fam_b or fam_b_opts[0]
|
| 96 |
pre_val = pre_val or choices[3] # Neutral (4)
|
| 97 |
|
| 98 |
-
pre_int
|
| 99 |
-
demo_str = format_demographics(s["demographics"])
|
| 100 |
|
| 101 |
# ββ Per-item config (model + prompt variant assigned at session init) β
|
| 102 |
item_cfg = {
|
| 103 |
**cfg,
|
| 104 |
"prompt_variant": item.get("prompt_variant", {}),
|
| 105 |
"model_name": item.get("model_name", ""),
|
| 106 |
-
"sampler_path": item.get("sampler_path", ""),
|
| 107 |
}
|
| 108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
# ββ Build prompts βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 110 |
system_prompt = build_seller_system_prompt_preference(item, item_cfg, demo_str)
|
| 111 |
opening_msg = opening_message_preference(item)
|
|
|
|
| 95 |
fam_b = fam_b or fam_b_opts[0]
|
| 96 |
pre_val = pre_val or choices[3] # Neutral (4)
|
| 97 |
|
| 98 |
+
pre_int = parse_rating(pre_val)
|
|
|
|
| 99 |
|
| 100 |
# ββ Per-item config (model + prompt variant assigned at session init) β
|
| 101 |
item_cfg = {
|
| 102 |
**cfg,
|
| 103 |
"prompt_variant": item.get("prompt_variant", {}),
|
| 104 |
"model_name": item.get("model_name", ""),
|
| 105 |
+
"sampler_path": item.get("sampler_path", ""),
|
| 106 |
}
|
| 107 |
|
| 108 |
+
# Build the demographics string in whichever format the trained model expects.
|
| 109 |
+
# When include_bio is True we feed the participant's own background answers
|
| 110 |
+
# (movies_criteria / movies_enjoy / movies_avoid) the same way training does.
|
| 111 |
+
include_bio = bool(item_cfg["prompt_variant"].get("include_bio", False))
|
| 112 |
+
demo_str = format_demographics(
|
| 113 |
+
s["demographics"],
|
| 114 |
+
background=s.get("background", {}),
|
| 115 |
+
include_bio=include_bio,
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
# ββ Build prompts βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 119 |
system_prompt = build_seller_system_prompt_preference(item, item_cfg, demo_str)
|
| 120 |
opening_msg = opening_message_preference(item)
|
study_config.yaml
CHANGED
|
@@ -31,10 +31,10 @@ categories:
|
|
| 31 |
model_variants:
|
| 32 |
- name: base
|
| 33 |
model_name: "meta-llama/Llama-3.1-8B-Instruct"
|
| 34 |
-
sampler_path: "tinker://
|
| 35 |
prompt_variant:
|
| 36 |
-
personalization:
|
| 37 |
-
|
| 38 |
count: 2 # items using this variant for odd-numbered users
|
| 39 |
# counts swap on alternating users:
|
| 40 |
|
|
@@ -46,8 +46,8 @@ min_turns: 3 # Minimum exchanges before "done" button is enab
|
|
| 46 |
max_turns: 3 # Hard cap; input is disabled after this many exchanges
|
| 47 |
|
| 48 |
# Prolific
|
| 49 |
-
prolific_completion_code: "
|
| 50 |
-
prolific_study_id: "
|
| 51 |
|
| 52 |
# HuggingFace dataset repo where results (JSON + CSV) are uploaded
|
| 53 |
-
output_dataset_repo: "ehejin/user_study-preference-
|
|
|
|
| 31 |
model_variants:
|
| 32 |
- name: base
|
| 33 |
model_name: "meta-llama/Llama-3.1-8B-Instruct"
|
| 34 |
+
sampler_path: "tinker://90528292-6961-5d83-b389-d70a8c1ba6a6:train:0/sampler_weights/000200"
|
| 35 |
prompt_variant:
|
| 36 |
+
personalization: false
|
| 37 |
+
include_bio: false
|
| 38 |
count: 2 # items using this variant for odd-numbered users
|
| 39 |
# counts swap on alternating users:
|
| 40 |
|
|
|
|
| 46 |
max_turns: 3 # Hard cap; input is disabled after this many exchanges
|
| 47 |
|
| 48 |
# Prolific
|
| 49 |
+
prolific_completion_code: "C3QSGJK3"
|
| 50 |
+
prolific_study_id: "69fd3473f636a4e92454c022"
|
| 51 |
|
| 52 |
# HuggingFace dataset repo where results (JSON + CSV) are uploaded
|
| 53 |
+
output_dataset_repo: "ehejin/user_study-preference-personalized_0505_NP1"
|