Ë
    uPêjð:  ã                  ól  — U d Z ddlmZ ddlZddlmZmZ ddlmZ ddl	Z	ddl
mZ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mZ ddlmZ  eeeeez  ez  ez  f   «      Z de!d<    ee"e   «      Z#de!d<    ee$e   «      Z%de!d<   dZ&de!d<   dZ'de!d<   dZ(de!d<    G d„ de«      Z)dd„Z*dd„Z+dd„Z,y)zm
TinyFish Search API.
Endpoint: GET https://api.search.tinyfish.ai
Docs: https://docs.tinyfish.ai/search-api
é    )ÚannotationsN)ÚFinalÚLiteral)Ú	urlencode)ÚTypeAdapterÚValidationError)Úverbose_logger)Úprocess_response_headers)ÚLogging)ÚBaseLLMException)ÚBaseSearchConfigÚSearchResponse)Úget_secret_strr   Ú_UrlEncodableParamsÚ_StrListÚ_StrFrozenSetÚ_tinyfish_paramsÚ_TINYFISH_PARAMS_KEYz#https://docs.tinyfish.ai/search-apiÚ_TINYFISH_DOCS_URLé
   Ú_TINYFISH_RESULT_CAPc                  óÄ   ‡ — e Zd ZdZd
ˆ fd„Zedd„«       Zdd„Z	 	 d	 	 	 	 	 	 	 	 	 dd„Z	 d	 	 	 	 	 	 	 	 	 dd„Z		 	 	 	 	 	 	 	 dd„Z
	 	 	 	 	 	 	 	 dd„Z	 	 	 	 	 	 	 	 dd	„Zˆ xZS )ÚTinyfishSearchConfigzhttps://api.search.tinyfish.aic                ó0   •— t         ‰| �  «        d | _        y ©N)ÚsuperÚ__init__Ú_caller_max_results)ÚselfÚ	__class__s    €úp/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/llms/tinyfish/search/transformation.pyr   zTinyfishSearchConfig.__init__&   s   ø€ Ü‰ÑÔð
 04ˆÕ ó    c                  ó   — y)NÚTinyFish© r%   r"   r!   Úui_friendly_namez%TinyfishSearchConfig.ui_friendly_name.   s   € àr"   c                 ó   — y)NÚGETr%   )r   s    r!   Úget_http_methodz$TinyfishSearchConfig.get_http_method2   s   € Ør"   c                ón   — | j                  ||dd| j                  ¬«      }|st        d«      ‚i |¥|ddœ¥S )N)ÚTINYFISH_API_KEYÚTINYFISH_API_BASE)Úcaller_api_keyÚcaller_api_baseÚkey_env_varsÚbase_env_varÚdefault_api_basezITINYFISH_API_KEY is not set. Set `TINYFISH_API_KEY` environment variable.zapplication/json)z	X-API-KeyÚAccept)Úresolve_server_api_keyr,   Ú
ValueError)r   ÚheadersÚapi_keyÚapi_baseÚkwargsÚresolved_keys         r!   Úvalidate_environmentz)TinyfishSearchConfig.validate_environment5   sR   € ð #×9Ñ9Ø"Ø$Ø.Ø,Ø!×3Ñ3ð :ó 
ˆñ ÜÐhÓiÐiØS�'ÐS¨Ð@RÒSÐSr"   c                óÌ   — |xs t        d«      xs | j                  }t        |t        «      r6t        |v r.t
        j                  |t           «      }|› dt        |d¬«      › �S |S )Nr,   ú?T)Údoseq)r   r,   Ú
