o
    \۷ix`                     @  s8  d Z ddlmZ ddlZddl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mZmZmZ ddlZddlmZmZ zddlmZ dd	lmZ dd
lmZ ddlmZ W n eyq   G dd dZ e  Ze  Ze  Ze!Z"Y nw dCddZ#dDddZ$G dd de%eZ&G dd deZ'edZ(G dd deZ)G d d! d!e)Z*G d"d# d#eZ+G d$d% d%e%eZ,G d&d' d'eZ-G d(d) d)eZ.G d*d+ d+e)Z/G d,d- d-e)Z0G d.d/ d/eZ1G d0d1 d1eZ2G d2d3 d3eZ3G d4d5 d5eZ4G d6d7 d7Z5G d8d9 d9eZ6G d:d; d;Z7dEd>d?ZG d@dA dAe%eZ8g dBZ9dS )Fu  Python wrapper around the Rust implementation of *harmony*.

The heavy lifting (tokenisation, rendering, parsing, …) is implemented in
Rust.  The thin bindings are available through the private ``openai_harmony``
extension module which is compiled via *maturin* / *PyO3*.

This package provides a small, typed convenience layer that mirrors the public
API of the Rust crate so that it can be used from Python code in an
idiomatic way (``dataclasses``, ``Enum``s, …).
    )annotationsN)Enum)AbstractSetAny
CollectionDictListLiteralOptionalPatternSequenceTypeVarUnion)	BaseModelField   HarmonyError)PyHarmonyEncoding)PyStreamableParser)load_harmony_encodingc                   @  s   e Zd ZdddZdS )	_StubnamestrreturnNonec                 C  s   t d)NzThe compiled harmony bindings are not available. Make sure to build the project with `maturin develop` before running this code.)RuntimeError)selfr    r   M/home/ubuntu/vllm_env/lib/python3.10/site-packages/openai_harmony/__init__.py__getattr__6   s   z_Stub.__getattr__N)r   r   r   r   )__name__
__module____qualname__r    r   r   r   r   r   5       r   tokensfrozenset[str]r   Pattern[str]c                 C  s&   d dd | D }td| dS )N|c                 s  s    | ]}t |V  qd S N)reescape).0tokenr   r   r   	<genexpr>D   s    z'_special_token_regex.<locals>.<genexpr>())joinr*   compile)r%   innerr   r   r   _special_token_regexC   s   r4   r-   r   r   c                 C  s   t d| d|  d|  d)Nz;Encountered text corresponding to disallowed special token zp.
If you want this text to be encoded as a special token, pass it to `allowed_special`, e.g. `allowed_special={'z', ...}`.
If you want this text to be encoded as normal text, disable the check for this token by passing `disallowed_special=(enc.special_tokens_set - {'zR'})`.
To disable this check for all special tokens, pass `disallowed_special=()`.
r   )r-   r   r   r   raise_disallowed_special_tokenH   s   r5   c                   @  s2   e Zd ZdZdZdZdZdZdZe	dddZ
dS )Rolez6The role of a message author (mirrors ``chat::Role``).user	assistantsystem	developertoolvalueobjectr   'Role'c                 C  s   t d|)NzUnknown role: )
ValueError)clsr<   r   r   r   	_missing_b      zRole._missing_N)r<   r=   r   r>   )r!   r"   r#   __doc__USER	ASSISTANTSYSTEM	DEVELOPERTOOLclassmethodrA   r   r   r   r   r6   Y   s    r6   c                   @  s0   e Zd ZU ded< dZded< edd	d
ZdS )Authorr6   roleNOptional[str]r   r   r   'Author'c                 C  s   | ||dS )NrK   r   r   )r@   rK   r   r   r   r   newk      z
Author.new)rK   r6   r   r   r   rM   )r!   r"   r#   __annotations__r   rI   rO   r   r   r   r   rJ   g   s
   
 rJ   Tc                   @  s   e Zd ZdddZdS )Contentr   Dict[str, Any]c                 C  s   t r)   )NotImplementedErrorr   r   r   r   to_dictw   s   zContent.to_dictNr   rT   )r!   r"   r#   rW   r   r   r   r   rS   v   r$   rS   c                   @  s    e Zd ZU ded< dddZdS )	TextContentr   textr   rT   c                 C  s   d| j dS )NrZ   typerZ   rZ   rV   r   r   r   rW   ~   s   zTextContent.to_dictNrX   )r!   r"   r#   rQ   rW   r   r   r   r   rY   {   s   
 rY   c                   @  s<   e Zd ZU ded< ded< dZded< e	ddd	d
ZdS )ToolDescriptionr   r   descriptionNOptional[dict]
parametersr   'ToolDescription'c                 C  s   | |||dS )N)r   r_   ra   r   )r@   r   r_   ra   r   r   r   rO      s   zToolDescription.newr)   )r   r   r_   r   ra   r`   r   rb   )r!   r"   r#   rQ   ra   rI   rO   r   r   r   r   r^      s   
 r^   c                   @     e Zd ZdZdZdZdS )ReasoningEffortLowMediumHighN)r!   r"   r#   LOWMEDIUMHIGHr   r   r   r   rd          rd   c                   @  s,   e Zd ZU ded< ded< eddd	Zd
