
    i8                        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 d dlmZ er2d dlmZ d dlmZ d d	lmZ d d
lmZ d dlmZ d dlmZ eZeZeeef   ZeZneZeZeZeZeZ	 d d
lmZ dZ G d de      Zy# e$ r dZY w xY w)    N)TYPE_CHECKINGAnyOptionalUnion)verbose_logger)_utils)ArizeOTELAttributes)ArizePhoenixConfigTracerProvider)SpanSpanKind)OpenTelemetry)OpenTelemetryConfig)Protocolz https://otlp.arize.com/v1/tracesc                       e Zd ZdZd Zd Zdedee   fdZ	e
defd       Ze
dee   fd	       Zd
 Zd Zd Ze
defd       Zd Zy)ArizePhoenixLoggera+  
    Arize Phoenix logger that sends traces to a Phoenix endpoint.

    Creates its own dedicated TracerProvider so it can coexist with the
    generic ``otel`` callback (or any other OTEL-based integration) without
    fighting over the global ``opentelemetry.trace`` TracerProvider singleton.
    c                    ddl m} ddlm} ||j	                  d      | _        || _        y || j                  | j                              }|j                  | j                                |j	                  d      | _        || _        t        j                  d| j                  j                  | j                  j                         y)a  
        Override to always create a *private* TracerProvider for Arize Phoenix.

        The base ``OpenTelemetry._init_tracing`` falls back to the global
        TracerProvider when one already exists.  That causes whichever
        integration initialises second to silently reuse the first one's
        exporter, so spans only reach one destination.

        By creating our own provider we guarantee Arize Phoenix always gets
        its own exporter pipeline, regardless of initialisation order.
        r   r   r   Nlitellm)resourcezOArizePhoenixLogger: Created dedicated TracerProvider (endpoint=%s, exporter=%s))opentelemetry.sdk.tracer   opentelemetry.tracer   
