Skip to content

Commit 2183c28

Browse files
authored
Merge pull request #139 from iautolab/test/lean-translator-live-api
test: add live API tests for LeanTranslator
2 parents d8609ed + b951469 commit 2183c28

2 files changed

Lines changed: 91 additions & 0 deletions

File tree

tests/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,15 @@
3030
"gemini": os.environ.get("OPENLRC_TEST_MODEL_GEMINI", "google/gemini-2.5-flash-lite"),
3131
}
3232

33+
_EXTRA_BODY = {"chat_template_kwargs": {"enable_thinking": False}}
34+
3335
TEST_MODELS: dict[str, ModelConfig] = {
3436
key: ModelConfig(
3537
provider=ModelProvider.OPENAI,
3638
name=name,
3739
base_url=TEST_LLM_BASE_URL,
3840
api_key=TEST_LLM_API_KEY,
41+
extra_body=_EXTRA_BODY,
3942
)
4043
for key, name in _MODEL_NAMES.items()
4144
}

tests/test_lean_translator.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
from pathlib import Path
99
from unittest.mock import MagicMock, patch
1010

11+
from openlrc.agents import create_chatbot
12+
from openlrc.media_utils import get_similarity
1113
from openlrc.prompter import LeanContextReviewPrompter, LeanTranslatePrompter
1214
from openlrc.translate import LeanTranslator
1315
from openlrc.validators import LeanTranslateValidator
16+
from tests.conftest import LIVE_API, TEST_LLM_API_KEY, TEST_MODELS
1417

1518

1619
def _make_mock_chatbot(name: str = "gpt-4.1-nano") -> MagicMock:
@@ -643,3 +646,88 @@ def mock_message(msgs, **kwargs):
643646
# api_fee should include fees from index 3 onward (after the 3 pre-existing entries)
644647
# The mock_message appends 0.05 each call; at least one call for translation
645648
self.assertGreater(translator.api_fee, 0)
649+
650+
651+
@unittest.skipUnless(LIVE_API, "Requires OPENLRC_TEST_LIVE_API=1 and valid API keys")
652+
class TestLeanTranslatorLive(unittest.TestCase):
653+
"""Live API tests for LeanTranslator.
654+
655+
These tests make real LLM API calls and require:
656+
- OPENLRC_TEST_LIVE_API=1
657+
- OPENLRC_TEST_LLM_API_KEY set to a valid API key
658+
"""
659+
660+
@classmethod
661+
def setUpClass(cls):
662+
if not TEST_LLM_API_KEY:
663+
raise unittest.SkipTest("OPENLRC_TEST_LLM_API_KEY is required for LLM integration tests.")
664+
665+
def tearDown(self):
666+
compare_path = Path("translate_intermediate.json")
667+
compare_path.unlink(missing_ok=True)
668+
669+
def test_single_chunk_no_cr(self):
670+
"""Basic single-chunk translation without CR."""
671+
chatbot = create_chatbot(TEST_MODELS["gemini"])
672+
try:
673+
translator = LeanTranslator(chatbot=chatbot, enable_cr=False)
674+
translations = translator.translate("Hello, how are you?", "en", "es")
675+
finally:
676+
chatbot.close()
677+
678+
self.assertEqual(len(translations), 1)
679+
self.assertTrue(translations[0].strip())
680+
self.assertGreater(get_similarity(translations[0], "Hola, ¿cómo estás?"), 0.5)
681+
682+
def test_multiple_chunk_no_cr(self):
683+
"""Multiple chunks without CR, verifying all lines are translated."""
684+
texts = ["Hello, how are you?", "I am fine, thank you.", "See you tomorrow.", "Good night."]
685+
chatbot = create_chatbot(TEST_MODELS["gemini"])
686+
try:
687+
translator = LeanTranslator(chatbot=chatbot, enable_cr=False, chunk_size=2)
688+
translations = translator.translate(texts, "en", "es")
689+
finally:
690+
chatbot.close()
691+
692+
self.assertEqual(len(translations), len(texts))
693+
self.assertTrue(all(t.strip() for t in translations))
694+
695+
def test_with_cr(self):
696+
"""Full pipeline: CR generates guideline, then translation uses it."""
697+
texts = ["The suspect fled to the uptown area.", "Detective John began the investigation."]
698+
chatbot = create_chatbot(TEST_MODELS["gemini"])
699+
try:
700+
translator = LeanTranslator(chatbot=chatbot, enable_cr=True)
701+
translations = translator.translate(texts, "en", "zh")
702+
finally:
703+
chatbot.close()
704+
705+
self.assertEqual(len(translations), len(texts))
706+
self.assertTrue(all(t.strip() for t in translations))
707+
708+
def test_cr_chatbot_separation(self):
709+
"""Mixed-model: GPT does CR, Gemini does translation."""
710+
texts = ["The suspect fled to the uptown area.", "Detective John began the investigation."]
711+
cr_bot = create_chatbot(TEST_MODELS["gpt"])
712+
mt_bot = create_chatbot(TEST_MODELS["gemini"])
713+
try:
714+
translator = LeanTranslator(chatbot=mt_bot, cr_chatbot=cr_bot, enable_cr=True)
715+
translations = translator.translate(texts, "en", "zh")
716+
finally:
717+
cr_bot.close()
718+
mt_bot.close()
719+
720+
self.assertEqual(len(translations), len(texts))
721+
self.assertTrue(all(t.strip() for t in translations))
722+
self.assertGreater(translator.api_fee, 0)
723+
724+
def test_empty_texts(self):
725+
"""Empty input returns empty list without API calls."""
726+
chatbot = create_chatbot(TEST_MODELS["gemini"])
727+
try:
728+
translator = LeanTranslator(chatbot=chatbot, enable_cr=False)
729+
translations = translator.translate([], "en", "es")
730+
finally:
731+
chatbot.close()
732+
733+
self.assertEqual(translations, [])

0 commit comments

Comments
 (0)