isinstanceÚdictr   r   Úvalidate_pythonr   )r   r7   Úoptional_paramsÚdatar8   Úresolved_baseÚvalidated_paramss          r!   Úget_complete_urlz%TinyfishSearchConfig.get_complete_urlG   si   € ð  (Òh¬>Ð:MÓ+NÒhÐRV×RhÑRhˆÜ�dœDÔ!Ô&:¸dÑ&BÜ&9×&IÑ&IÈ$ÔOcÑJdÓ&eÐØ#�_ A¤iÐ0@ÈÔ&MÐ%NÐOÐOØÐr"   c                ól  — t        |t        «      rdj                  |«      n|}	 t        j	                  |j                  d«      «      }|rt        ||«      }d|i}|j                  d«      }t        |t        «      r||d<   |j                  d«      }t        |t        t        t        f«      r)	 t        dt        t        |«      t        «      «      | _        | j)                  «       }	t*        j	                  |	«      }
|j-                  «       D ]X  \  }}||
vsŒ||vsŒt        |t.        t        f«      rt1        j2                  |d	¬
«      }nt        |t4        «      r|rdnd}|||<   ŒZ t6        |iS # t        t        f$ r g }Y �Œ9w xY w# t         t        t"        f$ r t%        j&                  d|«       Y ŒÜw xY w)u,  
        Transform a LiteLLM search request to TinyFish's querystring format.

        Maps LiteLLM's unified-spec params (see
        ``BaseSearchConfig.get_supported_perplexity_optional_params``) to
        TinyFish equivalents:
        - ``query`` (str or list[str]) â†’ ``query`` (list joined by spaces)
        - ``country`` â†’ ``location``
        - ``search_domain_filter`` (list[str]) â†’ folded into the query using
          search operators
        - ``max_results`` â†’ not sent on the wire; stashed on
          ``self._caller_max_results`` for client-side response truncation
          (TinyFish doesn't honor it server-side)
        - ``max_tokens_per_page`` â†’ silently dropped (no TinyFish equivalent)

        Any other ``optional_params`` keys are forwarded to TinyFish as-is.
        dict and list values are JSON-encoded so structured payloads survive
        ``urlencode``.

        Returns:
            ``{_TINYFISH_PARAMS_KEY: <dict of querystring entries>}``.
            ``get_complete_url`` reads this back to build the final URL.
        ú Úsearch_domain_filterÚqueryÚcountryÚlocationÚmax_resultsé   zATinyFish Search: max_results=%r is not a valid integer; ignoring.)ú,ú:)Ú
