
    7~j                        d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 ddl
Z
ddlmZ ddlmZmZmZ ddlmZmZmZmZ  ej        e          Zd	Z G d
 d          ZdS )u  Perplexity Sonar-Pro client.

Two call shapes used by the pipeline:
  - city_intelligence(city, country, show_date)  → CityIntelligence
  - local_news(city, country, since_date, show_date)  → PerplexityNewsResult
    (also extracts competitor_warnings from the returned list)

The competitor-warnings extraction happens here because Perplexity is the only
provider that can scan current news sources; the Gemini fact-check stage only
verifies what comes out of this stage.
    )annotationsN)date)Any)settings)CityIntelligenceNewsItemPerplexityNewsResult)JsonExtractionError_build_semaphoreextract_jsonhttp_post_jsonz*https://api.perplexity.ai/chat/completionsc                  B    e Zd ZdZddZddZddZddZddZddZ	dS )PerplexityClientzDSonar-Pro HTTP client (OpenAI-compatible chat.completions endpoint).returnNonec                    t          j        t          j                  | _        t          t          j                  | _        d S )N)timeout)httpxAsyncClientr   PERPLEXITY_TIMEOUT_clientr   PERPLEXITY_MAX_CONCURRENCY_semselfs    q/Users/abstractioneditor2/ai-workstation/projects/automation/show-city-briefing-agent/app/providers/perplexity.py__init__zPerplexityClient.__init__,   s0    (1LMMM$X%HII			    c                H   K   | j                                          d {V  d S )N)r   acloser   s    r   closezPerplexityClient.close0   s2      l!!###########r   dict[str, str]c                (    dt           j         dddS )NzBearer zapplication/json)AuthorizationzContent-TypeAccept)r   PERPLEXITY_API_KEYr   s    r   _headerszPerplexityClient._headers3   s%    Dx'BDD.(
 
 	
r   systemstruserdict[str, Any]c          
       K   t           j        d|dd|dgdd}| j        4 d {V  t          | j        t
          |                                 |t          t           j                  d           d {V }d d d           d {V  n# 1 d {V swxY w Y   	 |d         d	         d
         d         }n)# t          t          f$ r}t          d          |d }~ww xY w|                    dg           }||dS )Nr(   )rolecontentr*   g?)modelmessagestemperature
perplexity)clienturlheaderspayloadr   provider_namechoicesr   messager.   z.perplexity: missing choices[0].message.content	citationstextr:   )r   PERPLEXITY_MODELr   r   r   _PERPLEXITY_URLr'   floatr   KeyError
IndexErrorr
   get)r   r(   r*   r6   datar<   excr:   s           r   _chatzPerplexityClient._chat:   s     .!f55D11 
 
 9 	 	 	 	 	 	 	 	'|#h9::*        D	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 		a	?1%i0;DD*% 	a 	a 	a%&VWW]``	aHH["--	9555s*   AB		
BBB6 6CCCcitycountry	show_dater   r   c          
     T  K   d}d| d| d|                                  d}|                     ||           d {V }	 t          |d                   }n?# t          $ r2 t                              d           |d         d d	         g g g d
}Y nw xY wt          |                    d          t                    r|d         g|d<   g }g }	t          |                    d          t                    rd |d         D             }	t          |d |                    dg           D             d d         d |                    dg           D             d d         t          |                    d          pd           |	d d         |d         d d         |d         d          S )Nu   You are a city-intelligence analyst for a stand-up comedy tour. Only return claims that you can support with current online sources. Do not speculate. If you cannot find a fact, omit it. Return STRICT JSON only — no commentary, no markdown fences.z I am preparing a comedy show in , z on a  . Produce a JSON object with these keys: points_of_pride (up to 5 strings the city is known for locally), sensitivities_to_avoid (up to 5 topics a visiting comedian should not poke at), local_conversation_today (one sentence describing the biggest local conversation in this city right now), audience_competitors (list of up to 5 other comedians or shows currently competing for this city's live-comedy audience).r(   r*   r<   z9perplexity city_intel returned non-JSON; storing raw text  )local_conversation_todaypoints_of_pridesensitivities_to_avoidaudience_competitorsrN   rP   c                ,    g | ]}t          |          S  r)   .0xs     r   
<listcomp>z6PerplexityClient.city_intelligence.<locals>.<listcomp>w   s    JJJa3q66JJJr   c                ,    g | ]}t          |          S rR   rS   rT   s     r   rW   z6PerplexityClient.city_intelligence.<locals>.<listcomp>z   s    OOOSVVOOOr      c                ,    g | ]}t          |          S rR   rS   rT   s     r   rW   z6PerplexityClient.city_intelligence.<locals>.<listcomp>{   s    #]#]#]qCFF#]#]#]r   rO   rM   i  r:   r;   )rF   rN   rO   rM   rP   raw_provider_payload)	isoformatrE   r   r
   loggerwarning
isinstancerB   r)   listr   )
r   rF   rG   rH   r(   r*   rC   parsedrowscompetitorss
             r   city_intelligencez"PerplexityClient.city_intelligenceU   s#     M 	It I Iw I IIDWDWDYDY I I I 	 ZZvDZ99999999		!$v,//FF" 	 	 	NNVWWW,0L$,?#%*,(*	 FFF	 fjj!233S99 	D)/0A)B(CF$%!#fjj!788$?? 	KJJ62H+IJJJKOOVZZ8I2-N-NOOOPRQRPRS#]#]FJJ?WY[4\4\#]#]#]^`_`^`#a%(4N)O)O)WSW%X%X!,RaR*.v,uu*=DQ\L]!^!^
 
 
 	
s   A 9BB
since_dater	   c               L  K   d}d| d| d|                                  d|                                  d| d|                                  d}|                     ||	           d {V }	 t          |d
                   }n/# t          $ r" t                              d           g g d}Y nw xY wt          |t                    sg g d}g }	|                    dg           d d         D ]}
|		                    t          t          |
                    dd          pd          d d         t          |
                    dd          pd          d d         t          |
                    d          pd           t          |
                    d          pd           d                     g }|                    dg           d d         D ]}
|	                    t          t          |
                    dd          pd          d d         t          |
                    dd          pd          d d         t          |
                    d          pd           t          |
                    d          pd           d                     t          |	|          S )Nu   You are a local-news summarizer for a visiting comedian. Cite the original URLs when possible. Return STRICT JSON only — no commentary, no markdown fences.zReturn local news for rJ   z published between z and a,  . Produce a JSON object with two keys:
  "recent_news": array of objects {title, summary, date, url} of up to 8 items most relevant to a visiting comedian.
  "competitor_warnings": array of objects {title, summary, date, url} of up to 5 items describing OTHER comedy shows or comedians performing in u    within ±3 days of z4. If no competition is found, return an empty array.rK   r<   z9perplexity local_news returned non-JSON; storing raw text)recent_newscompetitor_warningsrg      title i,  summaryrL   r   r4   F)rj   rl   r   r4   is_competitorrh   rY   T)r\   rE   r   r
   r]   r^   r_   dictrB   appendr   r)   r	   )r   rF   rG   re   rH   r(   r*   rC   ra   	news_rowsitemcompetitor_rowss               r   
local_newszPerplexityClient.local_news   s!     " 	AT A AW A A##%%A A,5,?,?,A,AA A "A A 8A7J7J7L7LA A A 	 ZZvDZ99999999	D!$v,//FF" 	D 	D 	DNNVWWW%'CCFFF	D &$'' 	D%'CCF	JJ}b11"1"5 	 	DX$((7B//5266tt<DHHY339r::5D5A&))1T22/400#       JJ4b99"1"= 	 	D""8$((7B//5266tt<DHHY339r::5D5A&))1T22/400"$ $ $     $	____s   .B )B0/B0N)r   r   )r   r"   )r(   r)   r*   r)   r   r+   )rF   r)   rG   r)   rH   r   r   r   )
rF   r)   rG   r)   re   r   rH   r   r   r	   )
__name__
__module____qualname____doc__r   r!   r'   rE   rd   rs   rR   r   r   r   r   )   s        NNJ J J J$ $ $ $
 
 
 
6 6 6 66*
 *
 *
 *
\-` -` -` -` -` -`r   r   )rw   
__future__r   asynciojsonloggingdatetimer   typingr   r   
app.configr   
app.modelsr   r   r	   app.providers._commonr
   r   r   r   	getLoggerrt   r]   r>   r   rR   r   r   <module>r      sJ  
 
 # " " " " "                               
            
	8	$	$>G` G` G` G` G` G` G` G` G` G`r   