S )ChannelConfig	List[str]valid_channelsboolchannel_requiredchannelsr   'ChannelConfig'c                 C  s   | |ddS )NT)rn   rp   r   )r@   rq   r   r   r   require_channels   rP   zChannelConfig.require_channelsN)rq   rm   r   rr   )r!   r"   r#   rQ   rI   rs   r   r   r   r   rl      s
   
 rl   c                   @  sF   e Zd ZU ded< dZded< ded< edd
dZedddZdS )ToolNamespaceConfigr   r   NrL   r_   zList[ToolDescription]toolsr   'ToolNamespaceConfig'c                  C  "   ddl m}  | d}tdi |S )Nr   get_tool_namespace_configbrowserr   openai_harmonyry   rt   _get_tool_namespace_configcfgr   r   r   rz         zToolNamespaceConfig.browserc                  C  rw   )Nr   rx   pythonr   r{   r}   r   r   r   r      r   zToolNamespaceConfig.python)r   rv   )r!   r"   r#   rQ   r_   staticmethodrz   r   r   r   r   r   rt      s   
 rt   c                   @  s   e Zd ZU dZded< ejZded< dZded< dZ	ded	< e
d
d dZded< dZded< ed4ddZd5ddZd6ddZd7ddZd8ddZd9d d!Zd:d$d%Zd;d(d)Zd4d*d+Zd4d,d-Zd<d/d0Zed=d2d3ZdS )>SystemContentz:You are ChatGPT, a large language model trained by OpenAI.rL   model_identityzOptional[ReasoningEffort]reasoning_effortNconversation_start_datez2024-06knowledge_cutoffc                   C  s   t g dS )N)analysis
commentaryfinal)rl   rs   r   r   r   r   <lambda>   s    zSystemContent.<lambda>default_factoryzOptional[ChannelConfig]channel_config(Optional[dict[str, ToolNamespaceConfig]]ru   r   'SystemContent'c                 C     |  S r)   r   r@   r   r   r   rO         zSystemContent.newr   c                 C  
   || _ | S r)   )r   )r   r   r   r   r   with_model_identity      z!SystemContent.with_model_identityrd   c                 C  r   r)   )r   )r   r   r   r   r   with_reasoning_effort      z#SystemContent.with_reasoning_effortc                 C  r   r)   )r   )r   r   r   r   r   with_conversation_start_date   r   z*SystemContent.with_conversation_start_datec                 C  r   r)   )r   )r   r   r   r   r   with_knowledge_cutoff   r   z#SystemContent.with_knowledge_cutoffrl   c                 C  r   r)   )r   )r   r   r   r   r   with_channel_config   r   z!SystemContent.with_channel_configrq   	list[str]c                 C  s   t || _| S r)   )rl   rs   r   )r   rq   r   r   r   with_required_channels      z$SystemContent.with_required_channels	ns_configrt   c                 C      | j d u ri | _ || j |j< | S r)   ru   r   r   r   r   r   r   
