mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-28 16:07:46 +00:00
test: add tests for RDAgentLog debug() and LiteLLMAPIBackend
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,72 @@
|
||||
"""Tests for rdagent/log/logger.py — RDAgentLog wrapper around loguru."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from rdagent.log.logger import RDAgentLog
|
||||
|
||||
|
||||
class TestRDAgentLog:
|
||||
def test_singleton(self):
|
||||
a = RDAgentLog()
|
||||
b = RDAgentLog()
|
||||
assert a is b
|
||||
|
||||
def test_has_debug_method(self):
|
||||
logger = RDAgentLog()
|
||||
assert hasattr(logger, "debug")
|
||||
assert callable(logger.debug)
|
||||
|
||||
def test_debug_accepts_args(self):
|
||||
logger = RDAgentLog()
|
||||
logger.debug("test message")
|
||||
logger.debug("test message", tag="mytag")
|
||||
logger.debug("test message", raw=True)
|
||||
logger.debug("test message", tag="x", raw=False)
|
||||
|
||||
def test_info_warning_error_exist(self):
|
||||
logger = RDAgentLog()
|
||||
for method in ("info", "warning", "error", "debug"):
|
||||
assert hasattr(logger, method), f"missing {method}"
|
||||
assert callable(getattr(logger, method)), f"{method} not callable"
|
||||
|
||||
def test_log_object(self):
|
||||
logger = RDAgentLog()
|
||||
logger.log_object({"key": "value"})
|
||||
logger.log_object(["a", "b"], tag="test")
|
||||
|
||||
def test_tag_context_manager(self):
|
||||
logger = RDAgentLog()
|
||||
with logger.tag("test_tag"):
|
||||
logger.info("inside tag")
|
||||
logger.info("outside tag")
|
||||
|
||||
def test_debug_does_not_raise_on_empty(self):
|
||||
logger = RDAgentLog()
|
||||
logger.debug("")
|
||||
logger.debug("")
|
||||
logger.debug("")
|
||||
|
||||
def test_debug_tag_propagation(self):
|
||||
logger = RDAgentLog()
|
||||
with logger.tag("debug_context"):
|
||||
logger.debug("debug with tag", tag="inner")
|
||||
logger.debug("debug outside")
|
||||
|
||||
|
||||
class TestRDAgentLogMethods:
|
||||
"""Verify all log-level methods exist and are callable."""
|
||||
|
||||
def test_all_methods_present(self):
|
||||
logger = RDAgentLog()
|
||||
expected = {"debug", "info", "warning", "error", "log_object"}
|
||||
for name in expected:
|
||||
assert hasattr(logger, name), f"RDAgentLog missing method: {name}"
|
||||
|
||||
def test_methods_are_bound(self):
|
||||
logger = RDAgentLog()
|
||||
for name in ("debug", "info", "warning", "error"):
|
||||
method = getattr(logger, name)
|
||||
assert callable(method)
|
||||
# Should accept at minimum a string message
|
||||
method("bound method test")
|
||||
@@ -0,0 +1,136 @@
|
||||
"""Tests for rdagent/oai/backend/litellm.py — LiteLLM API backend.
|
||||
|
||||
These are offline tests that don't require a running LLM server.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from rdagent.oai.backend.litellm import LiteLLMAPIBackend
|
||||
|
||||
|
||||
class TestLiteLLMAPIBackendInit:
|
||||
def test_creates_without_crash(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
assert backend is not None
|
||||
|
||||
def test_has_inner_function(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
assert hasattr(backend, "_create_chat_completion_inner_function")
|
||||
|
||||
def test_complete_kwargs_returns_dict_like(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
kwargs = backend.get_complete_kwargs()
|
||||
assert kwargs is not None
|
||||
|
||||
def test_supports_response_schema_returns_bool(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
result = backend.supports_response_schema()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
class TestLiteLLMAPIBackendTokenCounting:
|
||||
@patch("rdagent.oai.backend.litellm.token_counter")
|
||||
def test_calculate_token_from_messages_returns_int(self, mock_counter):
|
||||
mock_counter.return_value = 42
|
||||
backend = LiteLLMAPIBackend()
|
||||
result = backend._calculate_token_from_messages(
|
||||
[{"role": "user", "content": "hello"}]
|
||||
)
|
||||
assert isinstance(result, int)
|
||||
assert result == 42
|
||||
|
||||
@patch("rdagent.oai.backend.litellm.token_counter")
|
||||
def test_calculate_token_from_messages_handles_empty(self, mock_counter):
|
||||
mock_counter.return_value = 0
|
||||
backend = LiteLLMAPIBackend()
|
||||
result = backend._calculate_token_from_messages([])
|
||||
assert result == 0
|
||||
|
||||
|
||||
class TestLiteLLMAPIBackendStreaming:
|
||||
@patch("rdagent.oai.backend.litellm.completion")
|
||||
@patch("rdagent.oai.backend.litellm.token_counter")
|
||||
def test_non_streaming_response(self, mock_tokens, mock_completion):
|
||||
mock_tokens.return_value = 10
|
||||
from rdagent.oai.backend.litellm import LITELLM_SETTINGS
|
||||
LITELLM_SETTINGS.chat_stream = False
|
||||
try:
|
||||
# Build a proper mock response structure
|
||||
resp = MagicMock()
|
||||
choice = MagicMock()
|
||||
msg = MagicMock()
|
||||
msg.content = '{"key": "value"}'
|
||||
choice.message = msg
|
||||
choice.finish_reason = "stop"
|
||||
resp.choices = [choice]
|
||||
mock_completion.return_value = resp
|
||||
|
||||
backend = LiteLLMAPIBackend()
|
||||
content, finish = backend._create_chat_completion_inner_function(
|
||||
messages=[{"role": "user", "content": "test"}],
|
||||
)
|
||||
assert '{"key": "value"}' in str(content)
|
||||
assert finish == "stop"
|
||||
finally:
|
||||
LITELLM_SETTINGS.chat_stream = True
|
||||
|
||||
@patch("rdagent.oai.backend.litellm.completion")
|
||||
@patch("rdagent.oai.backend.litellm.token_counter")
|
||||
def test_streaming_response(self, mock_tokens, mock_completion):
|
||||
mock_tokens.return_value = 5
|
||||
|
||||
chunk1 = {"choices": [{"finish_reason": None, "delta": {"content": "hello"}}]}
|
||||
chunk2 = {"choices": [{"finish_reason": "stop", "delta": {}}]}
|
||||
mock_completion.return_value = [chunk1, chunk2]
|
||||
|
||||
backend = LiteLLMAPIBackend()
|
||||
from rdagent.oai.backend.litellm import LITELLM_SETTINGS
|
||||
LITELLM_SETTINGS.chat_stream = True
|
||||
try:
|
||||
content, finish = backend._create_chat_completion_inner_function(
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
)
|
||||
assert "hello" in content
|
||||
finally:
|
||||
LITELLM_SETTINGS.chat_stream = False
|
||||
|
||||
|
||||
class TestLiteLLMAPIBackendEdgeCases:
|
||||
def test_empty_messages_token_count(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
with patch("rdagent.oai.backend.litellm.token_counter", return_value=0):
|
||||
result = backend._calculate_token_from_messages([])
|
||||
assert result == 0
|
||||
|
||||
def test_unicode_messages_token_count(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
messages = [{"role": "user", "content": "üéñ–—…€🦀"}]
|
||||
with patch("rdagent.oai.backend.litellm.token_counter", return_value=5):
|
||||
result = backend._calculate_token_from_messages(messages)
|
||||
assert result == 5
|
||||
|
||||
def test_very_long_message_token_count(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
long_msg = "hello " * 10000
|
||||
messages = [{"role": "user", "content": long_msg}]
|
||||
with patch("rdagent.oai.backend.litellm.token_counter", return_value=20000):
|
||||
result = backend._calculate_token_from_messages(messages)
|
||||
assert result == 20000
|
||||
|
||||
def test_build_log_messages_returns_string(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
messages = [
|
||||
{"role": "system", "content": "test system"},
|
||||
{"role": "user", "content": "test user"},
|
||||
]
|
||||
result = backend._build_log_messages(messages)
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_supports_response_schema_does_not_crash(self):
|
||||
backend = LiteLLMAPIBackend()
|
||||
for _ in range(10):
|
||||
backend.supports_response_schema()
|
||||
Reference in New Issue
Block a user