From 9eaf2fe5e36ac4f8cf0353b211881f44967158a2 Mon Sep 17 00:00:00 2001 From: tcpsyn Date: Sat, 21 Mar 2026 02:03:07 -0600 Subject: [PATCH] Fix avatar misgendering, returning caller overflow, false callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Avatar prefetch checks gender marker, re-fetches on mismatch - Returning callers need 2+ actual calls before re-eligible (was 1) - Promotion rate lowered 10% → 5% to prevent pool flooding - Callback injection skipped for returning callers (already have context) - Show history clarifies "you are NOT that caller" to prevent identity confusion Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/main.py | 29 ++++++++++++++++------------- backend/services/avatars.py | 10 +++++++++- backend/services/regulars.py | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/backend/main.py b/backend/main.py index f7a6277..3132baf 100644 --- a/backend/main.py +++ b/backend/main.py @@ -6361,7 +6361,7 @@ class Session: if random.random() < reaction_chance and best_target: reaction = self._build_specific_reaction(current_bg, best_target) if random.random() < 0.30: - lines.append(f"\nYOU HEARD {best_target.caller_name.upper()} EARLIER and you {reaction}. This is partly why you called — bring it up early and tie it into your story.") + lines.append(f"\nYOU HEARD {best_target.caller_name.upper()} EARLIER ON THE SHOW TONIGHT and you {reaction}. It reminded you of your own situation — bring it up early and tie it into your story. NOTE: You are NOT {best_target.caller_name} — you are a different caller who was listening.") else: lines.append(f"\nYOU HEARD {best_target.caller_name.upper()} EARLIER and you {reaction}. Mention it if it comes up naturally, but your call is about YOUR thing.") else: @@ -7928,16 +7928,19 @@ async def start_call(caller_key: str): audio_service.stop_caller_audio() session.start_call(caller_key) - # Check for callback opportunity — inject callback context into background - callback = _maybe_generate_callback() - if callback: - existing_bg = session.caller_backgrounds.get(caller_key, "") - callback_ctx = f"\n\nCALLBACK: You already called earlier tonight. {callback['callback_reason']}. Reference your earlier call naturally — you're a returning caller with an update." - if isinstance(existing_bg, CallerBackground): - existing_bg.natural_description += callback_ctx - else: - session.caller_backgrounds[caller_key] = existing_bg + callback_ctx - print(f"[Callback] Injected callback context for {CALLER_BASES[caller_key].get('name', caller_key)}") + # Check for callback opportunity — only for non-returning callers + # Returning callers already have their own PREVIOUS CALLS context + base = CALLER_BASES[caller_key] + if not base.get("returning"): + callback = _maybe_generate_callback() + if callback: + existing_bg = session.caller_backgrounds.get(caller_key, "") + callback_ctx = f"\n\nPREVIOUS CALLS:\n- (earlier tonight) {callback['original_summary']}\nYou're calling back with an update — {callback['callback_reason']}. Reference your earlier call naturally." + if isinstance(existing_bg, CallerBackground): + existing_bg.natural_description += callback_ctx + else: + session.caller_backgrounds[caller_key] = existing_bg + callback_ctx + print(f"[Callback] Injected callback context for {base.get('name', caller_key)}") caller = session.caller # This generates the background if needed @@ -8115,8 +8118,8 @@ async def _summarize_ai_call(caller_key: str, caller_name: str, conversation: li if base.get("returning") and base.get("regular_id"): # Update existing regular's call history regular_caller_service.update_after_call(base["regular_id"], summary) - elif len(conversation) >= 8 and random.random() < 0.10: - # 10% chance to promote first-timer with 8+ messages + elif len(conversation) >= 8 and random.random() < 0.05: + # 5% chance to promote first-timer with 8+ messages bg = session.caller_backgrounds.get(caller_key, "") caller_style = session.caller_styles.get(caller_key, "") diff --git a/backend/services/avatars.py b/backend/services/avatars.py index d285d20..bc3706f 100644 --- a/backend/services/avatars.py +++ b/backend/services/avatars.py @@ -65,7 +65,15 @@ class AvatarService: for caller in callers: name = caller.get("name", "") gender = caller.get("gender", "male") - if name and not (AVATAR_DIR / f"{name}.jpg").exists(): + if not name: + continue + g = "female" if gender.lower().startswith("f") else "male" + path = AVATAR_DIR / f"{name}.jpg" + marker = AVATAR_DIR / f"{name}.gender" + # Always call get_or_fetch if: no file, no gender marker, or gender mismatch + if not path.exists() or not marker.exists() or marker.read_text().strip() != g: + if path.exists(): + print(f"[Avatar] Gender mismatch for {name}: cached={marker.read_text().strip() if marker.exists() else '?'}, want={g} — re-fetching") tasks.append(self.get_or_fetch(name, gender)) if not tasks: diff --git a/backend/services/regulars.py b/backend/services/regulars.py index 62e818b..9479834 100644 --- a/backend/services/regulars.py +++ b/backend/services/regulars.py @@ -44,7 +44,7 @@ class RegularCallerService: import random if not self._regulars: return [] - available = [r for r in self._regulars if len(r.get("call_history", [])) > 0] + available = [r for r in self._regulars if len(r.get("call_history", [])) > 1] if not available: return [] return random.sample(available, min(count, len(available)))