with_tools      
zSystemContent.with_toolsc                 C     |  t S r)   )r   rt   rz   rV   r   r   r   with_browser_tool      zSystemContent.with_browser_toolc                 C  r   r)   )r   rt   r   rV   r   r   r   with_python_tool   r   zSystemContent.with_python_tooldictc                 C     | j dd}d|d< |S )NTexclude_nonesystem_contentr\   
model_dumpr   outr   r   r   rW         zSystemContent.to_dictrawc                 C     | di |S Nr   r   r@   r   r   r   r   	from_dict   rB   zSystemContent.from_dict)r   r   )r   r   r   r   )r   rd   r   r   )r   r   r   r   )r   r   r   r   )r   rl   r   r   )rq   r   r   r   )r   rt   r   r   r   r   )r   r   r   r   )r!   r"   r#   r   rQ   rd   ri   r   r   r   r   r   ru   rI   rO   r   r   r   r   r   r   r   r   r   rW   r   r   r   r   r   r      s0   
 










r   c                   @  sj   e Zd ZU dZded< dZded< eddd	ZdddZdddZ	dddZ
dddZedddZdS ) DeveloperContentNrL   instructionsr   ru   r   'DeveloperContent'c                 C  r   r)   r   r   r   r   r   rO      r   zDeveloperContent.newr   c                 C  r   r)   )r   )r   r   r   r   r   with_instructions  r   z"DeveloperContent.with_instructionsr   rt   c                 C  r   r)   r   r   r   r   r   r     r   zDeveloperContent.with_toolsSequence[ToolDescription]c                 C  s   |  tdd t|dS )N	functions)r   r_   ru   )r   rt   list)r   ru   r   r   r   with_function_tools  s   z$DeveloperContent.with_function_toolsr   c                 C  r   )NTr   developer_contentr\   r   r   r   r   r   rW     r   zDeveloperContent.to_dictr   c                 C  r   r   r   r   r   r   r   r     rB   zDeveloperContent.from_dict)r   r   )r   r   r   r   )r   rt   r   r   )ru   r   r   r   r   )r   r   r   r   )r!   r"   r#   r   rQ   ru   rI   rO   r   r   r   rW   r   r   r   r   r   r      s   
 



r   c                   @  s   e Zd ZU ded< eedZded< dZded< dZded	< dZ	ded
< e
d)ddZe
d*ddZe
d+ddZd,ddZd-ddZd.ddZd/dd Zd0d"d#Zd1d$d%Ze
d2d'd(ZdS )3MessagerJ   authorr   zList[Content]contentNrL   channel	recipientcontent_typeUnion[str, Content]r   	'Message'c                 C  s"   t |tr
t|d}| ||gdS )Nr]   r   r   )
isinstancer   rY   )r@   r   r   r   r   r   from_author_and_content,  s   

zMessage.from_author_and_contentrK   r6   c                 C  s   |  t|d|S )NrK   )r   rJ   )r@   rK   r   r   r   r   from_role_and_content4  s   zMessage.from_role_and_contentcontentsSequence[Content]c                 C  s   | t |dt|dS )Nr   r   )rJ   r   )r@   rK   r   r   r   r   from_role_and_contents:  s   zMessage.from_role_and_contentsc                 C  s$   t |tr
t|d}| j| | S )Nr]   )r   r   rY   r   append)r   r   r   r   r   adding_contentD  s   

zMessage.adding_contentr   c                 C  r   r)   )r   )r   r   r   r   r   with_channelJ  r   zMessage.with_channelc                 C  r   r)   )r   )r   r   r   r   r   with_recipientN  r   zMessage.with_recipientc                 C  r   r)   )r   )r   r   r   r   r   with_content_typeR  r   zMessage.with_content_typerT   c                 C  sb   i | j  ddd | jD i}| jd ur| j|d< | jd ur%| j|d< | jd ur/| j|d< |S )Nr   c                 S     g | ]}|  qS r   rW   )r,   cr   r   r   
<listcomp>]      z#Message.to_dict.<locals>.<listcomp>r   r   r   )r   r   r   r   r   r   r   r   r   r   rW   Z  s   





