Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from livekit.agents import llm, stt
from livekit.plugins import openai

from logic import Mode, admitted_mode, caption_attributes, is_hearing_microphone_audio, spoken_text
from logic import Mode, admitted_mode, caption_attributes, is_hearing_microphone_audio, speech_prompt, spoken_text

log = logging.getLogger(__name__)
server = agents.AgentServer()
Expand Down Expand Up @@ -201,12 +201,13 @@ async def speak(reader: rtc.TextStreamReader, identity: str) -> None:
text = spoken_text(await reader.read_all())
if state.mode_for(identity) is not Mode.DEAF or text is None:
return
attributes = reader.info.attributes or {}
async with state.tts_lock:
started = time.monotonic()
first_frame = True
try:
chat = llm.ChatContext()
chat.add_message(role="user", content=text)
chat.add_message(role="user", content=speech_prompt(text, attributes))
await state.realtime_session.update_chat_ctx(chat)
generation = await state.realtime_session.generate_reply()
async for message in generation.message_stream:
Expand Down
16 changes: 16 additions & 0 deletions agent/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ def spoken_text(text: str) -> str | None:
return value if 0 < len(value) <= 500 else None


def speech_prompt(text: str, attributes: dict[str, str]) -> str:
voices = {
"CALM": "차분하고 편안한 목소리로",
"BRIGHT": "밝고 친근한 목소리로",
"CLEAR": "또박또박 명료한 목소리로",
}
speeds = {
"SLOW": "평소보다 천천히",
"NORMAL": "자연스러운 속도로",
"FAST": "평소보다 빠르게",
}
voice = voices.get(attributes.get("openwindow.voice", ""), voices["CALM"])
speed = speeds.get(attributes.get("openwindow.voice_speed", ""), speeds["NORMAL"])
return f"다음 문장만 그대로, {voice} {speed} 읽어 주세요: {text}"


def caption_attributes(segment_id: str, final: bool, track_id: str) -> dict[str, str]:
return {
"lk.segment_id": segment_id,
Expand Down
15 changes: 14 additions & 1 deletion agent/tests/test_logic.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio

from agent import RoomState
from logic import Mode, admitted_mode, caption_attributes, is_hearing_microphone_audio, mode_from_metadata, spoken_text
from logic import Mode, admitted_mode, caption_attributes, is_hearing_microphone_audio, mode_from_metadata, speech_prompt, spoken_text


def test_admission_reserves_one_slot_and_one_mode_per_identity():
Expand Down Expand Up @@ -31,6 +31,19 @@ def test_text_bound_and_caption_attributes():
}


def test_speech_prompt_uses_preferences_and_safe_defaults():
configured = speech_prompt(
"안녕하세요", {"openwindow.voice": "CLEAR", "openwindow.voice_speed": "SLOW"}
)
assert "또박또박 명료한" in configured
assert "평소보다 천천히" in configured
assert configured.endswith("안녕하세요")

defaulted = speech_prompt("반갑습니다", {"openwindow.voice": "UNKNOWN"})
assert "차분하고 편안한" in defaulted
assert "자연스러운 속도" in defaulted


def test_track_cleanup_cancels_the_transcription_task():
async def check() -> None:
state = RoomState.__new__(RoomState)
Expand Down
Loading