
    `gj2                        d Z ddlmZ ddlZddlmZmZmZmZ ddl	m
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dZddZddZddZddZddZy)ug  
hermes fallback — manage the fallback provider chain.

Fallback providers are tried in order when the primary model fails with
rate-limit, overload, or connection errors. See:
https://hermes-agent.nousresearch.com/docs/user-guide/features/fallback-providers

Subcommands:
  hermes fallback [list]   Show the current fallback chain (default when no subcommand)
  hermes fallback add      Pick provider + model via the same picker as `hermes model`,
                           then append the selection to the chain
  hermes fallback remove   Pick an entry to delete from the chain
  hermes fallback clear    Remove all fallback entries

Storage: ``fallback_providers`` in ``~/.hermes/config.yaml`` (top-level, list of
``{provider, model, base_url?, api_mode?}`` dicts).  The legacy single-dict
``fallback_model`` format is migrated to the new list format on first add.
    )annotationsN)AnyDictListOptionalget_fallback_chainc                    t        |       S )uy  Return the normalized fallback chain as a list of dicts.

    Accepts both the new list format (``fallback_providers``) and the legacy
    ``fallback_model`` format. When both are present, the effective chain is
    merged with ``fallback_providers`` entries kept first. The returned list is
    always a fresh copy — callers can mutate without touching the config dict.
    r   )configs    J/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/fallback_cmd.py_read_chainr      s     f%%    c                <    || d<   d| v r| j                  dd       yy)zAPersist the chain to ``fallback_providers`` and clear legacy key.fallback_providersfallback_modelN)pop)r   chains     r   _write_chainr   *   s)    #(F 6!

#T* "r   c                    | j                  dd      }| j                  dd      }| j                  d      }|rd| dnd}| d| d	| S )
z6One-line human-readable rendering of a fallback entry.provider?modelbase_urlz  []   (via ))get)entryr   r   basesuffixs        r   _format_entryr"   2   sX    yyS)HIIgs#E99Z D"s4&]FWGH:Qvh//r   c                   t        | t              sy| j                  d      xs dj                         }| j                  d      xs | j                  d      xs dj                         }|r|sy||d}| j                  d      xs dj                         }|r||d<   | j                  d      xs dj                         }|r||d<   |S )	z^Pull the ``{provider, model, base_url?, api_mode?}`` dict from a ``config["model"]`` snapshot.Nr   r   defaultr   )r   r   r   api_mode)
isinstancedictr   strip)	model_cfgr   r   r   r   r%   s         r    _extract_fallback_from_model_cfgr*   ;   s    i&j)/R668H]]9%Ew)?E2LLNE5)1EBEj)/R668H$jj)/R668H$jLr   c                 ^    	 ddl m}   |        }|j                  d      S # t        $ r Y yw xY w)zRReturn the current ``active_provider`` in auth.json, or a sentinel if unavailable.r   )_load_auth_storeactive_providerN)hermes_cli.authr,   r   	Exception)r,   stores     r   _snapshot_auth_active_providerr1   N   s4    4 "yy*++ s     	,,c                    	 ddl m}m}m}  |       5   |       }| |d<    ||       ddd       y# 1 sw Y   yxY w# t        $ r Y yw xY w)z>Write back a previously snapshotted ``active_provider`` value.r   )_auth_store_lockr,   _save_auth_storer-   N)r.   r3   r,   r4   r/   )valuer3   r,   r4   r0   s        r   _restore_auth_active_providerr6   X   sX    
XX 	$$&E',E#$U#	$ 	$ 	$   		s#   = 1= := = 	A	A	c           	        ddl m}  |       }t        |      }t                |s+t        d       t                t        d       t                yt	        |      }|rt        d|        t                t        dt        |       dt        |      d	k(  rd
nd d       t        |d	      D ]  \  }}t        d| dt        |              ! t                t        d       t        d       t                y)z!Print the current fallback chain.r   )load_configz#  No fallback providers configured.z$  Add one with:  hermes fallback addNz  Primary:   z  Fallback chain (    r   entries):    . zM  Tried in order when the primary fails (rate-limit, 5xx, connection errors).zY  Docs: https://hermes-agent.nousresearch.com/docs/user-guide/features/fallback-providers)hermes_cli.configr8   r   print_describe_primarylen	enumerater"   )argsr8   r   r   primaryir   s          r   cmd_fallback_listrG   k   s    -]FE	G3445'GgY'(	s5zl!s5zQGI+VVX
YZeQ' 25QCr-./012	G	
YZ	
ef	Gr   c                x   | j                  d      }t        |t              ri|j                  d      xs dj                         xs d}|j                  d      xs |j                  d      xs dj                         xs d}| d| dS t        |t              r |j                         r|j                         S y)z?One-line description of the primary model for display purposes.r   r   r   r$   r   r   N)r   r&   r'   r(   str)r   r)   r   r   s       r   rA   rA      s    

7#I)T"MM*-4;;=Dy)JY]]7-CJsQQSZWZz++)S!ioo&7  r   c                   ddl m}m} ddlm}m}  |d        |       }t        j                  |j                  d            }t               }t                t        d       t        d       t                	  ||         |       }|j                  d      }	t        |	      }
|
s,t        |       t        |       t                t        d	       y
t        |      }|rZ|d   |
d   k(  rO|d   |
d   k(  rDt        |       t        |       t                t        dt        |
       d       t        d       y
t        |       t        |        |       }t!        |      }|D ]U  }|j                  d      |
d   k(  s|j                  d      |
d   k(  s3t                t        dt        |
       d        y
 |j#                  |
       t%        ||        ||       t                t        dt        |
              t        dt'        |       dt'        |      dk(  rdnd d       t                t        d       y
# t        $ r t        |       t        |        w xY w)zQLaunch the same picker as `hermes model`, then append the selection to the chain.r   )_require_ttyselect_provider_and_modelr8   save_configzfallback addr   zG  Adding a fallback provider.  The picker below is the same one used byuH     `hermes model` — select the provider + model you want as a fallback.)rD   z  No fallback added.Nr   z.  Selected model matches the current primary (z).u;     A provider cannot be a fallback for itself — no change.  u.    is already in the fallback chain — skipped.z  Added fallback:   Chain is now r9   r:   r   r;    long.zL  Run `hermes fallback list` to view, or `hermes fallback remove` to delete.)hermes_cli.mainrK   rL   r?   r8   rN   copydeepcopyr   r1   r@   
SystemExit_restore_model_cfgr6   r*   r"   r   appendr   rB   )rD   rK   rL   r8   rN   
before_cfgmodel_beforeactive_provider_before	after_cfgmodel_after	new_entryprimary_entry	final_cfgr   existings                  r   cmd_fallback_addra      s.   G:  J==!89L;=	G	
ST	
TU	G!t, I--(K0=I<(%&<=$% 5\BMz2i
6KKg&)G*<<<(%&<=>}Y?W>XXZ[\KL |$!"89I	"E  <<
#y'<<LL)Yw-??GB}Y/00^_` 
LLE"		G	}Y78
9:	OCJ<qCJ!O(SSY
Z[	G	
XYs  <(%&<=	s   9	I !I$c                    ddl m}m}  |       }| |j                  dd       nt	        j
                  |       |d<    ||       y)z>Restore ``config["model"]`` to a previously-captured snapshot.r   rM   Nr   )r?   r8   rN   r   rS   rT   )rY   r8   rN   cfgs       r   rV   rV      s:    :
