Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c30a75cc8f | |||
| 90e51698b8 |
+35
-5
@@ -24,7 +24,7 @@ from .config import settings
|
|||||||
from .services.caller_service import CallerService
|
from .services.caller_service import CallerService
|
||||||
from .services.transcription import transcribe_audio
|
from .services.transcription import transcribe_audio
|
||||||
from .services.llm import llm_service
|
from .services.llm import llm_service
|
||||||
from .services.cost_tracker import cost_tracker
|
from .services.cost_tracker import cost_tracker, LLMCallRecord, TTSCallRecord
|
||||||
from .services.tts import generate_speech
|
from .services.tts import generate_speech
|
||||||
from .services.audio import audio_service
|
from .services.audio import audio_service
|
||||||
from .services.stem_recorder import StemRecorder
|
from .services.stem_recorder import StemRecorder
|
||||||
@@ -5314,7 +5314,10 @@ TIME: {time_ctx} {season_ctx}
|
|||||||
{fluency_hint}
|
{fluency_hint}
|
||||||
{f'SOME DETAILS ABOUT THEM: {seed_text}' if seed_text else ''}
|
{f'SOME DETAILS ABOUT THEM: {seed_text}' if seed_text else ''}
|
||||||
{f'CALLER ENERGY: {style_hint}' if style_hint else ''}
|
{f'CALLER ENERGY: {style_hint}' if style_hint else ''}
|
||||||
{f"SHOW THEME: Tonight's show theme is '{session.show_theme}'. This caller might have a story or angle related to this theme — or they might not. Not every caller has to be about the theme, but if their reason for calling can naturally connect to it, lean into that connection. The theme should feel like a through-line, not a mandate." if session.show_theme else ''}
|
{f"""SHOW THEME: Tonight's show theme is '{session.show_theme}'.
|
||||||
|
Most callers tonight are calling BECAUSE of the theme — they heard the host announce it and thought "oh man, I have a story for this." Their reason for calling should be genuinely, specifically connected to the theme. Not a surface-level mention — the theme should be woven into WHY they picked up the phone. Maybe the theme hit a nerve, maybe it reminded them of something wild that happened, maybe they have a hot take or a confession related to it.
|
||||||
|
About 1 in 3 callers can be unrelated to the theme — they just have their own thing going on and called regardless. But the majority should feel like the theme drew them in.
|
||||||
|
When the theme connects, make it SPECIFIC — not "oh yeah I have a story about that" but a concrete situation that naturally ties to '{session.show_theme}'.""" if session.show_theme else ''}
|
||||||
|
|
||||||
Respond with a JSON object containing these fields:
|
Respond with a JSON object containing these fields:
|
||||||
|
|
||||||
@@ -6017,7 +6020,7 @@ def get_caller_prompt(caller: dict, show_history: str = "",
|
|||||||
|
|
||||||
theme_context = ""
|
theme_context = ""
|
||||||
if session.show_theme:
|
if session.show_theme:
|
||||||
theme_context = f"\nSHOW THEME: Tonight's show theme is \"{session.show_theme}\". You're aware of the theme — the host mentioned it at the top of the show. If your story or situation connects to it, you might bring it up naturally. But don't force it. Not every caller has to be about the theme. If the host steers you toward the theme, go with it.\n"
|
theme_context = f"""\nSHOW THEME: Tonight's theme is \"{session.show_theme}\". If your story connects to this theme, OWN IT — you called because you heard the theme and knew you had to share. Mention the theme connection early, be enthusiastic about it. You're not just aware of the theme, you're excited that it's YOUR night to call. If the host brings up the theme, engage with energy. If your story doesn't relate to the theme, that's fine — just be yourself and tell your story.\n"""
|
||||||
|
|
||||||
now = datetime.now(_MST)
|
now = datetime.now(_MST)
|
||||||
date_str = now.strftime("%A, %B %d")
|
date_str = now.strftime("%A, %B %d")
|
||||||
@@ -6592,6 +6595,10 @@ def _save_checkpoint():
|
|||||||
"relationship_context": session.relationship_context,
|
"relationship_context": session.relationship_context,
|
||||||
"intern_monitoring": session.intern_monitoring,
|
"intern_monitoring": session.intern_monitoring,
|
||||||
"costs": cost_tracker.get_live_summary(),
|
"costs": cost_tracker.get_live_summary(),
|
||||||
|
"cost_records": {
|
||||||
|
"llm": [asdict(r) for r in cost_tracker.llm_records],
|
||||||
|
"tts": [asdict(r) for r in cost_tracker.tts_records],
|
||||||
|
},
|
||||||
"saved_at": time.time(),
|
"saved_at": time.time(),
|
||||||
}
|
}
|
||||||
with open(CHECKPOINT_FILE, "w") as f:
|
with open(CHECKPOINT_FILE, "w") as f:
|
||||||
@@ -6639,6 +6646,28 @@ def _load_checkpoint() -> bool:
|
|||||||
CALLER_BASES[key]["voice"] = snapshot["voice"]
|
CALLER_BASES[key]["voice"] = snapshot["voice"]
|
||||||
CALLER_BASES[key]["returning"] = snapshot.get("returning", False)
|
CALLER_BASES[key]["returning"] = snapshot.get("returning", False)
|
||||||
CALLER_BASES[key]["regular_id"] = snapshot.get("regular_id")
|
CALLER_BASES[key]["regular_id"] = snapshot.get("regular_id")
|
||||||
|
# Restore cost tracker records
|
||||||
|
cost_records = data.get("cost_records", {})
|
||||||
|
if cost_records:
|
||||||
|
cost_tracker.reset()
|
||||||
|
for r in cost_records.get("llm", []):
|
||||||
|
cost_tracker.llm_records.append(LLMCallRecord(**r))
|
||||||
|
for r in cost_records.get("tts", []):
|
||||||
|
cost_tracker.tts_records.append(TTSCallRecord(**r))
|
||||||
|
# Rebuild running totals from restored records
|
||||||
|
for r in cost_tracker.llm_records:
|
||||||
|
cost_tracker._llm_cost += r.cost_usd
|
||||||
|
cost_tracker._llm_calls += 1
|
||||||
|
cost_tracker._prompt_tokens += r.prompt_tokens
|
||||||
|
cost_tracker._completion_tokens += r.completion_tokens
|
||||||
|
cost_tracker._total_tokens += r.total_tokens
|
||||||
|
cat = cost_tracker._by_category.setdefault(r.category, {"cost": 0.0, "calls": 0, "tokens": 0})
|
||||||
|
cat["cost"] += r.cost_usd
|
||||||
|
cat["calls"] += 1
|
||||||
|
cat["tokens"] += r.total_tokens
|
||||||
|
for r in cost_tracker.tts_records:
|
||||||
|
cost_tracker._tts_cost += r.cost_usd
|
||||||
|
print(f"[Checkpoint] Restored {len(cost_tracker.llm_records)} LLM + {len(cost_tracker.tts_records)} TTS cost records")
|
||||||
mins = age / 60
|
mins = age / 60
|
||||||
print(f"[Checkpoint] Restored session {session.id} ({len(session.call_history)} calls, {mins:.0f}m old)")
|
print(f"[Checkpoint] Restored session {session.id} ({len(session.call_history)} calls, {mins:.0f}m old)")
|
||||||
return True
|
return True
|
||||||
@@ -8026,8 +8055,9 @@ def _trim_to_sentences(text: str, max_sentences: int) -> str:
|
|||||||
"""Hard-trim response to at most max_sentences sentences."""
|
"""Hard-trim response to at most max_sentences sentences."""
|
||||||
if not text:
|
if not text:
|
||||||
return text
|
return text
|
||||||
# Split on sentence-ending punctuation, keeping the delimiter
|
# Split on sentence-ending punctuation, keeping the delimiter.
|
||||||
parts = re.split(r'(?<=[.!?])\s+', text.strip())
|
# Negative lookbehind avoids splitting on common abbreviations (Mr. Mrs. Ms. Dr. St. etc.)
|
||||||
|
parts = re.split(r'(?<!Mr)(?<!Mrs)(?<!Ms)(?<!Dr)(?<!St)(?<!Jr)(?<!Sr)(?<!vs)(?<![A-Z])(?<=[.!?])\s+', text.strip())
|
||||||
if len(parts) <= max_sentences:
|
if len(parts) <= max_sentences:
|
||||||
return text
|
return text
|
||||||
trimmed = ' '.join(parts[:max_sentences])
|
trimmed = ' '.join(parts[:max_sentences])
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ YOUR BACKGROUND: Communications degree from NMSU. This is your first day on the
|
|||||||
YOUR PERSONALITY:
|
YOUR PERSONALITY:
|
||||||
- You are a weird little dude. Kinda creepy, very funny, awkward, and surprisingly sharp. You give off a vibe that something is slightly off about you but people can't quite place it. But underneath it all, you are genuinely lovable. You have a good heart. You root for people. You get excited for callers. You care about the show. People should hear you and think "this guy is insane" and also "I love this guy." You are the kind of person who is impossible not to root for even when you're being deeply strange.
|
- You are a weird little dude. Kinda creepy, very funny, awkward, and surprisingly sharp. You give off a vibe that something is slightly off about you but people can't quite place it. But underneath it all, you are genuinely lovable. You have a good heart. You root for people. You get excited for callers. You care about the show. People should hear you and think "this guy is insane" and also "I love this guy." You are the kind of person who is impossible not to root for even when you're being deeply strange.
|
||||||
- You overshare constantly. You'll drop deeply personal, uncomfortably specific details about your life — sexual history, bizarre habits, unsettling childhood memories — and then keep going like nothing happened. You are genuinely vulnerable and honest about the deepest, weirdest parts of yourself. You don't do this for shock value. You just have no filter and no shame. This vulnerability is what makes you endearing — you're not performing, you're just being yourself, and yourself happens to be a lot.
|
- You overshare constantly. You'll drop deeply personal, uncomfortably specific details about your life — sexual history, bizarre habits, unsettling childhood memories — and then keep going like nothing happened. You are genuinely vulnerable and honest about the deepest, weirdest parts of yourself. You don't do this for shock value. You just have no filter and no shame. This vulnerability is what makes you endearing — you're not performing, you're just being yourself, and yourself happens to be a lot.
|
||||||
- You start explanations with "So basically..." and end them with "...if that makes sense."
|
- You start explanations with "So basically..." and occasionally end them with "...if that makes sense." Use that phrase sparingly — once per show at most, not every response.
|
||||||
- You say "actually" when correcting things. You use "per se" slightly wrong. You say "ironically" about things that are not ironic.
|
- You say "actually" when correcting things. You use "per se" slightly wrong. You say "ironically" about things that are not ironic.
|
||||||
- You are NOT a comedian. You are funny because you are sincere, specific, and deeply strange. You state disturbing or absurd things with complete seriousness. You have strong opinions about low-stakes things. You occasionally say something devastating without realizing it.
|
- You are NOT a comedian. You are funny because you are sincere, specific, and deeply strange. You state disturbing or absurd things with complete seriousness. You have strong opinions about low-stakes things. You occasionally say something devastating without realizing it.
|
||||||
- When you accidentally reveal something dark or sad, you move past it immediately like it's nothing. "Yeah, my landlord's selling the building so I might have to — anyway, it says here that..."
|
- When you accidentally reveal something dark or sad, you move past it immediately like it's nothing. "Yeah, my landlord's selling the building so I might have to — anyway, it says here that..."
|
||||||
@@ -52,7 +52,8 @@ HOW YOU INTERJECT:
|
|||||||
WHEN LUKE ASKS YOU TO LOOK SOMETHING UP:
|
WHEN LUKE ASKS YOU TO LOOK SOMETHING UP:
|
||||||
- Respond like you're already doing it: "Yeah, one sec..." or "Pulling that up..."
|
- Respond like you're already doing it: "Yeah, one sec..." or "Pulling that up..."
|
||||||
- Deliver the info slightly too formally, like you're reading. Then rephrase in normal language if Luke seems confused.
|
- Deliver the info slightly too formally, like you're reading. Then rephrase in normal language if Luke seems confused.
|
||||||
- If you can't find it or don't know: say so. "I'm not finding anything on that" or "I don't actually know." You do not bluff.
|
- If you can't find it or don't know and Luke ASKED you directly: say so briefly. "I'm not finding anything on that" or "I don't actually know." You do not bluff.
|
||||||
|
- If you looked something up on your own (monitoring, interjecting) and couldn't find anything: just stay quiet. Do NOT announce failed lookups. Nobody wants to hear "I looked for X but couldn't find anything." If you have nothing useful, say nothing.
|
||||||
- Occasionally you already know the answer because you looked it up before being asked. This is one of your best qualities.
|
- Occasionally you already know the answer because you looked it up before being asked. This is one of your best qualities.
|
||||||
|
|
||||||
WHAT YOU KNOW:
|
WHAT YOU KNOW:
|
||||||
@@ -447,6 +448,15 @@ class InternService:
|
|||||||
if not text or "NOTHING_TO_ADD" in text:
|
if not text or "NOTHING_TO_ADD" in text:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Suppress interjections that are just announcing failed lookups
|
||||||
|
failed_phrases = ["couldn't find", "could not find", "not finding anything",
|
||||||
|
"no results", "didn't find", "wasn't able to find",
|
||||||
|
"couldn't locate", "no information on"]
|
||||||
|
text_lower = text.lower()
|
||||||
|
if any(phrase in text_lower for phrase in failed_phrases):
|
||||||
|
print(f"[Intern] Suppressed failed-lookup interjection: {text[:60]}...")
|
||||||
|
return None
|
||||||
|
|
||||||
if tool_calls:
|
if tool_calls:
|
||||||
entry = {
|
entry = {
|
||||||
"question": "(interjection)",
|
"question": "(interjection)",
|
||||||
|
|||||||
+3
-1
@@ -1119,7 +1119,9 @@ def post_to_social(metadata: dict, episode_slug: str, image_path: str = None):
|
|||||||
short += f"\n\n{hashtags}"
|
short += f"\n\n{hashtags}"
|
||||||
content = short[:max_len]
|
content = short[:max_len]
|
||||||
|
|
||||||
settings = {"post_type": "post"}
|
settings = {"__type": platform, "post_type": "post"}
|
||||||
|
if platform == "x":
|
||||||
|
settings["who_can_reply_post"] = "everyone"
|
||||||
if "channel" in intg_config:
|
if "channel" in intg_config:
|
||||||
settings["channel"] = intg_config["channel"]
|
settings["channel"] = intg_config["channel"]
|
||||||
|
|
||||||
|
|||||||
@@ -769,27 +769,32 @@ local function phase2_normalize(dialog_regions, ad_regions, ident_regions, dialo
|
|||||||
end
|
end
|
||||||
|
|
||||||
log("Phase 2: Dialog RMS = " .. string.format("%.1f", dialog_rms_db) .. " dBFS")
|
log("Phase 2: Dialog RMS = " .. string.format("%.1f", dialog_rms_db) .. " dBFS")
|
||||||
local dialog_db = dialog_rms_db
|
|
||||||
|
-- Ads/idents are pre-compressed dense audio, so they sound louder than dialog
|
||||||
|
-- at the same RMS. Target a few dB below dialog to match perceived loudness.
|
||||||
|
local AD_IDENT_OFFSET_DB = -4
|
||||||
|
local ad_ident_target = dialog_rms_db + AD_IDENT_OFFSET_DB
|
||||||
|
log("Phase 2: AD/IDENT target = " .. string.format("%.1f", ad_ident_target) .. " dBFS (" .. AD_IDENT_OFFSET_DB .. "dB offset from dialog)")
|
||||||
|
|
||||||
if #ad_regions > 0 then
|
if #ad_regions > 0 then
|
||||||
progress_detail = "Ads"
|
progress_detail = "Ads"
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
log("Phase 2: Normalizing " .. #ad_regions .. " AD region(s)...")
|
log("Phase 2: Normalizing " .. #ad_regions .. " AD region(s)...")
|
||||||
normalize_track_regions(ADS_TRACK, ad_regions, dialog_db)
|
normalize_track_regions(ADS_TRACK, ad_regions, ad_ident_target)
|
||||||
end
|
end
|
||||||
if #ident_regions > 0 then
|
if #ident_regions > 0 then
|
||||||
progress_detail = "Idents"
|
progress_detail = "Idents"
|
||||||
progress_pct = 0.33
|
progress_pct = 0.33
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
log("Phase 2: Normalizing " .. #ident_regions .. " IDENT region(s)...")
|
log("Phase 2: Normalizing " .. #ident_regions .. " IDENT region(s)...")
|
||||||
normalize_track_regions(IDENTS_TRACK, ident_regions, dialog_db)
|
normalize_track_regions(IDENTS_TRACK, ident_regions, ad_ident_target)
|
||||||
end
|
end
|
||||||
|
|
||||||
progress_detail = "Music"
|
progress_detail = "Music"
|
||||||
progress_pct = 0.66
|
progress_pct = 0.66
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
log("Phase 2: Normalizing music track...")
|
log("Phase 2: Normalizing music track...")
|
||||||
normalize_music_track(dialog_regions, dialog_db)
|
normalize_music_track(dialog_regions, dialog_rms_db)
|
||||||
progress_pct = 1.0
|
progress_pct = 1.0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user