| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import json
- import os
- import tempfile
- import unittest
- from pathlib import Path
- from types import SimpleNamespace
- from unittest.mock import patch
- from llm_client.cache import build_cache_key
- from llm_client.client import LLMClient
- class _FakeCompletions:
- def __init__(self, contents):
- self.contents = iter(contents)
- self.calls = 0
- self.kwargs = []
- def create(self, **_kwargs):
- self.calls += 1
- self.kwargs.append(_kwargs)
- content = next(self.contents)
- return SimpleNamespace(
- choices=[SimpleNamespace(message=SimpleNamespace(content=content))]
- )
- class LlmEmptyResponseTests(unittest.TestCase):
- def _client(self, contents):
- completions = _FakeCompletions(contents)
- client = object.__new__(LLMClient)
- client.model = "test-model"
- client.cfg = SimpleNamespace(generate_max_tokens=65536)
- client.client = SimpleNamespace(
- chat=SimpleNamespace(completions=completions)
- )
- return client, completions
- @staticmethod
- def _config():
- return SimpleNamespace(
- llm=SimpleNamespace(
- generate_max_tokens=100,
- generate_temperature=0.4,
- ),
- max_retries=2,
- )
- def test_blank_api_response_is_retried_and_not_cached(self):
- client, completions = self._client([" ", "有效正文"])
- with tempfile.TemporaryDirectory() as temp_dir, patch.dict(
- os.environ,
- {"BID_LLM_CACHE": "1", "BID_LLM_CACHE_DIR": temp_dir},
- ), patch("llm_client.client.get_config", return_value=self._config()), patch(
- "llm_client.client.time.sleep"
- ):
- result = client.generate("system", "user", max_tokens=100)
- self.assertEqual(result, "有效正文")
- self.assertEqual(completions.calls, 2)
- cache_files = list(Path(temp_dir).glob("*.json"))
- self.assertEqual(len(cache_files), 1)
- cached = json.loads(cache_files[0].read_text(encoding="utf-8"))
- self.assertEqual(cached["content"], "有效正文")
- def test_existing_blank_cache_is_removed_before_request(self):
- client, completions = self._client(["重新生成的正文"])
- with tempfile.TemporaryDirectory() as temp_dir, patch.dict(
- os.environ,
- {"BID_LLM_CACHE": "1", "BID_LLM_CACHE_DIR": temp_dir},
- ), patch("llm_client.client.get_config", return_value=self._config()):
- key = build_cache_key(
- model="test-model",
- system_prompt="system",
- user_prompt="user",
- max_tokens=100,
- temperature=0.4,
- response_format=None,
- )
- cache_path = Path(temp_dir) / f"{key}.json"
- cache_path.write_text(
- json.dumps({"model": "test-model", "content": ""}),
- encoding="utf-8",
- )
- result = client.generate("system", "user", max_tokens=100)
- self.assertEqual(result, "重新生成的正文")
- self.assertEqual(completions.calls, 1)
- cached = json.loads(cache_path.read_text(encoding="utf-8"))
- self.assertEqual(cached["content"], "重新生成的正文")
- def test_blank_response_retries_double_max_tokens_four_times(self):
- client, completions = self._client(
- [" ", " ", " ", " ", "有效正文"]
- )
- with tempfile.TemporaryDirectory() as temp_dir, patch.dict(
- os.environ,
- {"BID_LLM_CACHE": "1", "BID_LLM_CACHE_DIR": temp_dir},
- ), patch(
- "llm_client.client.get_config",
- return_value=SimpleNamespace(
- llm=SimpleNamespace(
- generate_max_tokens=65536,
- generate_temperature=0.4,
- ),
- max_retries=4,
- ),
- ), patch("llm_client.client.time.sleep"):
- result = client.generate("system", "user", max_tokens=8192)
- self.assertEqual(result, "有效正文")
- self.assertEqual(completions.calls, 5)
- self.assertEqual(
- [kwargs["max_tokens"] for kwargs in completions.kwargs],
- [8192, 16384, 32768, 65536, 65536],
- )
- def test_json_parse_failure_retries_double_max_tokens_four_times(self):
- client, completions = self._client([
- "{not json",
- "{not json",
- "{not json",
- "{not json",
- "{not json",
- ])
- with tempfile.TemporaryDirectory() as temp_dir, patch.dict(
- os.environ,
- {"BID_LLM_CACHE": "1", "BID_LLM_CACHE_DIR": temp_dir},
- ), patch(
- "llm_client.client.get_config",
- return_value=SimpleNamespace(
- llm=SimpleNamespace(
- generate_max_tokens=65536,
- generate_temperature=0.4,
- ),
- max_retries=4,
- ),
- ), patch("llm_client.client.time.sleep"):
- result = client.extract_json(
- system_prompt="system",
- user_prompt="user",
- max_tokens=8192,
- )
- self.assertEqual(result, {})
- self.assertEqual(completions.calls, 5)
- self.assertEqual(
- [kwargs["max_tokens"] for kwargs in completions.kwargs],
- [8192, 16384, 32768, 65536, 65536],
- )
- if __name__ == "__main__":
- unittest.main()
|