
    i3                       U d Z ddlmZ ddlmZ i ddddddd	dd
dddddddddddddddddddddddddddd Zd!ed"<    eh d#          Zd$ed%<    edh          Z	d$ed&<    eh d'          Z
d$ed(<    edd)h          Zd$ed*<    eh d+          Zd$ed,<    eh d-          Zd$ed.<    ed/d0h          Zd$ed1<   dBd5ZdBd6ZdBd7ZdCd9ZdDd;ZdEd=ZdBd>ZdFd@ZdAS )Ga  Per-provider model name normalization.

Different LLM providers expect model identifiers in different formats:

- **Aggregators** (OpenRouter, Nous, AI Gateway, Kilo Code) need
  ``vendor/model`` slugs like ``anthropic/claude-sonnet-4.6``.
- **Anthropic** native API expects bare names with dots replaced by
  hyphens: ``claude-sonnet-4-6``.
- **Copilot** expects bare names *with* dots preserved:
  ``claude-sonnet-4.6``.
- **OpenCode Zen** preserves dots for GPT/GLM/Gemini/Kimi/MiniMax-style
  model IDs, but Claude still uses hyphenated native names like
  ``claude-sonnet-4-6``.
- **OpenCode Go** preserves dots in model names: ``minimax-m2.7``.
- **DeepSeek** only accepts two model identifiers:
  ``deepseek-chat`` and ``deepseek-reasoner``.
- **Custom** and remaining providers pass the name through as-is.

This module centralises that translation so callers can simply write::

    api_model = normalize_model_for_provider(user_input, provider)

Inspired by Clawdbot's ``normalizeAnthropicModelId`` pattern.
    )annotations)Optionalclaude	anthropicgptopenaio1o3o4geminigooglegemmadeepseekglmzz-aikimi
moonshotaiminimaxgrokzx-aiqwenmimoxiaomitrinityzarcee-ainemotronnvidiallamaz
meta-llamastepfun)stepr   zdict[str, str]_VENDOR_PREFIXES>   
ai-gatewaynouskilocode
openrouterzfrozenset[str]_AGGREGATOR_PROVIDERS_DOT_TO_HYPHEN_PROVIDERS>   copilot-acpopenai-codexcopilot_STRIP_VENDOR_ONLY_PROVIDERShuggingface_AUTHORITATIVE_NATIVE_PROVIDERS>   
minimax-cn
qwen-oauthkimi-codingollama-cloudkimi-coding-cnzaiarceecustomr   alibabar    _MATCHING_PREFIX_STRIP_PROVIDERS>   r1cotthinkreasoner	reasoning_DEEPSEEK_REASONER_KEYWORDSdeepseek-chatdeepseek-reasoner_DEEPSEEK_CANONICAL_MODELS
model_namestrreturnc                    t          |                                           }|t          v r|S t          D ]	}||v r dS 
dS )a  Map any model input to one of DeepSeek's two accepted identifiers.

    Rules:
    - Already ``deepseek-chat`` or ``deepseek-reasoner`` -> pass through.
    - Contains any reasoner keyword (r1, think, reasoning, cot, reasoner)
      -> ``deepseek-reasoner``.
    - Everything else -> ``deepseek-chat``.

    Args:
        model_name: The bare model name (vendor prefix already stripped).

    Returns:
        One of ``"deepseek-chat"`` or ``"deepseek-reasoner"``.
    r<   r;   )_strip_vendor_prefixlowerr=   r:   )r>   barekeywords      B/home/agentuser/.hermes/hermes-agent/hermes_cli/model_normalize.py_normalize_for_deepseekrG   {   s]      
