
    4Qj                     t   d dl mZ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 ddlmZmZmZmZmZmZmZmZ ddlmZmZmZmZ  ed	d
      Z ed      fdedz  ddfdZej?                  e      dedede	fd       Z ej?                  e      dedede	fd       Z!ejE                  d      de#fd       Z$ejE                  d      de#fd       Z%ejE                  d ee      g      de#fd       Z&ejO                  d ee      g      d)dedz  de#fd       Z(ejO                  de ee      g       d!ede#fd"       Z)ejO                  d#e ee      g       d!ede#fd$       Z*ejO                  d%e ee      g       d!ede#fd&       Z+ejO                  d'e ee      g       d!ede#fd(       Z,y)*    )DependsFastAPIHeaderHTTPExceptionRequest)ResponseValidationError)JSONResponse   )settings)LLMGenerationError)GenerateItineraryRequestGenerateItineraryResponseRecommendStayRequestRecommendStayResponseSuggestQuestionsRequestSuggestQuestionsResponseSuggestResponseRequestSuggestResponseResponse)	itinerary	questionsreplystayzmeridian-aiz0.1.0)titleversionN)defaultauthorizationreturnc                 l    t         j                  sy| dt         j                   k7  rt        dd      y)a  Baseline service-to-service auth. Meridian-Backend authenticates with a shared
    bearer token - never the end user's own frontend session token, which this
    service never sees and has no way to validate (see README.md 4.1 for why that
    split matters). This is the simple half of that section's design; swap the token
    comparison below for the signed-request-with-nonce scheme described there if a
    static shared secret ever stops being a strong enough guarantee.
    NzBearer i  z Invalid or missing service token)status_codedetail)r   service_tokenr   )r   s    ./var/www/html/meridian/meridian-ai/app/main.pyrequire_service_tokenr#      s9     !!'("8"8!9::4VWW ;    _requestexcc                 P    t        ddd|j                  |j                  di      S )N  errorLLM_GENERATION_FAILEDcodemessagedetailsr   content)r	   r-   r    r%   r&   s     r"   handle_llm_generation_errorr2   $   s-    #:s{{_b_i_ijk r$   c           	      :    t        ddddt        |      di      S )Nr(   r)   LLM_RESPONSE_INVALIDz-Model output did not match the expected shaper+   r/   )r	   strr1   s     r"    handle_response_validation_errorr6   ,   s>     #9Fu  CF  GJ  CK  L  M r$   z/modelsc                  >    ddl m}   |        t        j                  dS )u   Returns available LLM providers and their models. No auth required — the
    frontend reads this to populate the model selector in the generate-itinerary modal.
    r
   list_providers)	providersr   )providers.factoryr9   r   llm_providerr8   s    r"   modelsr=   8   s    
 2')h6K6KLLr$   z/healthc                  n    ddl m}   |        }dt        j                  |j                  |j
                  dS )Nr
   get_providerok)statusprimary_provideractive_providersexhausted_providers)r;   r@   r   r<   active	exhaustedr@   ps     r"   healthrJ   A   s0    /A$11HH {{	 r$   z/providers/status)dependenciesc                  d    ddl m}   |        }|j                  |j                  |j                  dS )zFShows which providers are active and which have been marked exhausted.r
   r?   )
configuredrF   rG   )r;   r@   namesrF   rG   rH   s     r"   providers_statusrO   M   s-     0Agg(([[ r$   z/providers/resetnamec                 d    ddl m}  |       }|j                  |        | xs d|j                  dS )zClears exhausted state so a topped-up provider is tried again.
    Pass ?name=openai to reset just one, or call with no param to reset all.
    r
   r?   all)resetrF   )r;   r@   rS   rF   )rP   r@   rI   s      r"   providers_resetrT   Y   s,    
 0AGGDM]Uahh77r$   z/generate_itinerary)response_modelrK   payloadc                 ,    t        j                  |       S N)r   generate_itineraryrV   s    r"   generate_itinerary_endpointr[   d   s     ''00r$   z/suggest_responsec                 ,    t        j                  |       S rX   )r   suggest_responserZ   s    r"   suggest_response_endpointr^   m   s     !!'**r$   z/recommend_stayc                 ,    t        j                  |       S rX   )r   recommend_stayrZ   s    r"   recommend_stay_endpointra   v   s     w''r$   z/suggest_questionsc                 ,    t        j                  |       S rX   )r   suggest_questionsrZ   s    r"   suggest_questions_endpointrd      s     &&w//r$   rX   )-fastapir   r   r   r   r   fastapi.exceptionsr   fastapi.responsesr	   configr   
exceptionsr   schemasr   r   r   r   r   r   r   r   servicesr   r   r   r   appr5   r#   exception_handlerr2   r6   getdictr=   rJ   rO   postrT   r[   r^   ra   rd    r$   r"   <module>rr      s   D D 6 *  *	 	 	 8 7M73 7=T6J Xt Xt X )*' 8J |  + ./w =T Ye  0 M M M    	G4I,J+KL$  M 
G4I,J+KL8#* 8 8 M8 ,/01 
 
1)A 1d 1
1 */01 
 
+'= +$ +
+ (/01 
 
(%9 (d (
( +/01 
 
0(? 0D 0
0r$   