separatorsÚtrueÚfalse)r>   ÚlistÚjoinr   r@   Úgetr   Ú	TypeErrorÚ_append_domain_filtersÚstrÚintÚfloatÚmaxÚminr   r   r4   ÚOverflowErrorr	   ÚwarningÚ(get_supported_perplexity_optional_paramsr   Úitemsr?   ÚjsonÚdumpsÚboolr   )r   rI   rA   r8   Úresolved_queryÚdomainsÚrequest_datarJ   Úraw_maxÚraw_supportedÚsupported_perplexityÚparamÚvalues                r!   Útransform_search_requestz-TinyfishSearchConfig.transform_search_requestT   s¢  € ô: -7°u¼dÔ,C˜Ÿ™ %œÈˆð	Ü×.Ñ.¨×/BÑ/BÐCYÓ/ZÓ[ˆGñ Ü3°NÀGÓLˆNà29¸>Ð1Jˆà(×,Ñ,¨YÓ7ˆÜ�gœsÔ#Ø'.ˆL˜Ñ$ð
 )×,Ñ,¨]Ó;ˆÜ�g¤¤U¬CÐ0Ô1ðÜ+.¨q´#´c¸'³lÔDXÓ2YÓ+Z�Ô(ð ×9Ñ9Ó;ð 	ô '4×&CÑ&CÀMÓ&RÐØ+×1Ñ1Ó3ò 
	,‰LˆE�5ØÐ0Ò0°UÀ,Ò5Nä˜e¤d¬D \Ô2Ü ŸJ™J u¸ÔD‘Eô   ¤tÔ,Ù&+™F°�EØ&+�˜UÒ#ð
	,ô % lÐ3Ð3øôS  ¤Ð+ò 	Ø‹Gð	ûô$ ¤	¬=Ð9ò ä×&Ñ&ØWØöðús#   ¥$E. Â.(F Å.FÆFÆ*F3Æ2F3c                ó  — d|j                   cxk  rdk  s=n | j                  |j                  |j                   t        |j                  «      ¬«      ‚	 |j                  «       }t        |«       	 t        j                  |«      }t        |«       | j                  xs t        }|j                  d| |_        t        |j                  «      }|j                  }	||	d<   t!        |«      |	d<   |S # t
        j                  $ rB | j                  d|j                  dd › �|j                   t        |j                  «      ¬«      ‚w xY w# t        $ r9}| j                  d|› �|j                   t        |j                  «      ¬«      ‚d}~ww xY w)	uF	  
        Transform a TinyFish response to LiteLLM's unified ``SearchResponse``.

        Per-result field handling:
        - ``title``, ``url``, ``snippet`` are declared on ``SearchResult`` and
          populated by ``SearchResponse.model_validate`` when present. Missing
          or ``None`` values are defaulted to ``""`` beforehand by
          ``_default_missing_result_fields`` so a degraded result flows through
          instead of failing the whole call.
        - All undeclared per-result fields (``position``, ``site_name``, and
          any others TinyFish returns) ride through as extras via
          ``SearchResult``'s ``extra="allow"`` config â€” accessible as
          attributes on the result object or enumerable via
          ``result.model_extra``.

        Top-level ``parameter_warnings`` is read when present and each entry
        is re-fired via ``verbose_logger.warning``. Absent or malformed
        entries are silently skipped â€” never throws.

        Top-level extras (``query``, ``total_results``, ``page``, and any
        future TinyFish additions) ride through via
        ``SearchResponse.extra="allow"``. The validated response is returned
        in place after truncating ``results`` to the caller's ``max_results``,
        so every field pydantic populated survives regardless of which
        storage bucket (declared attribute or ``__pydantic_extra__``) holds it.

        TinyFish response headers (e.g. ``x-request-id``, ``retry-after``,
        ``x-ratelimit-limit`` â€” httpx normalizes header names to lowercase)
        are stashed on ``response._hidden_params["headers"]`` (raw) and
        ``response._hidden_params["additional_headers"]`` (sanitized via
        ``process_response_headers``) so callers can correlate a search with
        server-side logs.

        Error paths routed through ``self._wrap_error`` for uniform
        ``"TinyFish Search: <msg>. See <docs> for details."`` wrapping:
        - non-2xx HTTP status (caught here because ``AsyncHTTPHandler.get``
          does not call ``raise_for_status``)
        - 200 with non-JSON body
        - 200 with valid JSON whose shape doesn't satisfy ``SearchResponse``

        Returns:
            ``SearchResponse`` truncated to ``self._caller_max_results`` (or
            ``_TINYFISH_RESULT_CAP`` when the caller didn't set ``max_results``).
        éÈ   i,  )Úerror_messageÚstatus_coder5   zExpected JSON response, got: Nz?Response shape does not match LiteLLM's SearchResponse schema: r5   Úadditional_headers)rp   Ú_wrap_errorÚtextr?   r5   ra   ÚJSONDecodeErrorÚ_default_missing_result_fieldsr   Úmodel_validater   Ú_emit_parameter_warningsr   r   ÚresultsÚ_hidden_paramsr
   )
