You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_chat_completion_cache.py 7.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import copy
  2. from typing import List, Tuple, Union
  3. import pytest
  4. from autogen_core.models import (
  5. ChatCompletionClient,
  6. CreateResult,
  7. LLMMessage,
  8. SystemMessage,
  9. UserMessage,
  10. )
  11. from autogen_ext.models.cache import ChatCompletionCache
  12. from autogen_ext.models.replay import ReplayChatCompletionClient
  13. from pydantic import BaseModel
  14. def get_test_data(
  15. num_messages: int = 3,
  16. ) -> Tuple[list[str], list[str], SystemMessage, ChatCompletionClient, ChatCompletionCache]:
  17. responses = [f"This is dummy message number {i}" for i in range(num_messages)]
  18. prompts = [f"This is dummy prompt number {i}" for i in range(num_messages)]
  19. system_prompt = SystemMessage(content="This is a system prompt")
  20. replay_client = ReplayChatCompletionClient(responses)
  21. replay_client.set_cached_bool_value(False)
  22. cached_client = ChatCompletionCache(replay_client)
  23. return responses, prompts, system_prompt, replay_client, cached_client
  24. @pytest.mark.asyncio
  25. async def test_cache_basic_with_args() -> None:
  26. responses, prompts, system_prompt, _, cached_client = get_test_data()
  27. response0 = await cached_client.create([system_prompt, UserMessage(content=prompts[0], source="user")])
  28. assert isinstance(response0, CreateResult)
  29. assert not response0.cached
  30. assert response0.content == responses[0]
  31. response1 = await cached_client.create([system_prompt, UserMessage(content=prompts[1], source="user")])
  32. assert not response1.cached
  33. assert response1.content == responses[1]
  34. # Cached output.
  35. response0_cached = await cached_client.create([system_prompt, UserMessage(content=prompts[0], source="user")])
  36. assert isinstance(response0, CreateResult)
  37. assert response0_cached.cached
  38. assert response0_cached.content == responses[0]
  39. # Cache miss if args change.
  40. response2 = await cached_client.create(
  41. [system_prompt, UserMessage(content=prompts[0], source="user")], json_output=True
  42. )
  43. assert isinstance(response2, CreateResult)
  44. assert not response2.cached
  45. assert response2.content == responses[2]
  46. @pytest.mark.asyncio
  47. async def test_cache_structured_output_with_args() -> None:
  48. responses, prompts, system_prompt, _, cached_client = get_test_data(num_messages=4)
  49. class Answer(BaseModel):
  50. thought: str
  51. answer: str
  52. class Answer2(BaseModel):
  53. calculation: str
  54. answer: str
  55. response0 = await cached_client.create(
  56. [system_prompt, UserMessage(content=prompts[0], source="user")], json_output=Answer
  57. )
  58. assert isinstance(response0, CreateResult)
  59. assert not response0.cached
  60. assert response0.content == responses[0]
  61. response1 = await cached_client.create(
  62. [system_prompt, UserMessage(content=prompts[1], source="user")], json_output=Answer
  63. )
  64. assert not response1.cached
  65. assert response1.content == responses[1]
  66. # Cached output.
  67. response0_cached = await cached_client.create(
  68. [system_prompt, UserMessage(content=prompts[0], source="user")], json_output=Answer
  69. )
  70. assert isinstance(response0, CreateResult)
  71. assert response0_cached.cached
  72. assert response0_cached.content == responses[0]
  73. # Without the json_output argument, the cache should not be hit.
  74. response0 = await cached_client.create([system_prompt, UserMessage(content=prompts[0], source="user")])
  75. assert isinstance(response0, CreateResult)
  76. assert not response0.cached
  77. assert response0.content == responses[2]
  78. # With a different output type, the cache should not be hit.
  79. response0 = await cached_client.create(
  80. [system_prompt, UserMessage(content=prompts[1], source="user")], json_output=Answer2
  81. )
  82. assert isinstance(response0, CreateResult)
  83. assert not response0.cached
  84. assert response0.content == responses[3]
  85. @pytest.mark.asyncio
  86. async def test_cache_model_and_count_api() -> None:
  87. _, prompts, system_prompt, replay_client, cached_client = get_test_data()
  88. assert replay_client.model_info == cached_client.model_info
  89. assert replay_client.capabilities == cached_client.capabilities
  90. messages: List[LLMMessage] = [system_prompt, UserMessage(content=prompts[0], source="user")]
  91. assert replay_client.count_tokens(messages) == cached_client.count_tokens(messages)
  92. assert replay_client.remaining_tokens(messages) == cached_client.remaining_tokens(messages)
  93. @pytest.mark.asyncio
  94. async def test_cache_token_usage() -> None:
  95. responses, prompts, system_prompt, replay_client, cached_client = get_test_data()
  96. response0 = await cached_client.create([system_prompt, UserMessage(content=prompts[0], source="user")])
  97. assert isinstance(response0, CreateResult)
  98. assert not response0.cached
  99. assert response0.content == responses[0]
  100. actual_usage0 = copy.copy(cached_client.actual_usage())
  101. total_usage0 = copy.copy(cached_client.total_usage())
  102. response1 = await cached_client.create([system_prompt, UserMessage(content=prompts[1], source="user")])
  103. assert not response1.cached
  104. assert response1.content == responses[1]
  105. actual_usage1 = copy.copy(cached_client.actual_usage())
  106. total_usage1 = copy.copy(cached_client.total_usage())
  107. assert total_usage1.prompt_tokens > total_usage0.prompt_tokens
  108. assert total_usage1.completion_tokens > total_usage0.completion_tokens
  109. assert actual_usage1.prompt_tokens == actual_usage0.prompt_tokens
  110. assert actual_usage1.completion_tokens == actual_usage0.completion_tokens
  111. # Cached output.
  112. response0_cached = await cached_client.create([system_prompt, UserMessage(content=prompts[0], source="user")])
  113. assert isinstance(response0, CreateResult)
  114. assert response0_cached.cached
  115. assert response0_cached.content == responses[0]
  116. total_usage2 = copy.copy(cached_client.total_usage())
  117. assert total_usage2.prompt_tokens == total_usage1.prompt_tokens
  118. assert total_usage2.completion_tokens == total_usage1.completion_tokens
  119. assert cached_client.actual_usage() == replay_client.actual_usage()
  120. assert cached_client.total_usage() == replay_client.total_usage()
  121. @pytest.mark.asyncio
  122. async def test_cache_create_stream() -> None:
  123. _, prompts, system_prompt, _, cached_client = get_test_data()
  124. original_streamed_results: List[Union[str, CreateResult]] = []
  125. async for completion in cached_client.create_stream(
  126. [system_prompt, UserMessage(content=prompts[0], source="user")]
  127. ):
  128. original_streamed_results.append(copy.copy(completion))
  129. total_usage0 = copy.copy(cached_client.total_usage())
  130. cached_completion_results: List[Union[str, CreateResult]] = []
  131. async for completion in cached_client.create_stream(
  132. [system_prompt, UserMessage(content=prompts[0], source="user")]
  133. ):
  134. cached_completion_results.append(copy.copy(completion))
  135. total_usage1 = copy.copy(cached_client.total_usage())
  136. assert total_usage1.prompt_tokens == total_usage0.prompt_tokens
  137. assert total_usage1.completion_tokens == total_usage0.completion_tokens
  138. for original, cached in zip(original_streamed_results, cached_completion_results, strict=False):
  139. if isinstance(original, str):
  140. assert original == cached
  141. elif isinstance(original, CreateResult) and isinstance(cached, CreateResult):
  142. assert original.content == cached.content
  143. assert cached.cached
  144. assert not original.cached
  145. else:
  146. raise ValueError(f"Unexpected types : {type(original)} and {type(cached)}")
  147. # test serialization
  148. # cached_client_config = cached_client.dump_component()
  149. # loaded_client = ChatCompletionCache.load_component(cached_client_config)
  150. # assert loaded_client.client == cached_client.client