§
    ii»jh  ã            
       ó|  — d Z ddlZddlmc mZ ddlZddlZddl	Z	ddl
Z
e	j                             de
j                             e
j                             e
j                             e¦  «        d¦  «        ¦  «        ¦  «         ddlmZmZmZmZmZ  G d„ dej        ¦  «        Zedk    r ej        ¦   «          dS dS )zì
Unit tests for Multi-LLM Provider Client Engine in FreeExile.
Verifies integration and communication contracts with OpenRouter, OpenAI (GPT),
Anthropic (Claude), Google (Gemini), DeepSeek, Moonshot (Kimi), and Local Tactical Fallback.
é    Nz../../server)ÚLLMProviderTypeÚLLMProviderConfigÚAgentActionTypeÚTacticalActionDecisionÚLLMProviderClientRouterc                   ó2   — e Zd Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ ZdS )ÚTestLLMProviderClientc                 ó,   — t          ¦   «         | _        d S )N)r   Úrouter)Úselfs    ú<C:\Projects\FreeExile\tests\unit\test_llm_provider_client.pyÚsetUpzTestLLMProviderClient.setUp   s   € Ý-Ñ/Ô/ˆŒˆˆó    c                 óþ   — t           j        t           j        t           j        t           j        t           j        t           j        t           j        h}|D ]*}|                      |t          t           ¦  «        ¦  «         Œ+dS )zUVerifies that all 6 requested cloud LLM providers plus local tactical are recognized.N)
r   Ú
OPENROUTERÚOPENAIÚ	ANTHROPICÚGEMINIÚDEEPSEEKÚKIMIÚLOCAL_TACTICALÚassertInÚlist)r   Úexpected_providersÚprovs      r   Útest_provider_enum_definitionsz4TestLLMProviderClient.test_provider_enum_definitions   sp   € õ Ô&ÝÔ"ÝÔ%ÝÔ"ÝÔ$ÝÔ ÝÔ*ð
Ðð 'ð 	7ð 	7ˆDØ�MŠM˜$¥¥_Ñ 5Ô 5Ñ6Ô6Ð6Ð6ð	7ð 	7r   c                 óX  — t          t          j        d¬¦  «        }|                      |j        d¦  «         |                      d|j        ¦  «         t          t          j        d¬¦  «        }|                      |j        d¦  «         |                      d|j        ¦  «         t          t          j        d¬¦  «        }|                      |j        d	¦  «         |                      d
|j        ¦  «         t          t          j	        d¬¦  «        }|                      d|j        ¦  «         dS )zDVerifies default base URLs and recommended models for each provider.z
sk-or-test©ÚproviderÚapi_keyz!meta-llama/llama-3.3-70b-instructzopenrouter.aiz
sk-ds-testzdeepseek-chatzdeepseek.comz
sk-km-testzmoonshot-v1-8kzmoonshot.cnz
sk-gm-testÚgeminiN)
r   r   r   ÚassertEqualÚmodelr   Úbase_urlr   r   r   )r   Úconfig_openrouterÚconfig_deepseekÚconfig_kimiÚconfig_geminis        r   Ú'test_default_endpoint_and_model_mappingz=TestLLMProviderClient.test_default_endpoint_and_model_mapping)   s  € å-µÔ7QÐ[gÐhÑhÔhÐØ×ÒÐ*Ô0Ð2UÑVÔVÐVØ�Š�oÐ'8Ô'AÑBÔBÐBå+µ_Ô5MÐWcÐdÑdÔdˆØ×Ò˜Ô.°Ñ@Ô@Ð@Ø�Š�n oÔ&>Ñ?Ô?Ð?å'µÔ1EÈ|Ð\Ñ\Ô\ˆØ×Ò˜Ô*Ð,<Ñ=Ô=Ð=Ø�Š�m [Ô%9Ñ:Ô:Ð:å)µ?Ô3IÐS_Ð`Ñ`Ô`ˆØ�Š�h Ô 3Ñ4Ô4Ð4Ð4Ð4r   c                 ó¸  — t          t          j        ¬¦  «        }dddddœgddd	d
œgdœ}t          j        | j                             ||¦  «        ¦  «        }|                      |t          ¦  «         |  	                    |j
        t          j        t          j        t          j        g¦  «         |                      t!          |j        ¦  «        dk    ¦  «         dS )zdWhen in LOCAL_TACTICAL mode or offline, decision must return deterministically without network call.)r   gš™™™™™É?ée   g       @ÚTHO)ÚidÚdistanceÚelementÚdrop_1Úcurr_chaos_stoneg      ø?)r-   Úitemr.   )Úplayer_hp_pctÚnearby_enemiesÚnearby_dropsr   N)r   r   r   ÚasyncioÚrunr   Úrequest_tactical_decisionÚassertIsInstancer   r   Úaction_typer   ÚDODGEÚ
USE_POTIONÚ	MOVE_AWAYÚ
assertTrueÚlenÚ	reasoning)r   ÚconfigÚworld_promptÚdecisions       r   Ú&test_local_tactical_heuristic_fallbackz<TestLLMProviderClient.test_local_tactical_heuristic_fallback:   sÐ   € å"­OÔ,JÐKÑKÔKˆà!Ø&)°sÀuÐMÐMÐNØ$,Ð6HÐVYÐZÐZÐ[ð
ð 
ˆõ ”;˜tœ{×DÒDÀVÈ\ÑZÔZÑ[Ô[ˆØ×Ò˜hÕ(>Ñ?Ô?Ð?à�Š�hÔ*­_Ô-BÅOÔD^Õ`oÔ`yÐ,zÑ{Ô{Ð{Ø�Š�˜HÔ.Ñ/Ô/°!Ò3Ñ4Ô4Ð4Ð4Ð4r   c                 ó*  — d}| j                              |¦  «        }|                      |j        t          j        ¦  «         |                      |j        d¦  «         |                      |j        d¦  «         |                      d|j	        ¦  «         dS )z\Verifies that structured JSON responses from LLMs parse cleanly into TacticalActionDecision.a1  
        {
            "action_type": "ATTACK_SKILL",
            "target_id": 99,
            "skill_id": 102,
            "target_x": 12.5,
            "target_y": -4.2,
            "reasoning": "Target is Earth element. Switching to Wood sword skill for elemental counter advantage."
        }
        éc   éf   zEarth elementN)
r   Úparse_response_to_decisionr"   r:   r   ÚATTACK_SKILLÚ	target_idÚskill_idr   r@   )r   Úraw_llm_jsonrC   s      r   Ú#test_structured_json_action_parsingz9TestLLMProviderClient.test_structured_json_action_parsingI   s‹   € ð	ˆð ”;×9Ò9¸,ÑGÔGˆØ×Ò˜Ô-­Ô/KÑLÔLÐLØ×Ò˜Ô+¨RÑ0Ô0Ð0Ø×Ò˜Ô*¨CÑ0Ô0Ð0Ø�Š�o xÔ'9Ñ:Ô:Ð:Ð:Ð:r   c                 óÖ  — t          t          j        d¬¦  «        }| j                             |¦  «        }|                      |                     d¦  «        d¦  «         |                      d|¦  «         t          t          j        d¬¦  «        }| j                             |¦  «        }|                      |                     d¦  «        d¦  «         |                      d|¦  «         d	S )
zVVerifies correct Authorization headers for Anthropic, OpenRouter, and standard Bearer.zsk-ant-12345r   z	x-api-keyzanthropic-versionzsk-or-12345ÚAuthorizationzBearer sk-or-12345zHTTP-RefererN)	r   r   r   r   Úbuild_request_headersr"   Úgetr   r   )r   Úconf_anthropicÚheaders_antÚconf_openrouterÚ
headers_ors        r   Ú test_provider_headers_generationz6TestLLMProviderClient.test_provider_headers_generation[   sÌ   € å*µOÔ4MÐWeÐfÑfÔfˆØ”k×7Ò7¸ÑGÔGˆØ×Ò˜Ÿš¨Ñ5Ô5°~ÑFÔFÐFØ�ŠÐ)¨;Ñ7Ô7Ð7å+µ_Ô5OÐYfÐgÑgÔgˆØ”[×6Ò6°ÑGÔGˆ
Ø×Ò˜Ÿš¨Ñ8Ô8Ð:NÑOÔOÐOØ�Š�n jÑ1Ô1Ð1Ð1Ð1r   N)	Ú__name__Ú
__module__Ú__qualname__r   r   r)   rD   rM   rV   © r   r   r	   r	      sn   € € € € € ð0ð 0ð 0ð7ð 7ð 7ð5ð 5ð 5ð"5ð 5ð 5ð;ð ;ð ;ð$
2ð 
2ð 
2ð 
2ð 
2r   r	   Ú__main__)Ú__doc__ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arÚunittestr6   ÚsysÚosÚpathÚinsertÚabspathÚjoinÚdirnameÚ__file__Úagent.llm_provider_clientr   r   r   r   r   ÚTestCaser	   rW   ÚmainrZ   r   r   ú<module>ro      s3  ððð ð € € € € € € € € € € € € €€€€Ø €€€Ø 
€
€
€
Ø 	€	€	€	à „‡‚��2”7—?’? 2¤7§<¢<°´·²ÀÑ0IÔ0IÈ>Ñ#ZÔ#ZÑ[Ô[Ñ \Ô \Ð \ðð ð ð ð ð ð ð ð ð ð ð ð ð ðN2ð N2ð N2ð N2ð N2˜HÔ-ñ N2ô N2ð N2ðb ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr   