
    ~j*                    B   d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZmZ ddlZddlmZ  ej        e          Z G d de          Z G d	 d
e          Z G d de          Z G d de          Zed/d            Zd0dZd1d"Zd2d%Zd&d'd(d3d-Zg d.ZdS )4a#  Shared HTTP + JSON helpers for the provider clients.

Keeps the four AI provider files small and uniformly secured:
  - secrets read from settings only; never inline.
  - one httpx.AsyncClient per provider (process-lifetime).
  - all providers accept the configured *_MAX_CONCURRENCY bound.
    )annotationsN)asynccontextmanager)AnyAsyncIteratorOptional)redact_secretsc                      e Zd ZdZdS )JsonExtractionErrorz<Raised when a provider response could not be parsed as JSON.N__name__
__module____qualname____doc__     n/Users/abstractioneditor2/ai-workstation/projects/automation/show-city-briefing-agent/app/providers/_common.pyr
   r
      s        FFFFr   r
   c                  $     e Zd ZdZd
 fd	Z xZS )ProviderHttpErrorz7Raised when a provider returns a non-2xx HTTP response.providerstrstatusintbodyreturnNonec                    || _         || _        t          |          d d         | _        t	                                          | d|            d S )Ni  z	 -> HTTP )r   r   r   r   super__init__)selfr   r   r   	__class__s       r   r   zProviderHttpError.__init__    sU     "4(($/	H77v7788888r   )r   r   r   r   r   r   r   r   )r   r   r   r   r   __classcell__)r    s   @r   r   r      sC        AA9 9 9 9 9 9 9 9 9 9r   r   c                      e Zd ZdZdS )RetryableProviderErroru<   5xx / 429 / timeout — retried by the pipeline retry layer.Nr   r   r   r   r#   r#   '   s        FFDr   r#   c                      e Zd ZdZdS )NonRetryableProviderErroru1   4xx (other than 429) — fail-fast, do not retry.Nr   r   r   r   r%   r%   ,   s        ;;Dr   r%   	semaphoreasyncio.Semaphorer   AsyncIterator[None]c               n   K   | 4 d {V  d W V  d d d           d {V  d S # 1 d {V swxY w Y   d S )Nr   )r&   s    r   boundedr*   1   s                                           s   $
..limitr   c                F    t          j        t          d|                     S )N   )asyncio	Semaphoremax)r+   s    r   _build_semaphorer1   7   s    SE]]+++r   clienthttpx.AsyncClienturlr   headersdict[str, str]payloaddict[str, Any]timeoutfloatprovider_namec                  K   	 |                      ||||           d{V }nS# t          j        t          j        f$ r5}t                              d||           t          |dd|           d}~ww xY w|j        }|j        }	|dk    s|dk    rt          |||	          |dk    rt          |||	          	 |
                                S # t          $ r}t          | d	| d
          |d}~ww xY w)zPOST JSON; on success return the decoded JSON body.

    Raises:
      RetryableProviderError on network error, 429, or any 5xx.
      NonRetryableProviderError on any other non-2xx status.
    )jsonr5   r9   Nz%s HTTP error: %sr   z	network: i  i  i  z returned non-JSON body (HTTP ))posthttpxTimeoutExceptionNetworkErrorloggerwarningr#   status_codetextr%   r=   
ValueErrorr
   )
r2   r4   r5   r7   r9   r;   responseexcr   r   s
             r   http_post_jsonrJ   ;   sD     JSwQXYYYYYYYY"E$67 J J J*M3???$]A7H37H7HIIIJ !F=D}}#$]FDAAA}}'vtDDD}}   !EEFEEE
 
	s,   $ A40A//A4:C 
C3C..C3rF   r   c                f   | pd                                 } | st          d          	 t          j        |           S # t          $ r Y nw xY wt          j        d| t
          j        t
          j        z            }|r8	 t          j        |	                    d                    S # t          $ r Y nw xY wdD ]m\  }}| 
                    |          }|                     |          }|dk    r8||k    r2	 t          j        | ||dz                      c S # t          $ r Y iw xY wnt          d          )a:  Extract a JSON value from a provider response that may contain prose.

    Tries (in order):
      1. strict JSON parse of the whole text (when the model was called with
         response_format=json_schema / json_object).
      2. The first ``` fenced JSON block.
      3. The first {...} or [...] substring.
     zempty responsez```(?:json)?\s*(.*?)\s*```r-   )){})[]zno JSON found in response)stripr
   r=   loadsrG   researchDOTALL
IGNORECASEgroupfindrfind)rF   fenceopenercloserstartends         r   extract_jsonr`   ]   sg    JBD 4!"2333z$   I3T29r};TUUE 	:ekk!nn--- 	 	 	D	2  		&!!jj  B;;3;;z$uS1W}"566666   
9
:
::s3   = 
A
	A
=&B$ $
B10B12D
D D )choicesr   messagecontentT)choices_pathdedent_responserd   tuple[str, ...]re   boolc                  K   t          | |||||           d{V }|}	|D ]}
t          |
t                    rEt          |	t                    r|
t	          |	          k    rt          | d|
           |	|
         }	\t          |	t                    r|
|	vrt          | d|
          |	|
         }	t          |	t                    st          | d          |	S )zRun a chat-completions-style call and return the assistant text.

    `choices_path` is the dotted path into the response body for the assistant
    text. Adjust it for providers that nest it differently.
    )r2   r4   r5   r7   r9   r;   Nz: choices path missing at z#: assistant content is not a string)rJ   
isinstancer   listlenr
   dictr   )r2   r4   r5   r7   r9   r;   rd   re   datanodekeys              r   complete_chatrp   ~   s1        #        D D  c3 	dD)) ]SCII-=-=)]*[*[VY*[*[\\\9DDdD)) _S__)]*]*]VY*]*]^^^9DDdC   Y!]"W"W"WXXXKr   )	r*   rp   r`   rJ   r
   r%   r   r#   r1   )r&   r'   r   r(   )r+   r   r   r'   )r2   r3   r4   r   r5   r6   r7   r8   r9   r:   r;   r   r   r8   )rF   r   r   r   )r2   r3   r4   r   r5   r6   r7   r8   r9   r:   r;   r   rd   rf   re   rg   r   r   )r   
__future__r   r.   r=   loggingrT   
contextlibr   typingr   r   r   r@   app.loggingr   	getLoggerr   rC   rG   r
   RuntimeErrorr   r#   r%   r*   r1   rJ   r`   rp   __all__r   r   r   <module>ry      s    # " " " " "    				 * * * * * * / / / / / / / / / /  & & & & & &		8	$	$G G G G G* G G G9 9 9 9 9 9 9 9	 	 	 	 	. 	 	 	
	 	 	 	 	 1 	 	 	
    
, , , ,   D; ; ; ;R %I $ $ $ $ $ $R
 
 
r   