§
    ‰eîip?  ã                  ó&  — U d Z ddlmZ ddl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dh¦  «        Zd%ed.<    eh d/£¦  «        Zd%ed0<    eh d1£¦  «        Zd%ed2<    ej        d3¦  «        ZdCd7„ZdCd8„ZdCd9„ZdDd;„ZdEd=„ZdFd?„ZdCd@„ZdGdB„ZdS )Hu  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** accepts ``deepseek-chat`` (V3), ``deepseek-reasoner``
  (R1-family), and the first-class V-series IDs (``deepseek-v4-pro``,
  ``deepseek-v4-flash``, and any future ``deepseek-v<N>-*``).  Older
  Hermes revisions folded every non-reasoner input into
  ``deepseek-chat``, which on aggregators routes to V3 â€” so a user
  picking V4 Pro was silently downgraded.
- **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.
é    )ÚannotationsN)Ú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>   úopenai-codexÚcopilotúcopilot-acpÚ_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Ú_LOWERCASE_MODEL_PROVIDERS>   Úr1ÚcotÚthinkÚreasonerÚ	reasoningÚ_DEEPSEEK_REASONER_KEYWORDS>   údeepseek-v4-proúdeepseek-v4-flashúdeepseek-chatúdeepseek-reasonerÚ_DEEPSEEK_CANONICAL_MODELSz^deepseek-v\d+([-.].+)?$Ú
model_nameÚstrÚreturnc                ó¸   — t          | ¦  «                             ¦   «         }|t          v r|S t                               |¦  «        r|S t
          D ]	}||v r dS Œ
dS )a�  Map a model input to a DeepSeek-accepted identifier.

    Rules:
    - Already a known canonical (``deepseek-chat``/``deepseek-reasoner``/
      ``deepseek-v4-pro``/``deepseek-v4-flash``) -> pass through.
    - Matches the V-series pattern ``deepseek-v<digit>...`` -> pass through
      (covers future ``deepseek-v5-*`` and dated variants without a release).
    - Contains a 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:
        A DeepSeek-accepted model identifier.
    r?   r>   )Ú_strip_vendor_prefixÚlowerr@   Ú_DEEPSEEK_V_SERIES_REÚmatchr;   )rA   ÚbareÚkeywords      ú</home/div/.hermes/hermes-agent/hermes_cli/model_normalize.pyÚ_normalize_for_deepseekrL   “   sz   € õ$   
Ñ+Ô+×1Ò1Ñ3Ô3€DàÕ)Ð)Ð)Øˆõ ×"Ò" 4Ñ(Ô(ð Øˆõ /ð 'ð 'ˆØ�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©rA   s    rK   rE   rE   º   s0   € ð ˆjÐÐØ×Ò  QÑ'Ô'¨Ô*Ð*ØÐrM   c                ó.   — |                       dd¦  «        S )z¬Replace 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``.
    ú.Ú-)ÚreplacerR   s    rK   Ú_dots_to_hyphensrW   Ë   s   € ð ×Ò˜c 3Ñ'Ô'Ð'rM   Ú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)ÚstriprF   Úhermes_cli.modelsr[   Ú	Exception)rX   Úrawr[   s      rK   Ú_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.
    rO   rP   )rQ   r\   r`   )rA   ra   ÚprefixÚ	remainderÚnormalized_prefixÚnormalized_targets         rK   Ú_strip_matching_provider_prefixrg   á   sœ   € ð �*ÐÐØÐà"×(Ò(¨¨aÑ0Ô0Ñ€FˆIØ�<Š<‰>Œ>ð  §¢Ñ!2Ô!2ð ØÐå1°&Ñ9Ô9ÐÝ1°/ÑBÔBÐØð !Ð.Ð2CÒCÐCØ�ŠÑ Ô Ð ØÐrM   ú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")
    NrO   rP   r   rU   )r\   rQ   rF   r   ÚitemsÚ
startswith)rA   ÚnameÚ
name_lowerÚfirst_tokenrc   Úvendors         rK   Údetect_vendorrp   ö   sá   € ð4 ×ÒÑÔ€DØð Øˆtð ˆd€{€{Ø�zŠz˜#˜qÑ!Ô! !Ô$×*Ò*Ñ,Ô,Ð4°Ð4à—’‘”€Jð ×"Ò" 3Ñ'Ô'¨Ô*€KØÕ&Ð&Ð&Ý Ô,Ð,õ +×0Ò0Ñ2Ô2ð ð ‰ˆ�Ø× Ò  Ñ(Ô(ð 	ØˆMˆMˆMð	ð ˆ4rM   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'
    rO   )rp   )rA   ro   s     rK   Ú_prepend_vendorrr   (  sD   € ð" ˆjÐÐØÐå˜:Ñ&Ô&€FØð (ØÐ'Ð'˜:Ð'Ð'Ð'ØÐrM   Úmodel_inputc                ób  — | 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 |dv r'	 ddl	m
}  ||¦  «        }|r|S n# t          $ r Y nw xY w|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	          ||¦  «        }|t           v r|                     ¦   «         }|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'

        >>> normalize_model_for_provider("MiMo-V2.5-Pro", "xiaomi")
        'mimo-v2.5-pro'
    rZ   zopencode-zenrO   zclaude->   r&   r'   r   )Únormalize_copilot_model_idzopenai/rP   r   )r\   r`   r#   rr   rg   rF   rk   rW   r$   r]   ru   r^   r(   rQ   rL   r4   r5   r*   )	rs   ra   rl   ÚproviderrI   ru   Ú