r   Úraw_responseÚlogging_objr8   Úraw_jsonÚparsedÚerL   Úraw_headersÚhiddens
             r!   Útransform_search_responsez.TinyfishSearchConfig.transform_search_response    s•  € ðj �|×/Ñ/Ô5°#Ô5Ø×"Ñ"Ø*×/Ñ/Ø(×4Ñ4Ü˜\×1Ñ1Ó2ð #ó ð ð	Ø&2×&7Ñ&7Ó&9ˆHô 	' xÔ0ð	Ü*×9Ñ9¸(ÓCˆFô 	! Ô(à!×5Ñ5ÒMÔ9MˆØŸ™¨¨Ð5ˆŒÜ! ,×"6Ñ"6Ó7ˆØ×-Ñ-ˆØ'ˆˆyÑÜ'?ÀÓ'LˆÐ#Ñ$Øˆøô5 ×#Ñ#ò 	Ø×"Ñ"Ø =¸l×>OÑ>OÐPTÐQTÐ>UÐ=VÐWØ(×4Ñ4Ü˜\×1Ñ1Ó2ð #ó ð ð	ûô ò 	Ø×"Ñ"Ø!`ÐabÐ`cÐdØ(×4Ñ4Ü˜\×1Ñ1Ó2ð #ó ð ûð	ús%   ÁC. Á0E Ã.AEÅ	FÅ4FÆFc                óN  — |}	 t        j                  |«      }t        |t        «      rF|j	                  d«      }t        |t        «      r%|j	                  d«      }t        |t
        «      r|r|}t        |d|› dt        › d�|¬«      S # t         j                  t        f$ r Y Œ4w xY w)u¤  
        Build an attributed ``BaseLLMException`` from a TinyFish error body.

        Used only at the call sites we control inside
        ``transform_search_response`` (non-2xx, JSONDecodeError, ValidationError).
        Not an override of ``BaseSearchConfig.get_error_class``: that path is
        left to inherit from the base so it auto-picks-up any future LiteLLM
        improvements. Trade-off: network failures (routed through LiteLLM
        core's ``_handle_error`` â†’ ``BaseSearchConfig.get_error_class``) won't
        carry the ``TinyFish Search:`` prefix â€” the bare error already names
        the host in the URL, so attribution is implicit there.
        ÚerrorÚmessagezTinyFish Search: z. See z for details.)rp   r„   r5   )
ra   Úloadsr>   r?   rU   rX   rt   rV   r   r   )r   ro   rp   r5   Úinner_messageÚbodyÚ	error_objÚ	candidates           r!   rr   z TinyfishSearchConfig._wrap_errorú   s¦   € ð* &ˆð		Ü"&§*¡*¨]Ó";ˆDÜ˜$¤Ô%Ø+/¯8©8°GÓ+<�	Ü˜i¬Ô.Ø/8¯}©}¸YÓ/G�IÜ! )¬SÔ1±iØ(1˜ô  Ø#Ø'¨ °fÔ=OÐ<PÐP]Ð^Øô
ð 	
øô ×$Ñ$¤iÐ0ò 	Ùð	ús   „A+B ÂB$Â#B$)ÚreturnÚNone)rŠ   rX   )rŠ   zLiteral['GET', 'POST'])NN)
r5   údict[str, str]r6   ú
str | Noner7   r�   r8   ÚobjectrŠ   rŒ   r   )
r7   r�   rA   údict[str, object]rB   z2dict[str, object] | list[dict[str, object]] | Noner8   rŽ   rŠ   rX   )rI   zstr | list[str]rA   r�   r8   rŽ   rŠ   r�   )rz   zhttpx.Responser{   zLiteLLMLoggingObj | Noner8   rŽ   rŠ   r   )ro   rX   rp   rY   r5   rŒ   rŠ   Ú	Exception)Ú__name__Ú
__module__Ú__qualname__r,   r   Ústaticmethodr&   r)   r:   rE   rl   r�   rr   Ú__classcell__)r    s   @r!   r   r   #   s4  ø„ Ø8Ðõ4ð òó ðóð #Ø#ð	TàðTð ðTð ð	Tð
 ðTð 
óTð, DHð	àðð +ðð Að	ð
 ðð 
óðJ4àðJ4ð +ðJ4ð ð	J4ð
 
óJ4ðXXà$ðXð .ðXð ð	Xð
 
óXðt%
àð%
ð ð%
ð  ð	%
ð
 
