o
    iN                     @   s  d Z ddlZddlZddlZddlmZmZmZmZ ddl	m
Z
 ddlmZ ddlmZmZmZmZ ddlmZ ded	efd
dZde
fddZdeded	efddZ	d.dededefddZ	d.dededefddZdeded	efddZded	efddZdedee d	efd d!Zd"ed#eeef d	efd$d%Z			d/de
d&ee d'ed(ed	ef
d)d*Z d+ed#eeef d	efd,d-Z!dS )0a  Transport utility functions and FastAPI route setup helpers.

This module provides common functionality for setting up transport-specific
FastAPI routes and handling WebRTC/WebSocket connections. It includes SDP
manipulation utilities for WebRTC compatibility and transport detection helpers.

Key features:

- WebRTC route setup with connection management
- WebSocket route setup for telephony providers
- SDP munging for ESP32 and other WebRTC compatibility
- Transport client ID detection across different transport types
- Video capture utilities for Daily transports

The utilities are designed to be transport-agnostic where possible, with
specific handlers for each transport type's unique requirements.

Example::

    from pipecat.runner.utils import parse_telephony_websocket

    async def telephony_websocket_handler(websocket: WebSocket):
        transport_type, call_data = await parse_telephony_websocket(websocket)
    N)AnyCallableDictOptional)	WebSocket)logger)DailyRunnerArgumentsLiveKitRunnerArgumentsSmallWebRTCRunnerArgumentsWebSocketRunnerArguments)BaseTransportmessage_datareturnc                 C   s  t d | ddkr'd| v r'd| di v r'd| di v r't d dS d| v r>d| v r>d	| di v r>t d
 dS d| v rYd| di v rYd| di v rYt d dS | ddkrd| v rd| di v rd| di v rd| di v rt d dS t d dS )zGAttempt to auto-detect transport type from WebSocket message structure.z=== Auto-Detection Analysis ===eventstart	streamSidcallSidzAuto-detected: TWILIOtwilio	stream_idcall_control_idzAuto-detected: TELNYXtelnyxstreamIdcallIdzAuto-detected: PLIVOplivo
stream_sidcall_sidaccount_sidzAuto-detected: EXOTELexotelz&Auto-detection failed - unknown formatunknown)r   traceget)r    r!   H/home/ubuntu/.local/lib/python3.10/site-packages/pipecat/runner/utils.py#_detect_transport_type_from_message1   s4   





r#   	websocketc              
      s  |   }i }i }z| I dH }td|  |r t|ni }W n tjy-   Y n ty7   tdw z| I dH }td|  |rOt|ni }W n tjy\   Y n tyi   t	d Y nw zt
|}t
|}|dkr|}|}	td| d n|dkr|}|}	td| d	 n	d}|}	t	d
 |dkr|	di }
|
di }|
d|
d|d}nu|dkr|	d|	di d|	di di d|	di dd|	di ddd}nC|dkr|	di }
|
d|
dd}n,|dkr0|	di }
|
d|
d |
d!|
dd|
dd|
d"dd#}ni }td$| d%|  ||fW S  tyW } z	td&|   d}~ww )'a  Parse telephony WebSocket messages and return transport type and call data.

    Args:
        websocket: FastAPI WebSocket connection from telephony provider.

    Returns:
        tuple: (transport_type: str, call_data: dict)

        call_data contains provider-specific fields:

        - Twilio::

            {
                "stream_id": str,
                "call_id": str,
                "body": dict
            }

        - Telnyx::

            {
                "stream_id": str,
                "call_control_id": str,
                "outbound_encoding": str,
                "from": str,
                "to": str,
            }

        - Plivo::

            {
                "stream_id": str,
                "call_id": str,
            }

        - Exotel::

            {
                "stream_id": str,
                "call_id": str,
                "account_sid": str,
                "from": str,
                "to": str,
            }

    Raises:
        ValueError: If WebSocket closes before sending any messages.

    Example usage::

        transport_type, call_data = await parse_telephony_websocket(websocket)
        if transport_type == "twilio":
            user_id = call_data["body"]["user_id"]
    NzFirst message: z>WebSocket closed before receiving telephony handshake messageszSecond message: z1Only received one WebSocket message, expected twor   zDetected transport: z (from first message)z (from second message)z$Could not auto-detect transport typer   r   customParametersr   r   )r   call_idbodyr   r   r   media_formatencodingfrom to)r   r   outbound_encodingr*   r,   r   r   r   )r   r&   r   r   r   r   custom_parameters)r   r&   r   r*   r,   r.   zParsed - Type: z, Data: z#Error parsing telephony WebSocket: )	iter_text	__anext__r   r   jsonloadsJSONDecodeErrorStopAsyncIteration