zMessage.to_dictc                 C     t |  S r)   jsondumpsrW   rV   r   r   r   to_jsong  r   zMessage.to_jsondatac                 C  s   t |d }t||dd}g }|d }t|tr d|dg}|D ]>}|ddkr6|tdi | q"|ddkrH|tdi | q"|dd	krZ|tdi | q"t	d
| | ||d}|d|_
|d|_|d|_|S )NrK   r   rN   r   rZ   r[   r\   r   r   zUnknown content variant: r   r   r   r   r   )r6   rJ   getr   r   r   rY   r   r   r?   r   r   r   )r@   r   rK   r   r   raw_contentr   msgr   r   r   r   j  s&   
zMessage.from_dict)r   rJ   r   r   r   r   )rK   r6   r   r   r   r   )rK   r6   r   r   r   r   )r   r   r   r   )r   r   r   r   )r   r   r   r   )r   r   r   r   rX   r   r   )r   rT   r   r   )r!   r"   r#   rQ   r   r   r   r   r   r   rI   r   r   r   r   r   r   r   rW   r   r   r   r   r   r   r   !  s(   
 
	




r   c                   @  sX   e Zd ZU eedZded< edddZd	d
 Z	dddZ
dddZedddZdS )Conversationr   List[Message]messagesSequence[Message]r   'Conversation'c                 C  s   | t |dS )Nr   )r   )r@   r   r   r   r   from_messages  rB   zConversation.from_messagesc                 C  
   t | jS r)   )iterr   rV   r   r   r   __iter__     
zConversation.__iter__rT   c                 C  s   ddd | j D iS )Nr   c                 S  r   r   r   r,   mr   r   r   r     r   z(Conversation.to_dict.<locals>.<listcomp>r   rV   r   r   r   rW     s   zConversation.to_dictr   c                 C  r   r)   r   rV   r   r   r   r     r   zConversation.to_jsonpayloadc                 C  s"   t |}| dd |d D dS )Nc                 S     g | ]}t |qS r   r   r   r   r   r   r   r         z*Conversation.from_json.<locals>.<listcomp>r   r   )r   loads)r@   r   r   r   r   r   	from_json  s   
zConversation.from_jsonN)r   r   r   r   rX   r   )r   r   r   r   )r!   r"   r#   r   r   r   rQ   rI   r   r   rW   r   r   r   r   r   r   r     s   
 

r   c                   @     e Zd ZU dZded< dS )RenderConversationConfigTro   auto_drop_analysisN)r!   r"   r#   r   rQ   r   r   r   r   r        
 r   c                   @  r   )RenderOptionsFro   conversation_has_function_toolsN)r!   r"   r#   r   rQ   r   r   r   r   r     r   r   c                   @  s   e Zd ZdZdDddZedEdd	ZejdFddZ		dGdHddZ
	dGdIddZ	dGdIddZ	dGdJdd Z	dGd!d"dKd*d+ZdLd,d-Ze d.d/dMd6d7ZdNdOd:d;ZdPd>d?ZdQd@dAZdQdBdCZdS )RHarmonyEncodingz?High-level wrapper around the Rust ``PyHarmonyEncoding`` class.r3   _PyHarmonyEncodingc                 C  s
   || _ d S r)   )_inner)r   r3   r   r   r   __init__  r   zHarmonyEncoding.__init__r   r   c                 C     | j jS r)   )r  r   rV   r   r   r   r        zHarmonyEncoding.nameset[str]c                 C  s   t | j S r)   )setr  special_tokensrV   r   r   r   special_tokens_set  rB   z"HarmonyEncoding.special_tokens_setNconversationr   next_turn_roler6   config"Optional[RenderConversationConfig]	List[int]c                 C  s8   |du r	ddi}nd|j i}| jj| t|j|dS )z
        Render a conversation for completion.
        Args:
            conversation: Conversation object
            next_turn_role: Role for the next turn
            config: Optional RenderConversationConfig (default auto_drop_analysis=True)
        Nr   T)conversation_jsonr
  r  )r   r  "render_conversation_for_completionr   r   r<   )r   r	  r
  r  config_dictr   r   r   r    s   

z2HarmonyEncoding.render_conversation_for_completionc                 C  0   |du r	ddi}nd|j i}| jj| |dS )z3Render a conversation without appending a new role.Nr   Tr  r  )r   r  render_conversationr   r   r	  r  r  r   r   r   r       

