@ctx.room.local_participant.register_rpc_method("artwork_description")
async def artwork_description(data: rtc.RpcInvocationData):
await artwork_handler.handle_artwork_description(data, session)
async def handle_artwork_description(self, data: rtc.RpcInvocationData, session):
"""Handle artwork description RPC request."""
payload = data.payload
payload = json.loads(payload)
tile_number = payload.get("tileNumber")
language = payload.get("language", "en-US") # Default to English
profile = payload.get("profile", "adult") # Default to adult
self.logger.info(
f"Description requested for element {tile_number} by {data.caller_identity}"
)
# Get artwork information from database
artwork_info = self.artwork_db.get_artwork_info(
artwork_id=str(tile_number), language=language, preset=profile
)
if artwork_info:
# Add the user's question to chat context
user_question = f"Tell me about {artwork_info.name} by {artwork_info.artist}"
user_message = ChatMessage(role="user", content=[user_question])
session.history.insert(user_message)
description_message = artwork_info.description
self.logger.info(
f"Retrieved artwork info: {artwork_info.name} by {artwork_info.artist} "
f"(Language: {artwork_info.language}, Preset: {artwork_info.preset})"
)
# Check for precomputed audio file
audio_filename = (
f"{artwork_info.artwork_id}_{artwork_info.language}_{artwork_info.preset}.mp3"
)
audio_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(
file))),
"media",
"precomputed_answers",
"audio",
audio_filename,
)
self.logger.info(f"Checking for precomputed audio file: {audio_path}")
if os.path.exists(audio_path):
try:
# Load MP3 file using pydub
audio_segment = AudioSegment.from_mp3(audio_path)
# Convert to raw audio data
# pydub exports as 16-bit PCM by default
audio_segment = audio_segment.set_frame_rate(48000).set_channels(1)
raw_audio = audio_segment.raw_data
# Get audio properties
sample_rate = audio_segment.frame_rate
num_channels = audio_segment.channels
samples_per_channel = len(audio_segment.get_array_of_samples())
audio_frame = rtc.AudioFrame(
data=raw_audio,
sample_rate=sample_rate,
num_channels=num_channels,
samples_per_channel=samples_per_channel,
)
async def audio_generator():
yield audio_frame
self.logger.info(
f"Using precomputed audio for artwork {tile_number}: {audio_filename}"
)
# Add the assistant's response to chat context first
assistant_message = ChatMessage(role="assistant", content=[description_message])
session.history.insert(assistant_message)
await session.say(
artwork_info.description, audio=audio_generator(), add_to_chat_ctx=False
)
return
except Exception as e:
self.logger.error(f"Error reading MP3 file {audio_path}: {e}")
self.logger.info("Falling back to text-to-speech synthesis")
else:
# Fallback message if artwork not found
user_question = f"Tell me about artwork number {tile_number}"
user_message = ChatMessage(role="user", content=[user_question])
session.history.insert(user_message)
description_message = f"I don't have information about artwork number {tile_number} yet. What would you like to know about it?"
self.logger.warning(f"No artwork information found for ID {tile_number}")
# Add the assistant's response to chat context first
assistant_message = ChatMessage(role="assistant", content=[description_message])
session.history.insert(assistant_message)
await session.say(
description_message,
allow_interruptions=True,
add_to_chat_ctx=False,
)