
    5Gi(                        d Z ddlmZ ddlmZ ddlmZmZ ddlZddl	Z	ddl
mZ ddlmZ ddlmZ d	Zd
Z G d de          Z G d de          Ze G d d                      ZddZddZddZddZdS ) z9Provider-agnostic LLM adapter with fake-response support.    )annotationsdeque)	dataclassfieldN)Any)	Anthropic)OpenAIg      >@zt

Return only valid JSON. Do not include markdown fences, explanations, or any text before or after the JSON object.c                      e Zd ZdZdS )LLMProviderErrorz<Raised when the provider or its credentials are unavailable.N__name__
__module____qualname____doc__     //root/projects/multi-agents-meeting/core/llm.pyr   r      s        FFFFr   r   c                      e Zd ZdZdS )LLMParseErrorzBRaised when model output cannot be parsed into the expected shape.Nr   r   r   r   r   r      s        LLLLr   r   c                      e Zd ZU dZ ee          Zded<   dZded<   e	d d            Z
d!dZd"dZd#dZd$dZd%dZd&dZd%dZd'dZdS )(	LLMClientz2Minimal LLM adapter used by the framework runtime.)default_factoryz
deque[Any]fake_responsesFbool	fake_mode	responses	list[Any]return'LLMClient'c                6     | t          |          d          S )NT)r   r   r   )clsr   s     r   from_fake_responseszLLMClient.from_fake_responses"   s    s%	"2"2dCCCCr   providerstrmodelsystem_promptuser_promptdict[str, Any]c                   | j         r^|                                 }t          |t                    r|S t          |t                    rt          |          S t          d          |                                }|dk    r|                     | j	        |||          S |dk    r|                     | j
        |||          S t          d|           )Nz!expected structured fake responseopenai)	generatorr&   r'   r(   	anthropicunsupported provider: )r   _pop_fake_response
isinstancedictr%   _parse_json_objectr   lower_generate_structured_with_retry$_generate_openai_structured_response'_generate_anthropic_structured_response
ValueError)selfr$   r&   r'   r(   responses         r   generate_structured_responsez&LLMClient.generate_structured_response&   s     > 	E..00H(D))  (C(( 4)(333 CDDD>>##x77C+'	 8    {""77F+'	 8    <(<<===r   r,   r   c                p    	  ||||          S # t           $ r |t          z   } ||||          cY S w xY w)N)r&   r'   r(   )r   JSON_RETRY_SUFFIX)r8   r,   r&   r'   r(   strengthened_user_prompts         r   r4   z)LLMClient._generate_structured_with_retryG   sr    	95S^____ 	 	 	'25F'F$9+4     	s    "55c                   | j         r:|                                 }t          |t                    st	          d          |S |                                }|dk    rI|                                 }|j                            |d|dd|dg          }t          |dd          S |d	k    rJ| 
                                }|j                            |d
