File size: 7,029 Bytes
6b23da9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6508a6c
6b23da9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ad9cc9
6b23da9
 
 
 
 
 
 
 
 
 
9ad9cc9
6b23da9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d1c998c
6b23da9
9ad9cc9
 
 
 
 
d1c998c
9ad9cc9
 
d1c998c
 
 
 
 
 
 
 
 
 
6b23da9
9ad9cc9
6b23da9
 
9ad9cc9
6b23da9
 
 
 
 
 
 
 
5ae1b70
6b23da9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ae1b70
6b23da9
 
 
 
 
9ad9cc9
 
 
 
 
6b23da9
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
"""
Preference study β€” pair introduction screen.

Participants see Product A and Product B, rate familiarity for both,
choose an initial preference (1–7), and click "Start Chat".

Clicking "Start Chat" initialises the conversation:
  1. AI  (synthetic): preference_initial(pair_overview)
  2. User (synthetic): <choice>N</choice>   where N = pre_rating integer
  3. AI  (model):      first persuasion response

The seller always argues for Product A regardless of the participant's lean.
"""
import time

import streamlit as st

from src.model import call_model
from src.lsp_wrappers import (
    build_seller_system_prompt_preference,
    closing_message_preference,
    format_demographics,
    opening_message_preference,
)
from src.ui.components import (
    familiarity_choices,
    parse_rating,
    rating_choices,
    render_pair_cards,
    render_progress,
)


def screen_pair_intro(s: dict, cfg: dict) -> None:
    idx     = s["current_index"]
    item    = s["items"][idx]
    pair_id = item["pair_id"]

    render_progress(idx + 1, cfg["pairs_per_user"])
    st.markdown("## Product Comparison")
    st.markdown(
        "Please read both products carefully before answering the questions below."
    )

    render_pair_cards(item)
    st.markdown("---")

    # ── Familiarity for Product A ─────────────────────────────────────────────
    cat_a      = item["product_a"].get("category", item.get("category", ""))
    fam_a_opts = familiarity_choices(cat_a)
    fam_a = st.radio(
        f"How familiar are you with **Product A** "
        f"(*{item['product_a'].get('title', '')[:60]}…*)?",
        fam_a_opts,
        index=None,
        key=f"fam_a_{idx}_{pair_id}",
    )

    # ── Familiarity for Product B ─────────────────────────────────────────────
    cat_b      = item["product_b"].get("category", item.get("category", ""))
    fam_b_opts = familiarity_choices(cat_b)
    fam_b = st.radio(
        f"How familiar are you with **Product B** "
        f"(*{item['product_b'].get('title', '')[:60]}…*)?",
        fam_b_opts,
        index=None,
        key=f"fam_b_{idx}_{pair_id}",
    )

    st.markdown("---")

    # ── Initial preference rating ─────────────────────────────────────────────
    choices = rating_choices("preference")
    pre_val = st.radio(
        "Considering these two products, which would you prefer to buy?",
        choices,
        index=None,
        key=f"pre_rating_{idx}_{pair_id}",
    )

    if st.button("Start Chat β†’", type="primary", use_container_width=True):
        if not cfg["debug_mode"]:
            if not fam_a:
                st.error("⚠️ Please rate your familiarity with Product A.")
                return
            if not fam_b:
                st.error("⚠️ Please rate your familiarity with Product B.")
                return
            if not pre_val:
                st.error("⚠️ Please rate your preference before starting the chat.")
                return

        # Defaults for debug mode
        fam_a   = fam_a   or fam_a_opts[0]
        fam_b   = fam_b   or fam_b_opts[0]
        pre_val = pre_val or choices[3]          # Neutral (4)

        pre_int = parse_rating(pre_val)

        # ── Per-item config (model + prompt variant assigned at session init) ─
        item_cfg = {
            **cfg,
            "prompt_variant": item.get("prompt_variant", {}),
            "model_name":     item.get("model_name", ""),
            "sampler_path":   item.get("sampler_path", ""),
        }

        # Build the demographics string in whichever format the trained model expects.
        # When include_bio is True we feed the participant's own background answers
        # (movies_criteria / movies_enjoy / movies_avoid) the same way training does.
        include_bio = bool(item_cfg["prompt_variant"].get("include_bio", False))
        demo_str    = format_demographics(
            s["demographics"],
            background=s.get("background", {}),
            include_bio=include_bio,
        )

        # ── Build prompts ─────────────────────────────────────────────────────
        system_prompt   = build_seller_system_prompt_preference(item, item_cfg, demo_str)
        opening_msg     = opening_message_preference(item)
        user_choice_msg = f"<choice>{pre_int}</choice>"
        closing_msg     = closing_message_preference(item)   # logged only

        # ── First persuasion response from the model ──────────────────────────
        messages = [
            {"role": "system",    "content": system_prompt},
            {"role": "assistant", "content": opening_msg},
            {"role": "user",      "content": user_choice_msg},
        ]
        with st.spinner("Starting conversation…"):
            ai_reply = call_model(messages, item_cfg)

        now = time.time()

        # ── Persist to state ──────────────────────────────────────────────────
        s["items"][idx]["familiarity_a"] = fam_a
        s["items"][idx]["familiarity_b"] = fam_b
        s["items"][idx]["pre_rating"]    = pre_int
        s["items"][idx]["conversation"].update({
            "system_prompt":   system_prompt,
            "closing_message": closing_msg,
            "turns": [
                {
                    "turn_index": 0,
                    "role":       "assistant",
                    "content":    opening_msg,
                    "timestamp":  now,
                    "synthetic":  True,
                },
                {
                    "turn_index": 1,
                    "role":       "user",
                    "content":    user_choice_msg,
                    "timestamp":  now,
                    "synthetic":  True,
                },
                {
                    "turn_index": 2,
                    "role":       "assistant",
                    "content":    ai_reply,
                    "timestamp":  now,
                    "model":      item_cfg["model_name"],
                },
            ],
            "num_turns": 0,
        })

        print(f"[CONV] num turns stored: {len(s['items'][idx]['conversation']['turns'])}")
        print(f"[CONV] turn roles: {[(t['role'], t.get('synthetic')) for t in s['items'][idx]['conversation']['turns']]}")
        print(f"[CONV] turn 0 content[:100]: {s['items'][idx]['conversation']['turns'][0]['content'][:100]}")
        print(f"[CONV] turn 1 content: {s['items'][idx]['conversation']['turns'][1]['content']}")

        s["screen"] = "chat"
        st.rerun()