z#HarmonyEncoding.render_conversationc                 C  r  )z#Render a conversation for training.Nr   Tr  )r   r   render_conversation_for_trainingr   r  r   r   r   r    r  z0HarmonyEncoding.render_conversation_for_trainingmessager   render_optionsOptional[RenderOptions]c                 C  r  )z$Render a single message into tokens.Nr   F)message_jsonr  )r   r  renderr   )r   r  r  render_options_dictr   r   r   r    s   
zHarmonyEncoding.renderTstrictr%   Sequence[int]rK   Optional[Role] | Noner  ro   r   c                C  s:   | j t||d u rd nt|j|}dd t|D S )Nc                 S  r   r   r   r   r   r   r   r     r   zIHarmonyEncoding.parse_messages_from_completion_tokens.<locals>.<listcomp>)r  %parse_messages_from_completion_tokensr   r   r<   r   r   )r   r%   rK   r  raw_jsonr   r   r   r!  
  s   z5HarmonyEncoding.parse_messages_from_completion_tokensc                 C  s   | j t|S )zDecode a list of tokens into a UTF-8 string. Will raise an error if the tokens result in invalid UTF-8. Use decode if you want to replace invalid UTF-8 with the unicode replacement character.)r  decode_utf8r   )r   r%   r   r   r   r#    s   zHarmonyEncoding.decode_utf8all)allowed_specialdisallowed_specialrZ   r%  !Literal['all'] | AbstractSet[str]r&   Literal['all'] | Collection[str]	list[int]c                C  s   |dkr| j }|dkr| j t| }|r,t|tst|}t|| }r,t|  z
| j	|t
|W S  tyR   |	dddd}| j	|t
| Y S w )aH  Encodes a string into tokens.

        Special tokens are artificial tokens used to unlock capabilities from a model,
        such as fill-in-the-middle. So we want to be careful about accidentally encoding special
        tokens, since they can be used to trick a model into doing something we don't want it to do.

        Hence, by default, encode will raise an error if it encounters text that corresponds
        to a special token. This can be controlled on a per-token level using the `allowed_special`
        and `disallowed_special` parameters. In particular:
        - Setting `disallowed_special` to () will prevent this function from raising errors and
          cause all text corresponding to special tokens to be encoded as natural text.
        - Setting `allowed_special` to "all" will cause this function to treat all text
          corresponding to special tokens to be encoded as special tokens.

        ```
        >>> enc.encode("hello world")
        [31373, 995]
        >>> enc.encode("<|endoftext|>", allowed_special={"<|endoftext|>"})
        [50256]
        >>> enc.encode("<|endoftext|>", allowed_special="all")
        [50256]
        >>> enc.encode("<|endoftext|>")
        # Raises ValueError
        >>> enc.encode("<|endoftext|>", disallowed_special=())
        [27, 91, 437, 1659, 5239, 91, 29]
        ```
        r$  zutf-16surrogatepassreplace)r  r  r   	frozensetr4   searchr5   groupr  encoder   UnicodeEncodeErrordecode)r   rZ   r%  r&  matchr   r   r   r/    s   "
zHarmonyEncoding.encoder+  errorsc                 C  s"   t | jt|}|jd|dS )aS  Decodes a list of tokens into a string.

        WARNING: the default behaviour of this function is lossy, since decoded bytes are not
        guaranteed to be valid UTF-8. You can use `decode_utf8` if you want to raise an error on invalid UTF-8.

        ```
        >>> enc.decode([31373, 995])
        'hello world'
        ```
        zutf-8)r3  )bytesr  decode_bytesr   r1  )r   r%   r3  r   r   r   r   r1  N  s   zHarmonyEncoding.decoder-   intc                 C  s   | j |S )z1Returns if an individual token is a special token)r  is_special_tokenr   r-   r   r   r   r7  \  rP   z HarmonyEncoding.is_special_tokenc                 C  
   | j  S r)   )r  stop_tokensrV   r   r   r   r:  b  r   zHarmonyEncoding.stop_tokensc                 C  r9  r)   )r  !stop_tokens_for_assistant_actionsrV   r   r   r   r;  e  r   z1HarmonyEncoding.stop_tokens_for_assistant_actions)r3   r   r   )r   r  r)   )r	  r   r
  r6   r  r  r   r  )r	  r   r  r  r   r  )r  r   r  r  r   r  )r%   r  rK   r   r  ro   r   r   )r%   r  r   r   )rZ   r   r%  r'  r&  r(  r   r)  )r+  )r%   r  r3  r   r   r   )r-   r6  r   ro   r   r  )r!   r"   r#   rC   r  propertyr   	functoolscached_propertyr  r  r  r  r  r!  r#  r  r/  r1  r7  r:  r;  r   r   r   r   r     s6    
	