normalizedÚstrippedÚresults	            rK   Únormalize_model_for_providerrz   F  s5  € ðv Ð˜2×$Ò$Ñ&Ô&€DØð Øˆå(¨Ñ9Ô9€Hð Õ(Ð(Ð(Ý˜tÑ$Ô$Ð$ð �>Ò!Ð!Ý.¨t°XÑ>Ô>ˆØ�$ˆ;ˆ;ØˆKØ�:Š:‰<Œ<×"Ò" 9Ñ-Ô-ð 	*Ý# DÑ)Ô)Ð)Øˆð Õ+Ð+Ð+Ý.¨t°XÑ>Ô>ˆØ�$ˆ;ˆ;ØˆKÝ Ñ%Ô%Ð%ð Ð-Ð-Ð-ð		ØDÐDÐDÐDÐDÐDà3Ð3°DÑ9Ô9ˆJØð "Ø!Ð!ð"øåð 	ð 	ð 	ð ˆDð	øøøð Õ/Ð/Ð/Ý2°4¸ÑBÔBˆØ�tÒÐ §¢°	Ñ :Ô :Ðà—:’:˜c 1Ñ%Ô% aÔ(Ð(Øˆð �:ÒÐÝ.¨t°XÑ>Ô>ˆØ�$ˆ;ˆ;ØˆKÝ& tÑ,Ô,Ð,ð Õ3Ð3Ð3Ý0°°xÑ@Ô@ˆð Õ1Ð1Ð1Ø—\’\‘^”^ˆFØˆð Õ2Ð2Ð2Øˆð €Ks   Ã	C Ã
C,Ã+C,)rA   rB   rC   rB   )rX   rB   rC   rB   )rA   rB   ra   rB   rC   rB   )rA   rB   rC   rh   )rs   rB   ra   rB   rC   rB   )Ú__doc__Ú
__future__r   ÚreÚtypingr   r   Ú__annotations__Ú	frozensetr#   r$   r(   r*   r4   r5   r;   r@   ÚcompilerG   rL   rE   rW   r`   rg   rp   rr   rz   © rM   rK   ú<module>rƒ      s«  ððð ð ð: #Ð "Ð "Ð "Ð "Ð "à 	€	€	€	Ø Ð Ð Ð Ð Ð ð$Øˆkð$à	ˆ8ð$ð 	ˆ(ð$ð 	ˆ(ð	$ð
 	ˆ(ð$ð ˆhð$ð ˆXð$ð �
ð$ð 
ˆ6ð$ð ˆLð$ð ˆyð$ð ˆFð$ð ˆFð$ð ˆHð$ð ˆzð$ð  �ð!$ð" ˆ\ð#$ð$ Øð'$ð $ð $Ð ð ð ð ñ ð. )2¨	ð 3ð 3ð 3ñ )ô )Ð ð ð ð ñ ð ,5¨9Øð6ñ ,ô ,Ð ð ð ð ñ ð
 09¨yð :ð :ð :ñ 0ô 0Ð ð ð ð ñ ð 3<°)ØØð=ñ 3ô 3Ð ð ð ð ñ ð 4=°9ð >ð >ð >ñ 4ô 4Ð  ð ð ð ñ ð& .7¨YØð8ñ .ô .Ð ð ð ð ñ ð /8¨ið 9ð 9ð 9ñ /ô /Ð ð ð ð ñ ð .7¨Yð 8ð 8ð 8ñ .ô .Ð ð ð ð ñ ð #˜œ
Ð#>Ñ?Ô?Ð ð ð  ð  ð  ðNð ð ð ð"(ð (ð (ð (ð
ð 
ð 
ð 
ðð ð ð ð*/ð /ð /ð /ðdð ð ð ð<Eð Eð Eð Eð Eð ErM   