get_tracertracer	span_kind_get_litellm_resourceconfigadd_span_processor_get_span_processorr   debugendpointexporter)selftracer_providerr   r   providers        y/Users/manta/Documents/Projects/TheRoad-I1/.venv/lib/python3.12/site-packages/litellm/integrations/arize/arize_phoenix.py_init_tracingz ArizePhoenixLogger._init_tracing1   s     	;0&)44Y?DK%DN "4+E+Edkk+RS##D$<$<$>?)))4!)KK  KK  		
    c                      y)z
        Override: Arize Phoenix should NOT overwrite the proxy's
        ``open_telemetry_logger``.  That attribute is reserved for the
        primary ``otel`` callback which handles proxy-level parent spans.
        N )r$   s    r'   "_init_otel_logger_on_litellm_proxyz5ArizePhoenixLogger._init_otel_logger_on_litellm_proxyR   s     	r)   spanresponse_objc                 2    t         j                  |||       y )N)r   set_arize_phoenix_attributes)r$   r-   kwargsr.   s       r'   set_attributesz!ArizePhoenixLogger.set_attributesZ   s    77flSr)   c                     ddl m} t        j                  | ||t               t
        j                  |      }|r || d|       y t
        j                         }|j                  r || d|j                         y )Nr   )safe_set_attributezopeninference.project.name)	Elitellm.integrations.opentelemetry_utils.base_otel_llm_obs_attributesr4   r   r2   r	   r   _get_dynamic_project_nameget_arize_phoenix_configproject_name)r-   r1   r.   r4   dynamic_project_namer   s         r'   r0   z/ArizePhoenixLogger.set_arize_phoenix_attributes^   s{    	
 	dFL:MN  2KKFSt%ACWX 	 (@@BF"""68K8K 	r)   returnc                    | j                  d      }t        |t              r?|j                  d      }t        |t              r|j                  d      }|rt        |      S | j                  d      }t        |t              r|j                  d      xs i }ni }t        |t              r|j                  d      }|rt        |      S y)z
        Retrieve dynamic Phoenix project name from request metadata.

        Users can set `metadata.phoenix_project_name` in their request to route
        traces to different Phoenix projects dynamically.
        standard_logging_objectmetadataphoenix_project_namelitellm_paramsN)get
isinstancedictstr)r1   standard_logging_payloadr=   r8   r?   s        r'   r6   z,ArizePhoenixLogger._get_dynamic_project_namet   s     $*::.G#H .5/33J?H(D)'||,BC|,,  $45nd+%))*5;HHh%#<<(>?L<((r)   c                    ddl m} |j                  di       xs i }|j                  di       xs i }|j                  di       xs i }|j                  d      r| j                  |      nd}t	        |      }|r||j                  d	|j                  d
            }| j
                  j                  d|| j                  |      nd|| j                  j                        }	|j                  |	      }
|
|	fS |dfS )u  
        Build a trace context for Phoenix's dedicated TracerProvider.

        The base ``_get_span_context`` returns parent spans from the global
        TracerProvider (the ``otel`` callback).  Those spans live on a
        *different* TracerProvider, so they won't appear in Phoenix — using
        them as parents just creates broken links.

        Instead we:
        1. Honour an incoming ``traceparent`` HTTP header (distributed tracing).
        2. In proxy mode, create our *own* parent span on Phoenix's tracer
           so the hierarchy is visible end-to-end inside Phoenix.
        3. In SDK (non-proxy) mode, just return (None, None) for a root span.
        r   )tracer?   proxy_server_requestheaderstraceparent)rH   N
start_timeapi_call_start_timelitellm_proxy_request)namerJ   contextkind)opentelemetryrF   r@   get_traceparent_from_headerboolr   
start_span_to_nsr   SERVERset_span_in_context)r$   r1   rF   r?   rG   rH   traceparent_ctxis_proxy_modestart_time_valparent_spanctxs              r'   _get_phoenix_contextz'ArizePhoenixLogger._get_phoenix_context   s    	($4b9?R-112H"MSQS&**9b9?R
 {{=) ,,W,= 	 12 $ZZfjjAV6WXN++00,!-  ;;~6'^^** 1 K ++K8C## $$r)   c                    ddl m}m} t        j                  d|| j
                         | j                  |      \  }}| j                  j                  | j                  |      | j                  |      |      }	|	j                   ||j                               | j                  |	||       | j                  |||||	       |	j                  | j                  |             | j!                  ||       |U|j                   ||j                               | j                  |||       |j                  | j                  |             | j#                  ||||       | j
                  j$                  r| j'                  |||	       yy)a  
        Override to always create spans on ArizePhoenixLogger's dedicated TracerProvider.

        The base class's ``_get_span_context`` would find the parent span created by
        the ``otel`` callback on the *global* TracerProvider.  That span is invisible
        in Phoenix (different exporter pipeline), so we ignore it and build our own
        hierarchy via ``_get_phoenix_context``.
        r   Status
StatusCodez?ArizePhoenixLogger: Logging kwargs: %s, OTEL config settings=%srM   rJ   rN   end_timer1   rN   N)r   r_   r`   r   r!   r   r\   r   rS   _get_span_namerT   
set_statusOKr2   _maybe_log_raw_requestend_create_guardrail_span_record_metricsenable_events_emit_semantic_logs
r$   r1   r.   rJ   rc   r_   r`   r[   rZ   r-   s
             r'   _handle_successz"ArizePhoenixLogger._handle_success   sc    	;MKK	
  44V<[ {{%%$$V,{{:. & 

 	z}}-.D&,7 	##FL*hPTU$++h/0 	##63#? """6*--#89V\BOOT[[%:O; 	V\:xH ;;$$$$V\4@ %r)   c                    ddl m}m} t        j                  d|| j
                         | j                  |      \  }}| j                  j                  | j                  |      | j                  |      |      }	|	j                   ||j                               | j                  |	||       | j                  |	|       |	j                  | j                  |             | j!                  ||       |i|j                   ||j                               | j                  |||       | j                  ||       |j                  | j                  |             yy)	z
        Override to always create failure spans on ArizePhoenixLogger's dedicated
        TracerProvider.  Mirrors ``_handle_success`` but sets ERROR status.
        r   r^   zIArizePhoenixLogger: Failure - Logging kwargs: %s, OTEL config settings=%sra   )r-   r1   rb   rd   N)r   r_   r`   r   r!   r   r\   r   rS   re   rT   rf   ERRORr2   _record_exception_on_spanri   rj   rn   s
             r'   _handle_failurez"ArizePhoenixLogger._handle_failure   s=   
 	;WKK	
  44V<[ {{%%$$V,{{:. & 

 	z//01D&,7&&D&@$++h/0 	##63#? """6**:*:#;<V\B**F*KOOT[[%:O;	 #r)   c                     t         j                  j                  dd      } t         j                  j                  dd      }|sFt         j                  j                  dd      }t         j                  j                  dd      }|xs |}d}d}|rd|j                  d      sd|v r	d|vr|}d	}nbd
|v r|}d}n:d|vr4|j	                  d      r|dz   }n|j	                  d      r| d}n| d}n|}d}nd}d}t        j                  d|        d}| d|  }nd
|v rt        d      t         j                  j                  dd      }t        ||||      S )z
        Retrieves the Arize Phoenix configuration based on environment variables.
        Returns:
            ArizePhoenixConfig: A Pydantic model containing Arize Phoenix configuration.
        PHOENIX_API_KEYNPHOENIX_COLLECTOR_HTTP_ENDPOINTPHOENIX_COLLECTOR_ENDPOINT	otlp_httpzgrpc://z:4317z
/v1/traces	otlp_grpczapp.phoenix.arize.comz/v1z/traces/z	v1/traceszhttp://localhost:6006/v1/traceszKNo PHOENIX_COLLECTOR_ENDPOINT found, using default local Phoenix endpoint: zAuthorization=Bearer zMPHOENIX_API_KEY must be set when using Phoenix Cloud (app.phoenix.arize.com).PHOENIX_PROJECT_NAMEdefault)otlp_auth_headersprotocolr"   r8   )	osenvironr@   
startswithendswithr   r!   
ValueErrorr
   )api_keycollector_endpointgrpc_endpointhttp_endpointr"   r~   r}   r8   s           r'   r7   z+ArizePhoenixLogger.get_arize_phoenix_config  s    **..!2D9ZZ^^,MtT!JJNN+GNMJJNN+LdSM!.!?-(!,,Y7--,FX2X-& +.@@1H*H!);;)2259#5	#A+44S9&8%9#C&8%9#D1H& 9H"H  ]^f]gh !"7y A$0_  zz~~&<iH!/%	
 	
r)   c                 V   K   | j                         }|j                  sdddS dddS w)N	unhealthyz,PHOENIX_API_KEY environment variable not set)statuserror_messagehealthyz1Arize-Phoenix credentials are configured properly)r   message)r7   r}   )r$   r   s     r'   async_health_checkz%ArizePhoenixLogger.async_health_check\  s?     ..0''%!O   J
 	
s   ')N)__name__
__module____qualname____doc__r(   r,   r   r   r   r2   staticmethodr0   rC   r6   r\   ro   rs   r
   r7   r   r+   r)   r'   r   r   (   s    
B4 x}  4  * Xc]  8.%`/Ab"<H A
&8 A
 A
J
r)   r   )r   typingr   r   r   r   litellm._loggingr   litellm.integrations.arizer   !litellm.integrations.arize._utilsr	   (litellm.types.integrations.arize_phoenixr
   r   r   r   r   _Spanr   "litellm.integrations.opentelemetryr   _OpenTelemetryr   _OpenTelemetryConfig litellm.types.integrations.arizer   	_ProtocolImportErrorARIZE_HOSTED_PHOENIX_ENDPOINTr   r+   r)   r'   <module>r      s    	 6 6 + - A G61,R GH.D"MHDNHD
 !C @
 @
  s   (A< <BB