ruff linting
This commit is contained in:
@@ -9,7 +9,7 @@ Features:
|
|||||||
- Input/output variables for Architect flow integration
|
- Input/output variables for Architect flow integration
|
||||||
- DTMF event support
|
- DTMF event support
|
||||||
- Barge-in (interruption) events
|
- Barge-in (interruption) events
|
||||||
- Pause/resume support for hold scenarios
|
- Pause/resume support for hold scenarios (optional)
|
||||||
|
|
||||||
Protocol Reference:
|
Protocol Reference:
|
||||||
- https://developer.genesys.cloud/devapps/audiohook
|
- https://developer.genesys.cloud/devapps/audiohook
|
||||||
@@ -49,6 +49,7 @@ from pipecat.serializers.base_serializer import FrameSerializer
|
|||||||
|
|
||||||
class AudioHookMessageType(str, Enum):
|
class AudioHookMessageType(str, Enum):
|
||||||
"""AudioHook protocol message types."""
|
"""AudioHook protocol message types."""
|
||||||
|
|
||||||
OPEN = "open"
|
OPEN = "open"
|
||||||
OPENED = "opened"
|
OPENED = "opened"
|
||||||
CLOSE = "close"
|
CLOSE = "close"
|
||||||
@@ -65,15 +66,17 @@ class AudioHookMessageType(str, Enum):
|
|||||||
|
|
||||||
class AudioHookChannel(str, Enum):
|
class AudioHookChannel(str, Enum):
|
||||||
"""AudioHook audio channel configuration."""
|
"""AudioHook audio channel configuration."""
|
||||||
|
|
||||||
EXTERNAL = "external" # Customer audio only (mono)
|
EXTERNAL = "external" # Customer audio only (mono)
|
||||||
INTERNAL = "internal" # Agent audio only (mono)
|
INTERNAL = "internal" # Agent audio only (mono)
|
||||||
BOTH = "both" # Stereo: external=left, internal=right
|
BOTH = "both" # Stereo: external=left, internal=right
|
||||||
|
|
||||||
|
|
||||||
class AudioHookMediaFormat(str, Enum):
|
class AudioHookMediaFormat(str, Enum):
|
||||||
"""Supported audio formats."""
|
"""Supported audio formats."""
|
||||||
|
|
||||||
PCMU = "PCMU" # μ-law, 8kHz
|
PCMU = "PCMU" # μ-law, 8kHz
|
||||||
L16 = "L16" # 16-bit linear PCM, 8kHz
|
L16 = "L16" # 16-bit linear PCM, 8kHz
|
||||||
|
|
||||||
|
|
||||||
class GenesysAudioHookSerializer(FrameSerializer):
|
class GenesysAudioHookSerializer(FrameSerializer):
|
||||||
@@ -200,7 +203,6 @@ class GenesysAudioHookSerializer(FrameSerializer):
|
|||||||
self._register_event_handler("on_error")
|
self._register_event_handler("on_error")
|
||||||
self._register_event_handler("on_dtmf")
|
self._register_event_handler("on_dtmf")
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session_id(self) -> str:
|
def session_id(self) -> str:
|
||||||
"""Get the Genesys AudioHook session ID generated by the serializer."""
|
"""Get the Genesys AudioHook session ID generated by the serializer."""
|
||||||
@@ -477,11 +479,7 @@ class GenesysAudioHookSerializer(FrameSerializer):
|
|||||||
"""
|
"""
|
||||||
msg = self._create_message(
|
msg = self._create_message(
|
||||||
AudioHookMessageType.EVENT,
|
AudioHookMessageType.EVENT,
|
||||||
parameters={
|
parameters={"entities": [{"type": "barge_in", "data": {}}]},
|
||||||
"entities": [
|
|
||||||
{"type": "barge_in", "data": {}}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug("🔇 Barge-in event sent to Genesys")
|
logger.debug("🔇 Barge-in event sent to Genesys")
|
||||||
@@ -572,7 +570,11 @@ class GenesysAudioHookSerializer(FrameSerializer):
|
|||||||
the frame type is not handled or session is not open.
|
the frame type is not handled or session is not open.
|
||||||
"""
|
"""
|
||||||
if isinstance(frame, (EndFrame, CancelFrame)):
|
if isinstance(frame, (EndFrame, CancelFrame)):
|
||||||
return json.dumps(self.create_disconnect_message(output_variables=self.output_variables, reason="completed"))
|
return json.dumps(
|
||||||
|
self.create_disconnect_message(
|
||||||
|
output_variables=self.output_variables, reason="completed"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
elif isinstance(frame, AudioRawFrame):
|
elif isinstance(frame, AudioRawFrame):
|
||||||
if not self._is_open or self._is_paused:
|
if not self._is_open or self._is_paused:
|
||||||
@@ -670,7 +672,9 @@ class GenesysAudioHookSerializer(FrameSerializer):
|
|||||||
# For PCMU, each sample is 1 byte
|
# For PCMU, each sample is 1 byte
|
||||||
# Extract only bytes at even positions (left channel = external)
|
# Extract only bytes at even positions (left channel = external)
|
||||||
audio_data = bytes(data[i] for i in range(0, len(data), 2))
|
audio_data = bytes(data[i] for i in range(0, len(data), 2))
|
||||||
logger.debug(f"🔊 Stereo audio: {original_len} bytes → {len(audio_data)} bytes (external channel)")
|
logger.debug(
|
||||||
|
f"🔊 Stereo audio: {original_len} bytes → {len(audio_data)} bytes (external channel)"
|
||||||
|
)
|
||||||
|
|
||||||
if self._params.media_format == AudioHookMediaFormat.PCMU:
|
if self._params.media_format == AudioHookMediaFormat.PCMU:
|
||||||
# Convert μ-law at 8kHz to PCM at pipeline rate
|
# Convert μ-law at 8kHz to PCM at pipeline rate
|
||||||
@@ -699,7 +703,6 @@ class GenesysAudioHookSerializer(FrameSerializer):
|
|||||||
|
|
||||||
return audio_frame
|
return audio_frame
|
||||||
|
|
||||||
|
|
||||||
async def _handle_control_message(self, message: Dict[str, Any]) -> Frame | None:
|
async def _handle_control_message(self, message: Dict[str, Any]) -> Frame | None:
|
||||||
"""Handle a JSON control message from Genesys.
|
"""Handle a JSON control message from Genesys.
|
||||||
|
|
||||||
@@ -783,7 +786,9 @@ class GenesysAudioHookSerializer(FrameSerializer):
|
|||||||
if media_list and isinstance(media_list, list) and len(media_list) > 0:
|
if media_list and isinstance(media_list, list) and len(media_list) > 0:
|
||||||
audio_media: Dict[str, Any] = media_list[0] # Get first media entry
|
audio_media: Dict[str, Any] = media_list[0] # Get first media entry
|
||||||
channels = audio_media.get("channels", [])
|
channels = audio_media.get("channels", [])
|
||||||
logger.debug(f"📡 Genesys audio config: format={audio_media.get('format')}, channels={channels}, rate={audio_media.get('rate')}")
|
logger.debug(
|
||||||
|
f"📡 Genesys audio config: format={audio_media.get('format')}, channels={channels}, rate={audio_media.get('rate')}"
|
||||||
|
)
|
||||||
# channels is a list like ["external"] or ["external", "internal"]
|
# channels is a list like ["external"] or ["external", "internal"]
|
||||||
if isinstance(channels, list):
|
if isinstance(channels, list):
|
||||||
if "external" in channels and "internal" in channels:
|
if "external" in channels and "internal" in channels:
|
||||||
@@ -805,11 +810,13 @@ class GenesysAudioHookSerializer(FrameSerializer):
|
|||||||
|
|
||||||
await self._call_event_handler("on_open", message)
|
await self._call_event_handler("on_open", message)
|
||||||
|
|
||||||
return OutputTransportMessageUrgentFrame(message=self.create_opened_response(
|
return OutputTransportMessageUrgentFrame(
|
||||||
start_paused=self._params.start_paused,
|
message=self.create_opened_response(
|
||||||
supported_languages=self._params.supported_languages,
|
start_paused=self._params.start_paused,
|
||||||
selected_language=self._params.selected_language
|
supported_languages=self._params.supported_languages,
|
||||||
))
|
selected_language=self._params.selected_language,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
async def _handle_close(self, message: Dict[str, Any]) -> Frame | None:
|
async def _handle_close(self, message: Dict[str, Any]) -> Frame | None:
|
||||||
"""Handle a 'close' message from Genesys.
|
"""Handle a 'close' message from Genesys.
|
||||||
|
|||||||
Reference in New Issue
Block a user