-C}}\2Gr   c                   ddl m}m}  |       }t        |      }|s t	                t	        d       t	                y|D cg c]  }t        |       }}|j                  d       	 ddlm}  |d|d      }||dk  s|t        |      k\  rt	                t	        d       y|j                  |      }	t        ||        ||       t	                t	        d	t        |	              |r6t	        d
t        |       dt        |      dk(  rdnd d       t	                yt	        d       t	                yc c}w # t        $ r t        d|      }Y w xY w)z+Pick an entry from the chain and remove it.r   rM   u9     No fallback providers configured — nothing to remove.NCancel)_curses_prompt_choicezSelect a fallback to remove:     Cancelled — no change.z  Removed fallback: rP   r9   r:   r   r;   rQ   z  Fallback chain is now empty.)r?   r8   rN   r   r@   r"   rW   hermes_cli.setuprf   r/   _numbered_pickrB   r   r   )
rD   r8   rN   r   r   echoicesrf   idxremoveds
             r   cmd_fallback_removern      s6   :]FEIJ)./A}Q/G/NN8F:#$BGQO {cAgE
!2*+iinG	G	 w!7 8
9:E
|1E
aWY,WW]^_ 
G 	./	G1 0  F;WEFs   D6'D; ;EEc           	        ddl m}m}  |       }t        |      }|s t	                t	        d       t	                yt	                t	        dt        |       dt        |      dk(  rdnd	 d
       t        |d      D ]  \  }}t	        d| dt        |              ! t	                	 t        d      j                         j                         }|dvrt	        d       yt        |g         ||       t	                t	        d       t	                y# t        t        f$ r t	                t	        d       Y yw xY w)z0Remove all fallback entries (with confirmation).r   rM   u8     No fallback providers configured — nothing to clear.Nz  Current fallback chain (r9   r:   r   r;   r<   r=   r>   z  Clear all entries? [y/N]: z  Cancelled.>   yyesrg   z  Fallback chain cleared.)r?   r8   rN   r   r@   rB   rC   r"   inputr(   lowerKeyboardInterruptEOFErrorr   )rD   r8   rN   r   r   rF   r   resps           r   cmd_fallback_clearrw     s   :]FEHI	G	&s5zl!s5zQGT]3^^`
