2026-03-30 16:51:25 -03:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
from unittest import TestCase, mock
|
|
|
|
|
|
|
|
|
|
from atlas_portal.app_factory import create_app
|
|
|
|
|
from atlas_portal.routes import ai
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AiRouteTests(TestCase):
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
cls.app = create_app()
|
|
|
|
|
cls.client = cls.app.test_client()
|
|
|
|
|
|
|
|
|
|
def test_chat_routes_profiles_to_modes(self):
|
|
|
|
|
seen: list[tuple[str, str]] = []
|
|
|
|
|
|
|
|
|
|
def fake_atlasbot_answer(message: str, mode: str, conversation_id: str) -> str:
|
|
|
|
|
seen.append((mode, conversation_id))
|
|
|
|
|
return f"{mode}:{conversation_id}"
|
|
|
|
|
|
|
|
|
|
with mock.patch.object(ai, "_atlasbot_answer", side_effect=fake_atlasbot_answer):
|
|
|
|
|
for profile, expected_mode in (
|
|
|
|
|
("atlas-quick", "quick"),
|
|
|
|
|
("atlas-smart", "smart"),
|
|
|
|
|
("atlas-genius", "genius"),
|
|
|
|
|
):
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
"/api/chat",
|
|
|
|
|
data=json.dumps(
|
|
|
|
|
{
|
|
|
|
|
"message": "How is Titan doing?",
|
|
|
|
|
"profile": profile,
|
|
|
|
|
"conversation_id": f"conv-{profile}",
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
content_type="application/json",
|
|
|
|
|
)
|
|
|
|
|
data = resp.get_json()
|
|
|
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
|
self.assertEqual(data.get("source"), f"atlas-{expected_mode}")
|
|
|
|
|
self.assertEqual(data.get("reply"), f"{expected_mode}:conv-{profile}")
|
|
|
|
|
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
seen,
|
|
|
|
|
[
|
|
|
|
|
("quick", "conv-atlas-quick"),
|
|
|
|
|
("smart", "conv-atlas-smart"),
|
|
|
|
|
("genius", "conv-atlas-genius"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_info_endpoint_exposes_profile_specific_model(self):
|
|
|
|
|
with mock.patch.object(ai.settings, "AI_ATLASBOT_MODEL_GENIUS", "genius-model"):
|
|
|
|
|
resp = self.client.get("/api/ai/info?profile=atlas-genius")
|
|
|
|
|
|
|
|
|
|
data = resp.get_json()
|
|
|
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
|
self.assertEqual(data.get("profile"), "atlas-genius")
|
|
|
|
|
self.assertEqual(data.get("model"), "genius-model")
|
2026-03-30 16:55:22 -03:00
|
|
|
|
|
|
|
|
def test_atlasbot_answer_uses_profile_specific_timeout(self):
|
|
|
|
|
captured: dict[str, object] = {}
|
|
|
|
|
|
|
|
|
|
class DummyResponse:
|
|
|
|
|
status_code = 200
|
|
|
|
|
|
|
|
|
|
def json(self):
|
|
|
|
|
return {"reply": "atlas reply"}
|
|
|
|
|
|
|
|
|
|
class DummyClient:
|
|
|
|
|
def __init__(self, timeout):
|
|
|
|
|
captured["timeout"] = timeout
|
|
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def __exit__(self, exc_type, exc, tb):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def post(self, endpoint, json=None, headers=None):
|
|
|
|
|
captured["endpoint"] = endpoint
|
|
|
|
|
captured["json"] = json
|
|
|
|
|
captured["headers"] = headers or {}
|
|
|
|
|
return DummyResponse()
|
|
|
|
|
|
|
|
|
|
with (
|
|
|
|
|
mock.patch.object(ai.httpx, "Client", DummyClient),
|
|
|
|
|
mock.patch.object(ai.settings, "AI_ATLASBOT_ENDPOINT", "http://atlasbot.invalid/v1/answer"),
|
|
|
|
|
):
|
|
|
|
|
reply = ai._atlasbot_answer("How is Titan doing?", "genius", "conv-1")
|
|
|
|
|
|
|
|
|
|
self.assertEqual(reply, "atlas reply")
|
|
|
|
|
self.assertEqual(captured["timeout"], ai.settings.AI_ATLASBOT_TIMEOUT_GENIUS_SEC)
|
|
|
|
|
self.assertEqual(captured["json"], {"prompt": "How is Titan doing?", "mode": "genius", "conversation_id": "conv-1"})
|
|
|
|
|
|
|
|
|
|
def test_chat_returns_fallback_when_atlasbot_returns_empty(self):
|
|
|
|
|
with mock.patch.object(ai, "_atlasbot_answer", return_value=""):
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
"/api/chat",
|
|
|
|
|
data=json.dumps(
|
|
|
|
|
{
|
|
|
|
|
"message": "How is Titan doing?",
|
|
|
|
|
"profile": "atlas-quick",
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
content_type="application/json",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
data = resp.get_json()
|
|
|
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
|
self.assertEqual(data.get("source"), "atlas-quick")
|
|
|
|
|
self.assertIn("Quick mode hit", data.get("reply", ""))
|