
    qiN                     x   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
Zde
fdZdeded	efdZ	 d#dededefdZ	 d#dededefdZdeded	efdZded	efdZdedee   d	efdZdedeeef   d	efdZ	 	 	 d$de
dee   deded	ef
d Z d!edeeef   d	efd"Z!y)%a  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                    t        j                  d       | j                  d      dk(  rBd| v r>d| j                  di       v r*d| j                  di       v rt        j                  d       yd| v r.d| v r*d	| j                  di       v rt        j                  d
       yd| v r>d| j                  di       v r*d| j                  di       v rt        j                  d       y| j                  d      dk(  rVd| v rRd| j                  di       v r>d| j                  di       v r*d| j                  di       v rt        j                  d       yt        j                  d       y)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   s    F/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/runner/utils.py#_detect_transport_type_from_messager$   1   s]   
LL23 	!W,|#<++GR88))'266,- 	|#|#!1!1'2!>>,- 	<,**7B77(("55+, 	!W,|#L,,Wb99,**7B77\--gr::,-
LL9:    	websocketc           	        K   | j                         }i }i }	 |j                          d{   }t        j                  d|        |rt	        j
                  |      ni }	 |j                          d{   }t        j                  d|        |rt	        j
                  |      ni }	 t        |      }t        |      }|dk7  r|}|}	t        j                  d| d       n<|dk7  r|}|}	t        j                  d| d	       nd}|}	t        j                  d
       |dk(  rJ|	j                  di       }
|
j                  di       }|
j                  d      |
j                  d      |d}nf|dk(  r|	j                  d      |	j                  di       j                  d      |	j                  di       j                  di       j                  d      |	j                  di       j                  dd      |	j                  di       j                  dd      d}n|dk(  r6|	j                  di       }
|
j                  d      |
j                  d      d}n|dk(  ry|	j                  di       }
|
j                  d      |
j                  d       |
j                  d!      |
j                  dd      |
j                  dd      |
j                  d"d      d#}ni }t        j                  d$| d%|        ||fS 7 # t        j                  $ r Y t        $ r t        d      w xY w7 # t        j                  $ r Y t        $ r t        j                  d       Y w xY w# t        $ r}t        j                  d&|         d}~ww xY 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/   r1   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es                 r#   parse_telephony_websocketrJ   `   s    p ((*NMN["0":":"<<'8&9:;9J

#45PRL#1#;#;#=='(:';<=;M$67SUCA-PB>R )+0N)MLL//??TUV!Y.1N*MLL//??UVW&N*MNNAB X%&**7B7J"'92>I'^^K8%>>)4!	I x'*..{;#0#4#4Wb#A#E#EFW#X%2%6%6w%C^R(Z%))'26::62F#''488rBI w&&**7B7J'^^J7%>>(3I
 x'&**7B7J'^^L9%>>*5)~~m<"vr2 nnT2.%/^^4G%LI I~&6hykJKy((c =   [YZZ[
 >   LJKLH  :1#>?s   NK8 K55K8 "L( 5L%65L( ,IM" 4N5K8 8L"NL""N%L( (M;N>MNMN"	N	+NN		N	transportclientc                     	 ddl m} t        | |      r|j                  S 	 	 ddlm} t        | |      r|d   S 	 t        j                  dt        |               y# t        $ r Y Fw xY w# t        $ r Y <w xY w)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.transportrO   
isinstancepc_idImportError"pipecat.transports.daily.transportrQ   r   r9   type)rK   rL   rO   rQ   s       r#   get_transport_client_idrY      s    Qi!56<< 7
Ei0$< 1
 NNHiHYZ[    s"   A A+ 	A('A(+	A76A7	frameratec                   K   	 ddl m} t        | |      r| j                  |d   |d       d{    	 ddlm} t        | |      r| j                  d       d{    yy7 3# t        $ r Y <w xY w7 # t        $ r Y yw xY w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   rP   rR   camerarZ   video_sourceNrN   r^   rW   rQ   rT   capture_participant_videorV   rS   rO   rK   rL   rZ   rQ   rO   s        r#    maybe_capture_participant_camerarc     s     Ei055t	 6   Qi!565585LLL 7   M g   B,A& A$A& 'A7 A5A7 "B$A& &	A2/B1A22B5A7 7	B BBBc                   K   	 ddl m} t        | |      r| j                  |d   |d       d{    	 ddlm} t        | |      r| j                  d       d{    yy7 3# t        $ r Y <w xY w7 # t        $ r Y yw xY ww)	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   rP   rR   screenVideor]   NrN   r_   r`   rb   s        r#    maybe_capture_participant_screenrg   2  s     	Ei055t	 6   Qi!5655=5QQQ 7   R rd   textpatternc                 T   t        j                  d       g }| j                         }|D ]i  }t        j                  d|      r@t        j                  ||      s0t        j                  d|      rG|j                  |       Y|j                  |       k dj                  |      dz   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   r:   
splitlinesresearchappendjoin)rh   ri   resultlineslines        r#   '_smallwebrtc_sdp_cleanup_ice_candidatesrv   P  s     LL?@FOOE  99]D)yy$'		'40Hd#MM$  ;;v''r%   c                    t        j                  d       g }| j                         }|D ]A  }t        j                  d|      rt        j                  d|      r1|j                  |       C dj                  |      dz   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-512rl   rm   )rh   rs   rt   ru   s       r#   %_smallwebrtc_sdp_cleanup_fingerprintsrx   f  sn     LL=>FOOE  yyD)"))It2LMM$  ;;v''r%   sdphostc                 8    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.
    )rx   rv   )ry   rz   s     r#   smallwebrtc_sdp_mungingr|   x  s"     0