|d|dg          }t          |j                  S t          d|           )Nzexpected text fake responser+   systemrolecontentuserr&   inputoutput_text r-      r&   
max_tokensr?   messagesr.   )r   r/   r0   r%   r   r3   _build_openai_clientr   creategetattr_build_anthropic_clientrK   _extract_anthropic_text_blocksrB   r7   )	r8   r$   r&   r'   r(   r9   client
completionmessages	            r   generate_text_responsez LLMClient.generate_text_responseX   s8    > 	..00Hh,, C#$ABBBO>>##x..00F)00%-@@#<< 1  J :}b999{""1133Fo,,$#)kBBC	 -  G 2'/BBB<(<<===r   r
   c                    t           j                            d          }|st          d          t           j                            d          }|rt	          ||t
                    S t	          |t
                    S )NOPENAI_API_KEYz0OPENAI_API_KEY is required for provider 'openai'OPENAI_BASE_URL)api_keybase_urltimeoutrX   rZ   )osenvirongetr   r
   DEFAULT_LLM_TIMEOUT_SECONDS)r8   rX   rY   s      r   rL   zLLMClient._build_openai_client|   su    *..!122 	W"#UVVV:>>"344 	c'HFabbbbg/JKKKKr   c                    |                                  }|j                            |d|dd|dg          }t          |dd          }t	          |          S )Nr?   r@   rC   rD   rF   rG   )rL   r   rM   rN   r2   )r8   r&   r'   r(   rQ   rR   texts          r   r5   z.LLMClient._generate_openai_structured_response   sq     **,,%,,!m<<K88 - 
 

 z="55!$'''r   r	   c                    t           j                            d          }|st          d          t	          |t
                    S )NANTHROPIC_API_KEYz6ANTHROPIC_API_KEY is required for provider 'anthropic'r[   )r\   r]   r^   r   r	   r_   )r8   rX   s     r   rO   z!LLMClient._build_anthropic_client   sB    *..!455 	]"#[\\\2MNNNNr   c                    |                                  }|j                            |d|d|dg          }t          |j                  }t          |          S )NrH   rC   r@   rI   )rO   rK   rM   rP   rB   r2   )r8   r&   r'   r(   rQ   rS   ra   s          r   r6   z1LLMClient._generate_anthropic_structured_response   sg     --///(( %+>>?	 ) 
 
 .go>>!$'''r   c                `    | j         st          d          | j                                         S )Nzno fake responses available)r   r   popleft)r8   s    r   r/   zLLMClient._pop_fake_response   s1    " 	? =>>>"**,,,r   N)r   r   r   r    )
r$   r%   r&   r%   r'   r%   r(   r%   r   r)   )
r,   r   r&   r%   r'   r%   r(   r%   r   r)   )
r$   r%   r&   r%   r'   r%   r(   r%   r   r%   )r   r
   )r&   r%   r'   r%   r(   r%   r   r)   )r   r	   )r   r   )r   r   r   r   r   r   r   __annotations__r   classmethodr#   r:   r4   rT   rL   r5   rO   r6   r/   r   r   r   r   r      s        <<!&u!=!=!=N====ID D D [D> > > >B   ""> "> "> ">HL L L L( ( ( ("O O O O( ( ( ( - - - - - -r   r   ra   r%   r   r)   c                   |                                  }|                    d          rt          |          }	 t          j        |          }ns# t          j        $ ra t          |          }|t          d          d 	 t          j        |          }n'# t          j        $ r}t          d          |d }~ww xY wY nw xY wt          |t                    st          d          |S )N```zmodel did not return valid JSONzexpected JSON object response)
strip
startswith_strip_fenced_blockjsonloadsJSONDecodeError_extract_first_json_objectr   r0   r1   )ra   strippedpayload	candidateexcs        r   r2   r2      s   zz||H5!! 1&x00	L*X&& L L L.x88	 ABBL	Lj++GG# 	L 	L 	L ABBK	L GL gt$$ =;<<<Ns5   A 0B? BB?B9$B44B99B?>B?c                   |                                  }t          |          dk    re|d                             d          rJ|d                             d          r/d                    |dd                                                   S | S )N   r   rj   
   )
splitlineslenrl   joinrk   )ra   liness     r   rm   rm      sw    OOE
5zzQ58..u55%):N:Nu:U:Uyyqt%%++---Kr   
str | Nonec                8   |                      d          }|dk    rd S d}d}d}t          |t          |                     D ]X}| |         }|r|rd}n|dk    rd}n|dk    rd}#|dk    rd},|dk    r|dz  }8|d	k    r|dz  }|dk    r| ||dz            c S Yd S )
N{rx   r   F\T"rz   })findranger|   )ra   startdepth	in_stringescapedindexchars          r   rq   rq      s    IIcNNE{{tEIGuc$ii(( / /E{ 	 "!	3;;IIS[[QJEES[[QJEzzEEAI-....4r   blocksr   c                    g }| D ]S}t          |dd          dk    rt          |dd          }t          |t                    r|                    |           Td                    |          S )NtyperG   ra   )rN   r0   r%   appendr}   )r   partsblockra   s       r   rP   rP      sv    E  5&"%%//ufb))dC   	LL775>>r   )ra   r%   r   r)   )ra   r%   r   r%   )ra   r%   r   r   )r   r   r   r%   )r   
__future__r   collectionsr   dataclassesr   r   rn   r\   typingr   r-   r	   r+   r
   r_   r<   RuntimeErrorr   r7   r   r   r2   rm   rq   rP   r   r   r   <module>r      s   ? ? " " " " " "       ( ( ( ( ( ( ( (  				                   #  M G G G G G| G G GM M M M MJ M M M S- S- S- S- S- S- S- S-l   *      <     r   