2

r   c                   @  rc   )StreamStateExpectStartHeaderrS   N)r!   r"   r#   EXPECT_STARTHEADERCONTENTr   r   r   r   r@  i  rk   r@  c                   @  s   e Zd ZdZddd1ddZd2ddZd3ddZed4ddZed5ddZ	ed6ddZ
ed6ddZed7d!d"Zed8d$d%Zed9d'd(Zed:d*d+Zed6d,d-Zed6d.d/Zd0S );StreamableParserz*Incremental parser over completion tokens.Tr  encodingr   rK   Role | Noner  ro   r   r   c                C  s*   |d ur	t |jnd }t|j||| _d S r)   )r   r<   _PyStreamableParserr  )r   rG  rK   r  role_strr   r   r   r  r  s   zStreamableParser.__init__r-   r6  'StreamableParser'c                 C  s   | j | | S r)   )r  processr8  r   r   r   rL  |  r   zStreamableParser.processc                 C  s   | j   | S r)   )r  process_eosrV   r   r   r   rM    s   
zStreamableParser.process_eosr   c                 C  r  r)   )r  current_contentrV   r   r   r   rN    r  z StreamableParser.current_contentOptional[Role]c                 C  s   | j j}|d urt|S d S r)   )r  current_roler6   r   r   r   r   r   rP       zStreamableParser.current_rolerL   c                 C  r  r)   )r  current_content_typerV   r   r   r   rS    r  z%StreamableParser.current_content_typec                 C  r  r)   )r  last_content_deltarV   r   r   r   rT    r  z#StreamableParser.last_content_deltar   c                 C  s   | j j}dd t|D S )Nc                 S  r   r   r   r   r   r   r   r     r   z-StreamableParser.messages.<locals>.<listcomp>)r  r   r   r   rQ  r   r   r   r     rR  zStreamableParser.messagesr  c                 C  r  r)   )r  r%   rV   r   r   r   r%     r  zStreamableParser.tokensrT   c                 C  s   t | jjS )z>Return a JSON string representing the parser's internal state.)r   r   r  staterV   r   r   r   
state_data  s   zStreamableParser.state_datar@  c                 C  s   | j }t|d S )NrU  )rV  r@  )r   r   r   r   r   rU    s   zStreamableParser.statec                 C  r  r)   )r  current_recipientrV   r   r   r   rW    r  z"StreamableParser.current_recipientc                 C  r  r)   )r  current_channelrV   r   r   r   rX    r  z StreamableParser.current_channelN)rG  r   rK   rH  r  ro   r   r   )r-   r6  r   rK  )r   rK  r   )r   rO  )r   rL   )r   r   r<  rX   )r   r@  )r!   r"   r#   rC   r  rL  rM  r=  rN  rP  rS  rT  r   r%   rV  rU  rW  rX  r   r   r   r   rF  o  s4    


rF  r   str | 'HarmonyEncodingName'c                 C  s"   t | ts	t| } t| }t|S )zBLoad an encoding by *name* (delegates to the Rust implementation).)r   r   _load_harmony_encodingr   )r   r3   r   r   r   r     s   
r   c                   @  s   e Zd ZdZdddZdS )HarmonyEncodingNameHarmonyGptOssr   r   c                 C  r   r)   )r   r<   rV   r   r   r   __str__  r   zHarmonyEncodingName.__str__Nr   )r!   r"   r#   HARMONY_GPT_OSSr]  r   r   r   r   r[    s    r[  )r6   rJ   rS   rY   r   r^   r   r   r   r   r[  r   rF  r@  r   )r%   r&   r   r'   )r-   r   r   r   )r   rY  r   r   ):rC   
__future__r   r>  r   enumr   typingr   r   r   r   r   r	   r
   r   r   r   r   r*   pydanticr   r   r|   r   r   r   r   rI  r   rZ  ModuleNotFoundErrorr   r   _HarmonyErrorr4   r5   r   r6   rJ   rR   rS   rY   r^   rd   rl   rt   r   r   r   r   r   r   r   r@  rF  r[  __all__r   r   r   r   <module>   sX    4

	F&j :
E