++1133D))) / ' 'd??&&&  ?    c                F    d| v r|                      dd          d         S | S )a@  Remove a ``vendor/`` prefix if present.

    Examples::

        >>> _strip_vendor_prefix("anthropic/claude-sonnet-4.6")
        'claude-sonnet-4.6'
        >>> _strip_vendor_prefix("claude-sonnet-4.6")
        'claude-sonnet-4.6'
        >>> _strip_vendor_prefix("meta-llama/llama-4-scout")
        'llama-4-scout'
    /   )splitr>   s    rF   rB   rB      s0     jQ''**rH   c                .    |                      dd          S )zReplace dots with hyphens in a model name.

    Anthropic's native API uses hyphens where marketing names use dots:
    ``claude-sonnet-4.6`` -> ``claude-sonnet-4-6``.
    .-)replacerM   s    rF   _dots_to_hyphensrR      s     c3'''rH   provider_namec                    | pd                                                                 }|s|S 	 ddlm}  ||          S # t          $ r |cY S w xY w)z2Resolve provider aliases to Hermes' canonical ids. r   )normalize_provider)striprC   hermes_cli.modelsrV   	Exception)rS   rawrV   s      rF   _normalize_provider_aliasr[      s}    B
%
%
'
'
-
-
/
/C 
888888!!#&&&   


s   ? AAtarget_providerc                   d| vr| S |                      dd          \  }}|                                r|                                s| S t          |          }t          |          }|r||k    r|                                S | S )a  Strip ``provider/`` only when the prefix matches the target provider.

    This prevents arbitrary slash-bearing model IDs from being mangled on
    native providers while still repairing manual config values like
    ``zai/glm-5.1`` for the ``zai`` provider.
    rJ   rK   )rL   rW   r[   )r>   r\   prefix	remaindernormalized_prefixnormalized_targets         rF   _strip_matching_provider_prefixrb      s     *"((a00FI<<>> !2!2 1&991/BB !.2CCC   rH   Optional[str]c                   |                                  }|sdS d|v r0|                    dd          d                                         pdS |                                }|                    d          d         }|t          v rt          |         S t                                          D ]\  }}|                    |          r|c S dS )a*  Detect the vendor slug from a bare model name.

    Uses the first hyphen-delimited token of the model name to look up
    the corresponding vendor in ``_VENDOR_PREFIXES``.  Also handles
    case-insensitive matching and special patterns.

    Args:
        model_name: A model name, optionally already including a
            ``vendor/`` prefix.  If a prefix is present it is used
            directly.

    Returns:
        The vendor slug (e.g. ``"anthropic"``, ``"openai"``) or ``None``
        if no vendor can be confidently detected.

    Examples::

        >>> detect_vendor("claude-sonnet-4.6")
        'anthropic'
        >>> detect_vendor("gpt-5.4-mini")
        'openai'
        >>> detect_vendor("anthropic/claude-sonnet-4.6")
        'anthropic'
        >>> detect_vendor("my-custom-model")
    NrJ   rK   r   rP   )rW   rL   rC   r   items
startswith)r>   name
name_lowerfirst_tokenr^   vendors         rF   detect_vendorrk      s    4 D t d{{zz#q!!!$**,,44J ""3''*K&&&,, +0022    (( 	MMM	 4rH   c                B    d| v r| S t          |           }|r| d|  S | S )aN  Prepend the detected ``vendor/`` prefix if missing.

    Used for aggregator providers that require ``vendor/model`` format.
    If the name already contains a ``/``, it is returned as-is.
    If no vendor can be detected, the name is returned unchanged
    (aggregators may still accept it or return an error).

    Examples::

        >>> _prepend_vendor("claude-sonnet-4.6")
        'anthropic/claude-sonnet-4.6'
        >>> _prepend_vendor("anthropic/claude-sonnet-4.6")
        'anthropic/claude-sonnet-4.6'
        >>> _prepend_vendor("my-custom-thing")
        'my-custom-thing'
    rJ   )rk   )r>   rj   s     rF   _prepend_vendorrm   	  sD    " j:&&F ('':'''rH   model_inputc                   | pd                                 }|s|S t          |          }|t          v rt          |          S |dk    rNt	          ||          }d|v r|S |                                                    d          rt          |          S |S |t          v r%t	          ||          }d|v r|S t          |          S |t          v rIt	          ||          }||k    r1|                    d          r|
                    dd          d         S |S |dk    r%t	          ||          }d|v r|S t          |          S |t          v rt	          ||          S |t          v r|S |S )a  Translate a model name into the format the target provider's API expects.

    This is the primary entry point for model name normalisation.  It
    accepts any user-facing model identifier and transforms it for the
    specific provider that will receive the API call.

    Args:
        model_input: The model name as provided by the user or config.
            Can be bare (``"claude-sonnet-4.6"``), vendor-prefixed
            (``"anthropic/claude-sonnet-4.6"``), or already in native
            format (``"claude-sonnet-4-6"``).
        target_provider: The canonical Hermes provider id, e.g.
            ``"openrouter"``, ``"anthropic"``, ``"copilot"``,
            ``"deepseek"``, ``"custom"``.  Should already be normalised
            via ``hermes_cli.models.normalize_provider()``.

    Returns:
        The model identifier string that the target provider's API
        expects.

    Raises:
        No exceptions -- always returns a best-effort string.

    Examples::

        >>> normalize_model_for_provider("claude-sonnet-4.6", "openrouter")
        'anthropic/claude-sonnet-4.6'

        >>> normalize_model_for_provider("anthropic/claude-sonnet-4.6", "anthropic")
        'claude-sonnet-4-6'

        >>> normalize_model_for_provider("anthropic/claude-sonnet-4.6", "copilot")
        'claude-sonnet-4.6'

        >>> normalize_model_for_provider("openai/gpt-5.4", "copilot")
        'gpt-5.4'

        >>> normalize_model_for_provider("claude-sonnet-4.6", "opencode-zen")
        'claude-sonnet-4-6'

        >>> normalize_model_for_provider("minimax-m2.5-free", "opencode-zen")
        'minimax-m2.5-free'

        >>> normalize_model_for_provider("deepseek-v3", "deepseek")
        'deepseek-chat'

        >>> normalize_model_for_provider("deepseek-r1", "deepseek")
        'deepseek-reasoner'

        >>> normalize_model_for_provider("my-model", "custom")
        'my-model'

        >>> normalize_model_for_provider("claude-sonnet-4.6", "zai")
        'claude-sonnet-4.6'
    rU   zopencode-zenrJ   zclaude-zopenai/rK   r   )rW   r[   r#   rm   rb   rC   rf   rR   r$   r(   rL   rG   r4   r*   )rn   r\   rg   providerrD   strippeds         rF   normalize_model_for_providerrr   '  s   p 2$$&&D (99H (((t$$$ >!!.tX>>$;;K::<<""9-- 	*#D))) +++.tX>>$;;K%%% ///24BBt	 : :::c1%%a(( :.tX>>$;;K&t,,, 333.tX>>> 222 KrH   N)r>   r?   r@   r?   )rS   r?   r@   r?   )r>   r?   r\   r?   r@   r?   )r>   r?   r@   rc   )rn   r?   r\   r?   r@   r?   )__doc__
__future__r   typingr   r   __annotations__	frozensetr#   r$   r(   r*   r4   r:   r=   rG   rB   rR   r[   rb   rk   rm   rr    rH   rF   <module>ry      sY    2 # " " " " "      $k$	8$ 	($ 	(	$
 	($ h$ X$ 
$ 
6$ L$ y$ F$ F$ H$ z$  !$" \#$$ '$ $ $     . )2	 3 3 3 ) )      ,596 , ,     
 09y : : : 0 0      3<)= 3 3      4=9 > > > 4 4      ( /8i 9 9 9 / /      .7Y8 . .        @   "( ( ( (
 
 
 
   */ / / /d   <j j j j j jrH   