"""Smoke tests: setup_llm wires temperature correctly per provider.

Constructs the real chat-model objects (no network — API keys are fake, and
ChatOpenAI/ChatAnthropic don't call out at construction time) and inspects the
resolved ``temperature`` attribute. This locks two decisions:

  * Kimi (Moonshot) omits temperature entirely (``None``) — its k3/k2.6
    reasoning models reject any value but 1, and there is no per-model signal,
    so we let each model use its own default. Regression guard for the reported
    "invalid temperature: only 1 is allowed" bug.
  * Every other OpenAI-compatible provider still pins ``temperature=0`` for
    reproducibility. Models that reject 0 are handled at call time by the
    ``heal_llm_param_error`` self-heal, not by weakening the default here.

Run (inside agent container):
    docker run --rm -v "/path/agentic:/app" \\
        -v "/path/graph_db:/app/graph_db:ro" \\
        -v "/path/knowledge_base:/app/knowledge_base:ro" \\
        -w /app redamon-agent python -m unittest \\
        tests.test_llm_setup_temperature_smoke -v
"""

from __future__ import annotations

import os
import sys
import unittest

_agentic_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, _agentic_dir)

from orchestrator_helpers.llm_setup import setup_llm  # noqa: E402


# (model_name, setup kwarg carrying the api key)
_PINNED_ZERO_PROVIDERS = [
    ("gpt-4o", "openai_api_key"),
    ("deepseek/deepseek-chat", "deepseek_api_key"),
    ("glm/glm-4.5", "glm_api_key"),
    ("qwen/qwen-max", "qwen_api_key"),
    ("xai/grok-4", "xai_api_key"),
    ("mistral/mistral-large-latest", "mistral_api_key"),
    ("openrouter/anthropic/claude-3.5-sonnet", "openrouter_api_key"),
]


class SetupLlmTemperatureSmokeTests(unittest.TestCase):

    def test_kimi_omits_temperature(self):
        llm = setup_llm("kimi/kimi-k3", kimi_api_key="fake-key-abc")
        self.assertIsNone(
            llm.temperature,
            "Kimi must NOT pin temperature=0 — k3/k2.6 reject it; the model's "
            "own default (1) must be used instead.",
        )
        # Sanity: it still routed to the Moonshot endpoint.
        self.assertIn("moonshot.ai", str(llm.openai_api_base or llm.root_client.base_url))

    def test_kimi_v1_model_also_omits_temperature(self):
        # Provider-wide omit — not model-specific — so classic moonshot-v1 too.
        llm = setup_llm("kimi/moonshot-v1-8k", kimi_api_key="fake-key-abc")
        self.assertIsNone(llm.temperature)

    def test_other_openai_compatible_providers_pin_zero(self):
        for model_name, key_kwarg in _PINNED_ZERO_PROVIDERS:
            with self.subTest(model=model_name):
                llm = setup_llm(model_name, **{key_kwarg: "fake-key-abc"})
                self.assertEqual(
                    llm.temperature, 0,
                    f"{model_name}: reproducibility default temperature=0 must hold",
                )

    def test_openai_reasoning_model_still_constructs_with_zero(self):
        # We do NOT special-case o-series at construction (no per-model
        # allowlist) — the self-heal drops temperature at call time. Construction
        # must therefore still succeed and carry temperature=0.
        llm = setup_llm("o3-mini", openai_api_key="fake-key-abc")
        self.assertEqual(llm.temperature, 0)


if __name__ == "__main__":
    unittest.main()