ValueErrorwarningr#   debugr    	Exceptionerror)r$   message_streamfirst_messagesecond_messagefirst_message_rawsecond_message_rawdetected_type_firstdetected_type_secondtransport_typecall_data_raw
start_data	body_data	call_dataer!   r!   r"   parse_telephony_websocket`   s   8









rG   	transportclientc                 C   s   zddl m} t| |r|jW S W n	 ty   Y nw zddlm} t| |r,|d W S W n	 ty6   Y nw tdt	|   dS )zGet client identifier from transport-specific client object.

    Args:
        transport: The transport instance.
        client: Transport-specific client object.

    Returns:
        Client identifier string, empty if transport not supported.
    r   SmallWebRTCTransportDailyTransportidz3Unable to get client id from unsupported transport r+   )
(pipecat.transports.smallwebrtc.transportrK   
isinstancepc_idImportError"pipecat.transports.daily.transportrM   r   r6   type)rH   rI   rK   rM   r!   r!   r"   get_transport_client_id   s$   


rU   	frameratec                       zddl m} t| |r| j|d |ddI dH  W n	 ty$   Y nw zddlm} t| |r=| jddI dH  W dS W dS  tyI   Y dS w )	zCapture participant camera video if transport supports it.

    Args:
        transport: The transport instance.
        client: Transport-specific client object.
        framerate: Video capture framerate. Defaults to 0 (auto).
    r   rL   rN   camerarV   video_sourceNrJ   rZ   rS   rM   rP   capture_participant_videorR   rO   rK   rH   rI   rV   rM   rK   r!   r!   r"    maybe_capture_participant_camera  s&   



r_   c                    rW   )	zCapture participant screen video if transport supports it.

    Args:
        transport: The transport instance.
        client: Transport-specific client object.
        framerate: Video capture framerate. Defaults to 0 (auto).
    r   rL   rN   screenVideorY   NrJ   r[   r\   r^   r!   r!   r"    maybe_capture_participant_screen2  s&   



ra   textpatternc                 C   sh   t d g }|  }|D ]}td|r't||r&td|s&|| q|| qd|d S )zClean up ICE candidates in SDP text for SmallWebRTC.

    Args:
        text: SDP text to clean up.
        pattern: Pattern to match for candidate filtering.

    Returns:
        Cleaned SDP text with filtered ICE candidates.
    z,Removing unsupported ICE candidates from SDPza=candidateraddr
r   r7   
splitlinesresearchappendjoin)rb   rc   resultlinesliner!   r!   r"   '_smallwebrtc_sdp_cleanup_ice_candidatesP  s   


ro   c                 C   sP   t d g }|  }|D ]}td|s td|s || qd|d S )zRemove unsupported fingerprint algorithms from SDP text.

    Args:
        text: SDP text to clean up.

    Returns:
        SDP text with sha-384 and sha-512 fingerprints removed.
    z*Removing unsupported fingerprints from SDPzsha-384zsha-512re   rf   )rb   rl   rm   rn   r!   r!   r"   %_smallwebrtc_sdp_cleanup_fingerprintsf  s   
	
rp   sdphostc                 C   s   t | } |rt| |} | S )zApply SDP modifications for SmallWebRTC compatibility.

    Args:
        sdp: Original SDP string.
        host: Host address for ICE candidate filtering.

    Returns:
        Modified SDP string with fingerprint and ICE candidate cleanup.
    )rp   ro   )rq   rr   r!   r!   r"   smallwebrtc_sdp_mungingx  s   

rs   transport_keytransport_paramsc                 C   s<   | |vrt d|  d|  d||   }td|   |S )aa  Get transport parameters from factory function.

    Args:
        transport_key: The transport key to look up
        transport_params: Dict mapping transport names to parameter factory functions

    Returns:
        Transport parameters from the factory function

    Raises:
        ValueError: If transport key is missing from transport_params
    zMissing transport params for 'z'. Please add 'z$' key to your transport_params dict.zUsing transport params for )r5   r   r7   )rt   ru   paramsr!   r!   r"   _get_transport_params  s   
rw   rv   rA   rE   c           	   	      s:  ddl m} |du rtdd|_td|  |dkr:ddlm} ||d	 |d
 t	ddt	ddd|_
n]|dkrYddlm} ||d	 |d |d dt	ddd|_
n>|dkryddlm} ||d	 |d
 t	ddt	ddd|_
n|dkrddlm} ||d	 |d
 d|_