4C5c4@Jr%   transport_keytransport_paramsc                 v    | |vrt        d|  d|  d       ||           }t        j                  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 )r8   r   r:   )r}   r~   paramss      r#   _get_transport_paramsr     s_     ,,,]O <(/)MO
 	

 -m,.F
LL.}o>?Mr%   r   rD   rH   c           
        K   ddl m} |t        d      d|_        t	        j
                  d|        |dk(  rFddlm}  ||d   |d	   t        j                  d
d      t        j                  dd            |_
        n|dk(  r6ddlm}  ||d   |d   |d   dt        j                  dd            |_
        n{|dk(  rFddlm}  ||d   |d	   t        j                  dd      t        j                  dd            |_
        n0|dk(  rddlm}  ||d   |d	         |_
        nt        d| d       || |      S w) 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   )FastAPIWebsocketTransportzeFastAPIWebsocketParams 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   r0   PCMUTELNYX_API_KEY)r   r   r0   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&   r   )$pipecat.transports.websocket.fastapir   r8   add_wav_headerr   infopipecat.serializers.twilior   osgetenv
serializerpipecat.serializers.telnyxr   pipecat.serializers.plivor   pipecat.serializers.exotelr   )	r&   r   rD   rH   r   r   r   r   r   s	            r#   _create_telephony_transportr     sr    " O~K
 	
 "F
KK9.9IJK!D1 -y)		"6;yy!4b9	
 
8	#D1,%&78'(;<#II.3
 
7	"B0,i(IIor2yy!3R8	
 
8	#D1 -y)

 .~.> ?A B
 	

 %yHHs   D>E runner_argsc                   K   t        | t              r2t        d|      }ddlm}  || j
                  | j                  d|      S t        | t              r&t        d|      }ddlm	}  ||| j                        S t        | t              rLt        | j                         d	{   \  }}t        ||      }t        | j                  |||       d	{   S t        | t              r<t        d
|      }ddlm}  || j$                  | j                  | j&                  |      S t)        dt+        |              7 7 iw)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   rP   zPipecat Bot)r   webrtcrN   )r   webrtc_connectionNlivekit)LiveKitTransportz#Unsupported runner arguments type: )rT   r	   r   rW   rQ   room_urltokenr   rS   rO   r   r   rJ   r&   r   r
   $pipecat.transports.livekit.transportr   url	room_namer8   rX   )r   r~   r   rQ   rO   rD   rH   r   s           r#   create_transportr     sA    ~ +34&w0@AE  	
 	
 
K!;	<&x1ABQ#);;
 	

 
K!9	:*CKDYDY*Z$Z!	&~7GH 1!!6>9
 
 	
 
K!7	8&y2BCIOO!!	
 	
 >tK?P>QRSS) %[
s%   B!D>#D:$.D>D<A(D><D>)r   )NNN)"__doc__r4   r   ro   typingr   r   r   r   fastapir   logurur   pipecat.runner.typesr	   r
   r   r   !pipecat.transports.base_transportr   dictstrr$   rJ   rY   intrc   rg   rv   rx   r|   r   r   r    r%   r#   <module>r      s  2  	 	 0 0    <,d ,s ,^Sy Sl} c c @ =>&)69< =>&)69<(# ( ( (,( ( ($ HSM c   S(]@S X[ 4 !	GIGISMGI GI 	GI
 GITkTkT(,S(](;kTkTr%   