Skip to content

Commit c2f591b

Browse files
committed
feat(whatsapp): Add recording indicator for TTS audio synthesis
- Implement `send_recording_indicator` method in WhatsApp base provider - Add recording indicator support for Evolution API provider - Update WhatsApp bot to show recording indicator during TTS audio synthesis - Enhance TTS audio response flow with async synthesis and recording indicator - Improve logging and error handling for recording indicator functionality Adds a visual cue for users during text-to-speech audio generation, providing a more interactive and responsive messaging experience.
1 parent 11ebc39 commit c2f591b

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

agentle/agents/whatsapp/providers/base/whatsapp_provider.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,17 @@ async def send_typing_indicator(self, to: str, duration: int = 3) -> None:
112112
"""
113113
pass
114114

115+
@abstractmethod
116+
async def send_recording_indicator(self, to: str, duration: int = 3) -> None:
117+
"""
118+
Send recording indicator to show the bot is recording audio.
119+
120+
Args:
121+
to: Recipient phone number
122+
duration: Duration in seconds to show recording
123+
"""
124+
pass
125+
115126
@abstractmethod
116127
async def mark_message_as_read(self, message_id: str) -> None:
117128
"""Mark a message as read."""

agentle/agents/whatsapp/providers/evolution/evolution_api_provider.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,68 @@ async def send_typing_indicator(self, to: str, duration: int = 3) -> None:
10881088
},
10891089
)
10901090

1091+
async def send_recording_indicator(self, to: str, duration: int = 3) -> None:
1092+
"""Send recording indicator via Evolution API."""
1093+
logger.debug(f"Sending recording indicator to {to} for {duration}s")
1094+
1095+
try:
1096+
# CRITICAL FIX: Check if there's a stored remoteJid for this contact
1097+
# This is essential for @lid numbers (Brazilian WhatsApp contacts)
1098+
session = await self.get_session(to)
1099+
remote_jid = session.context_data.get("remote_jid") if session else None
1100+
1101+
if remote_jid:
1102+
logger.debug(
1103+
f"🔑 Using stored remoteJid for recording indicator to {to}: {remote_jid}"
1104+
)
1105+
normalized_to = remote_jid
1106+
else:
1107+
normalized_to = self._normalize_phone(to)
1108+
1109+
payload = {
1110+
"number": normalized_to,
1111+
"presence": "recording",
1112+
"delay": duration * 1000,
1113+
"options": {
1114+
"delay": duration * 1000,
1115+
"presence": "recording",
1116+
"number": normalized_to,
1117+
}, # Evolution API expects milliseconds
1118+
}
1119+
1120+
url = self._build_url(
1121+
f"chat/sendPresence/{self.config.instance_name}",
1122+
use_message_prefix=False,
1123+
)
1124+
await self._make_request_with_resilience(
1125+
"POST", url, payload, expected_status=201
1126+
)
1127+
1128+
logger.debug(
1129+
f"Recording indicator sent successfully to {to} for {duration}s",
1130+
extra={
1131+
"to_number": to,
1132+
"normalized_to": normalized_to,
1133+
"duration_seconds": duration,
1134+
},
1135+
)
1136+
1137+
except EvolutionAPIError as e:
1138+
# Recording indicator failures are non-critical
1139+
logger.warning(
1140+
f"Failed to send recording indicator to {to}: {e}",
1141+
extra={"to_number": to, "duration_seconds": duration, "error": str(e)},
1142+
)
1143+
except Exception as e:
1144+
logger.warning(
1145+
f"Failed to send recording indicator to {to}: {type(e).__name__}: {e}",
1146+
extra={
1147+
"to_number": to,
1148+
"duration_seconds": duration,
1149+
"error_type": type(e).__name__,
1150+
},
1151+
)
1152+
10911153
async def mark_message_as_read(self, message_id: str) -> None:
10921154
"""Mark a message as read via Evolution API."""
10931155
logger.debug(f"Marking message as read: {message_id}")

agentle/agents/whatsapp/whatsapp_bot.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2097,8 +2097,17 @@ async def _send_response(
20972097
f"[TTS] Attempting to send audio response to {to} (chance: {self.config.speech_play_chance * 100}%)"
20982098
)
20992099
try:
2100+
# Show recording indicator while synthesizing
2101+
if self.config.typing_indicator:
2102+
logger.debug(
2103+
f"[TTS] Sending recording indicator to {to} during synthesis"
2104+
)
2105+
await self.provider.send_recording_indicator(
2106+
to, self.config.typing_duration
2107+
)
2108+
21002109
# Synthesize speech
2101-
speech_result = await self.tts_provider.synthesize(
2110+
speech_result = await self.tts_provider.synthesize_async(
21022111
response_text, config=self.config.speech_config
21032112
)
21042113

0 commit comments

Comments
 (0)