Eric Xu commited on
Commit
0694cd1
·
unverified ·
1 Parent(s): 8b7e05d

Auto-suggest context-aware segments from entity via LLM

Browse files

The cohort step now calls the LLM to suggest segments that match the
entity domain (dating profile → partner types, product → buyer personas,
resume → hiring managers). Replaces hardcoded product-focused defaults.

- Add /api/suggest-segments endpoint
- Auto-call on step 2 entry, with Re-suggest button for manual refresh

Files changed (2) hide show
  1. web/app.py +46 -0
  2. web/static/index.html +35 -10
web/app.py CHANGED
@@ -88,6 +88,11 @@ class CounterfactualConfig(BaseModel):
88
  parallel: int = 5
89
 
90
 
 
 
 
 
 
91
  # ── Routes ────────────────────────────────────────────────────────────────
92
 
93
  @app.get("/")
@@ -134,6 +139,47 @@ async def get_session(sid: str):
134
  }
135
 
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  @app.post("/api/cohort/generate")
138
  async def generate_cohort_endpoint(config: CohortConfig):
139
  """Generate an LLM cohort and attach to a new session."""
 
88
  parallel: int = 5
89
 
90
 
91
+ class SuggestSegmentsInput(BaseModel):
92
+ entity_text: str
93
+ audience_context: str
94
+
95
+
96
  # ── Routes ────────────────────────────────────────────────────────────────
97
 
98
  @app.get("/")
 
139
  }
140
 
141
 
142
+ @app.post("/api/suggest-segments")
143
+ async def suggest_segments(input: SuggestSegmentsInput):
144
+ """Use LLM to suggest audience segments based on entity and context."""
145
+ client = get_client()
146
+ model = get_model()
147
+
148
+ prompt = f"""Given this entity and audience context, suggest 4-5 evaluator segments.
149
+ Each segment should represent a distinct perspective that would evaluate this entity differently.
150
+
151
+ Entity:
152
+ {input.entity_text[:2000]}
153
+
154
+ Audience context: {input.audience_context}
155
+
156
+ Return JSON:
157
+ {{
158
+ "segments": [
159
+ {{"label": "<concise segment description, 5-10 words>", "count": <6-10>}}
160
+ ]
161
+ }}
162
+
163
+ Make segments specific to THIS domain. For a product, use buyer personas. For a dating profile,
164
+ use different types of potential partners. For a resume, use different hiring managers. Etc.
165
+ Be concrete and relevant — no generic segments."""
166
+
167
+ try:
168
+ resp = client.chat.completions.create(
169
+ model=model,
170
+ messages=[{"role": "user", "content": prompt}],
171
+ response_format={"type": "json_object"},
172
+ max_tokens=1024,
173
+ temperature=0.7,
174
+ )
175
+ content = resp.choices[0].message.content
176
+ content = re.sub(r'<think>[\s\S]*?</think>', '', content).strip()
177
+ data = json.loads(content)
178
+ return data
179
+ except Exception as e:
180
+ raise HTTPException(500, f"Failed to suggest segments: {e}")
181
+
182
+
183
  @app.post("/api/cohort/generate")
184
  async def generate_cohort_endpoint(config: CohortConfig):
185
  """Generate an LLM cohort and attach to a new session."""
web/static/index.html CHANGED
@@ -347,6 +347,7 @@
347
  <div id="segmentsList" class="segments-list"></div>
348
  <div class="btn-row">
349
  <button class="secondary" onclick="addSegment()">+ Add segment</button>
 
350
  <button onclick="generateCohort()" id="genCohortBtn">Generate cohort</button>
351
  </div>
352
 
@@ -593,16 +594,9 @@ async function init() {
593
  badge.className = 'config-badge warn';
594
  }
595
 
596
- // Default segments
597
- addSegment('Early adopter, tech-savvy', 8);
598
- addSegment('Mainstream user, non-technical', 8);
599
- addSegment('Budget-conscious comparison shopper', 8);
600
- addSegment('Enterprise decision-maker', 8);
601
-
602
- // Default changes
603
- addChange('Add free tier', 'Introduce a generous free plan that lets users try core features with no credit card required.');
604
- addChange('Add social proof', 'Display customer logos, case studies, and specific metrics (e.g., "Used by 5,000 teams") prominently on the page.');
605
- addChange('Lower price by 40%', 'Reduce all paid plan prices by 40% across the board.');
606
  }
607
 
608
  // ── Templates ──
@@ -656,6 +650,37 @@ async function saveEntity() {
656
  if (!desc.value) desc.value = `People evaluating: ${firstLine.replace(/^#+\s*/, '').trim()}`;
657
 
658
  goToStep(2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
  }
660
 
661
  // ── Step 2: Cohort ──
 
347
  <div id="segmentsList" class="segments-list"></div>
348
  <div class="btn-row">
349
  <button class="secondary" onclick="addSegment()">+ Add segment</button>
350
+ <button class="secondary" onclick="suggestSegments()">Re-suggest</button>
351
  <button onclick="generateCohort()" id="genCohortBtn">Generate cohort</button>
352
  </div>
353
 
 
594
  badge.className = 'config-badge warn';
595
  }
596
 
597
+ // Changes will be populated with defaults
598
+ addChange('', '');
599
+ addChange('', '');
 
 
 
 
 
 
 
600
  }
601
 
602
  // ── Templates ──
 
650
  if (!desc.value) desc.value = `People evaluating: ${firstLine.replace(/^#+\s*/, '').trim()}`;
651
 
652
  goToStep(2);
653
+
654
+ // Auto-suggest segments via LLM
655
+ await suggestSegments();
656
+ }
657
+
658
+ async function suggestSegments() {
659
+ const entityText = document.getElementById('entityText').value.trim();
660
+ const audienceCtx = document.getElementById('cohortDesc').value.trim();
661
+ if (!entityText) return;
662
+
663
+ // Clear existing segments and show loading
664
+ const list = document.getElementById('segmentsList');
665
+ list.innerHTML = '<div style="color:var(--text2);font-size:0.85rem;padding:8px">Suggesting segments...</div>';
666
+
667
+ try {
668
+ const resp = await fetch('/api/suggest-segments', {
669
+ method: 'POST',
670
+ headers: {'Content-Type': 'application/json'},
671
+ body: JSON.stringify({entity_text: entityText, audience_context: audienceCtx}),
672
+ });
673
+ const data = await resp.json();
674
+ list.innerHTML = '';
675
+ (data.segments || []).forEach(seg => {
676
+ addSegment(seg.label, seg.count || 8);
677
+ });
678
+ } catch (e) {
679
+ // Fallback to empty segments on error
680
+ list.innerHTML = '';
681
+ addSegment('', 8);
682
+ addSegment('', 8);
683
+ }
684
  }
685
 
686
  // ── Step 2: Cohort ──