ntd| d|| |d S )!a  Create a telephony transport with pre-parsed WebSocket data.

    Args:
        websocket: FastAPI WebSocket connection from telephony provider
        params: FastAPIWebsocketParams (required)
        transport_type: Pre-detected provider type ("twilio", "telnyx", "plivo")
        call_data: Pre-parsed call data dict with provider-specific fields

    Returns:
        Configured FastAPIWebsocketTransport ready for telephony use.
    r   )FastAPIWebsocketTransportNzeFastAPIWebsocketParams must be provided. The serializer and add_wav_header will be set automatically.Fz'Using pre-detected telephony provider: r   )TwilioFrameSerializerr   r&   TWILIO_ACCOUNT_SIDr+   TWILIO_AUTH_TOKEN)r   r   r   
auth_tokenr   )TelnyxFrameSerializerr   r-   PCMUTELNYX_API_KEY)r   r   r-   inbound_encodingapi_keyr   )PlivoFrameSerializerPLIVO_AUTH_IDPLIVO_AUTH_TOKEN)r   r&   auth_idr|   r   )ExotelFrameSerializer)r   r   z Unsupported telephony provider: z4. Supported providers: twilio, telnyx, plivo, exotel)r$   rv   )$pipecat.transports.websocket.fastapirx   r5   add_wav_headerr   infopipecat.serializers.twiliory   osgetenv
serializerpipecat.serializers.telnyxr}   pipecat.serializers.plivor   pipecat.serializers.exotelr   )	r$   rv   rA   rE   rx   ry   r}   r   r   r!   r!   r"   _create_telephony_transport  sV   









r   runner_argsc                    s   t | trtd|}ddlm} || j| jd|dS t | tr2td|}ddlm	} ||| j
dS t | trQt| jI d	H \}}t||}t| j|||I d	H S t | trltd
|}ddlm} || j| j| j|dS tdt|  )aH	  Create a transport from runner arguments using factory functions.

    This function uses the clean transport_params factory pattern where users
    define a dictionary mapping transport names to parameter factory functions.

    Args:
        runner_args: Arguments from the runner.
        transport_params: Dict mapping transport names to parameter factory functions.
            Keys should be: "daily", "webrtc", "twilio", "telnyx", "plivo", "exotel"
            Values should be functions that return transport parameters when called.

    Returns:
        Configured transport instance.

    Raises:
        ValueError: If transport key is missing from transport_params or runner_args type is unsupported.
        ImportError: If required dependencies are not installed.

    Example::

        transport_params = {
            "daily": lambda: DailyParams(
                audio_in_enabled=True,
                audio_out_enabled=True,
                vad_analyzer=SileroVADAnalyzer(),
            ),
            "webrtc": lambda: TransportParams(
                audio_in_enabled=True,
                audio_out_enabled=True,
                vad_analyzer=SileroVADAnalyzer(),
            ),
            "twilio": lambda: FastAPIWebsocketParams(
                audio_in_enabled=True,
                audio_out_enabled=True,
                vad_analyzer=SileroVADAnalyzer(),
                # add_wav_header and serializer will be set automatically
            ),
            "telnyx": lambda: FastAPIWebsocketParams(
                audio_in_enabled=True,
                audio_out_enabled=True,
                vad_analyzer=SileroVADAnalyzer(),
                # add_wav_header and serializer will be set automatically
            ),
            "plivo": lambda: FastAPIWebsocketParams(
                audio_in_enabled=True,
                audio_out_enabled=True,
                vad_analyzer=SileroVADAnalyzer(),
                # add_wav_header and serializer will be set automatically
            ),
            "exotel": lambda: FastAPIWebsocketParams(
                audio_in_enabled=True,
                audio_out_enabled=True,
                vad_analyzer=SileroVADAnalyzer(),
                # add_wav_header and serializer will be set automatically
            ),
        }

        transport = await create_transport(runner_args, transport_params)
    dailyr   rL   zPipecat Bot)rv   webrtcrJ   )rv   webrtc_connectionNlivekit)LiveKitTransportz#Unsupported runner arguments type: )rP   r   rw   rS   rM   room_urltokenr
   rO   rK   r   r   rG   r$   r   r	   $pipecat.transports.livekit.transportr   url	room_namer5   rT   )r   ru   rv   rM   rK   rA   rE   r   r!   r!   r"   create_transport  sB   
?








r   )r   )NNN)"__doc__r1   r   rh   typingr   r   r   r   fastapir   logurur   pipecat.runner.typesr   r	   r
   r   !pipecat.transports.base_transportr   dictstrr#   rG   rU   intr_   ra   ro   rp   rs   rw   r   r   r!   r!   r!   r"   <module>   sl   /  


J