abeQ' 25QCr-./012	G34::<BBD
 <*+	G	
%&	G x( ns   +'D $D=<D=c                   t        |        t        |d      D ]  \  }}t        d| d|         t                	 	 t        dt        |       d      j	                         }|syt        |      dz
  }d|cxk  rt        |      k  r|S  t        dt        |              g# t        $ r t        d	       Y t        t        f$ r t                Y yw xY w)
z9Fallback numbered-list picker when curses is unavailable.r:   rO   r>   z
Choice [1-z]: Nr   zPlease enter 1-zPlease enter a number)	r@   rC   rr   rB   r(   int
ValueErrorrt   ru   )questionrk   rF   cvalrl   s         r   ri   ri   :  s    	(O'1% 11#Rsm	G
	*S\N#67==?Cc(Q,CC&#g,&
 'OCL>23   	+)*!8, 	G	s#   (B# &#B# 
B# #C9CCc                    t        | dd      }|dv rt        |        y|dk(  rt        |        y|dv rt        |        y|dk(  rt	        |        yt        d|        t        d       t        d	      )
z:Top-level dispatcher for ``hermes fallback [subcommand]``.fallback_commandN>   Nr   lslistadd>   rmremoveclearzUnknown fallback subcommand: z$Use one of: list, add, remove, clear   )getattrrG   ra   rn   rw   r@   rU   )rD   subs     r   cmd_fallbackr   T  st    
$*D
1C
&&$		 	 D!	4 -cU3445mr   )r   Dict[str, Any]returnList[Dict[str, Any]])r   r   r   r   r   None)r   r   r   rI   )r)   r   r   zOptional[Dict[str, Any]])r   r   )r5   r   r   r   )r   r   )r   r   r   zOptional[str])rY   r   r   r   )r{   rI   rk   z	List[str]r   zOptional[int])__doc__
__future__r   rS   typingr   r   r   r   hermes_cli.fallback_configr	   r   r   r"   r*   r1   r6   rG   rA   ra   rV   rn   rw   ri   r    r   r   <module>r      sf   $ #  , , 9&+0&&8	MZ`	%P F4r   