÷%
r"   r   c                óD   — dj                  d„ |D «       «      }d| › d|› d�S )Nz OR c              3  ó&   K  — | ]	  }d |› �–— Œ y­w)zsite:Nr%   )Ú.0Úds     r!   ú	<genexpr>z)_append_domain_filters.<locals>.<genexpr>#  s   è ø€ Ò'E¸¨%°¨s¬Ñ'Eùs   ‚ú(z) (ú))rT   )rI   re   Údomain_clausess      r!   rW   rW   "  s,   € Ø"ŸK™KÑ'E¸WÔ'EÓE€NØˆuˆg�S˜Ð(¨Ð*Ð*r"   c                óò   — t        | t        «      sy| j                  d«      }t        |t        «      sy|D ]?  }t        |t        «      sŒdD ]'  }t        |j                  |«      t        «      rŒ#d||<   Œ) ŒA y)záDefault missing/null title/url/snippet to "" on each result item in place.

    SearchResult requires these three fields; a degraded TinyFish result flows
    through with empty strings instead of failing the whole call.
    Nrx   )ÚtitleÚurlÚsnippetÚ )r>   r?   rU   rS   rX   )r|   Ú
results_inÚitemÚfields       r!   ru   ru   '  sr   € ô �h¤Ô%ØØ Ÿ™ YÓ/€JÜ�j¤$Ô'ØØò !ˆÜ˜$¤Ô%ØØ0ò 	!ˆEÜ˜dŸh™h u›o¬sÕ3Ø ��U’ñ	!ñ!r"   c                óx  — t        | dd«      }t        |t        «      sy|D ]—  }t        |t        «      sŒ|j	                  d«      }|j	                  d«      }|j	                  d«      }t        |t
        «      r|sŒZt        |t
        «      r|sŒmt        |t
        «      r|sŒ€t        j                  d|||«       Œ™ y)a  Re-fire TinyFish-side ``parameter_warnings`` as warnings.

    Defensive: skip silently on any shape we don't recognize so a malformed
    entry (or an early/partial rollout of the field) never throws.
    Schema per entry: ``{type, parameter, message, docs_url?}``.
    Úparameter_warningsNÚtypeÚ	parameterr„   z/TinyFish Search parameter_warning (%s) `%s`: %s)Úgetattrr>   rS   r?   rU   rX   r	   r^   )r}   Úwarnings_fieldÚentryÚwarning_typer©   r„   s         r!   rw   rw   :  s­   € ô %,¨FÐ4HÈ$Ó$O€NÜ�n¤dÔ+ØØò 
ˆÜ˜%¤Ô&ØØ$Ÿy™y¨Ó0ˆØ!ŸI™I kÓ2ˆ	ØŸ)™) IÓ.ˆÜ˜,¬Ô,±LØÜ˜)¤SÔ)±ØÜ˜'¤3Ô'©wØÜ×ÑØ=ØØØõ		
ñ
r"   )rI   rX   re   z	list[str]rŠ   rX   )r|   rŽ   rŠ   r‹   )r}   r   rŠ   r‹   )-Ú__doc__Ú
__future__r   ra   Útypingr   r   Úurllib.parser   ÚhttpxÚpydanticr   r   Úlitellm._loggingr	   Ú'litellm.litellm_core_utils.core_helpersr
   Ú*litellm.litellm_core_utils.litellm_loggingr   ÚLiteLLMLoggingObjÚ)litellm.llms.base_llm.chat.transformationr   Ú+litellm.llms.base_llm.search.transformationr   r   Úlitellm.secret_managers.mainr   r?   rX   rY   rZ   rc   r   Ú__annotations__rS   r   Ú	frozensetr   r   r   r   r   rW   ru   rw   r%   r"   r!   ú<module>r½      sÂ   ðòõ #ã ß !Ý "ã ß 1å +Ý LÝ SÝ F÷õ 8á(¨¨c°3¸±9¸uÑ3DÀtÑ3KÐ.KÑ)LÓMÐ �UÓ MÙ˜d 3™iÓ(€ˆ%Ó (Ù" 9¨S¡>Ó2€ˆuÓ 2à0Ð �eÓ 0ØAÐ �EÓ AØ Ð �eÓ  ô|
Ð+ô |
ó~+ó
!ô&
r"   