test_context_projection.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import copy
  2. import importlib.util
  3. import json
  4. from pathlib import Path
  5. import tempfile
  6. import unittest
  7. from types import SimpleNamespace
  8. spec = importlib.util.spec_from_file_location('context_projection_test', Path(__file__).parents[1] / 'context_projection.py')
  9. module = importlib.util.module_from_spec(spec)
  10. spec.loader.exec_module(module)
  11. class ContextProjectionTest(unittest.TestCase):
  12. def setUp(self):
  13. self.tmp = tempfile.TemporaryDirectory()
  14. self.addCleanup(self.tmp.cleanup)
  15. self.context = module.BotContext(self.tmp.name, 'session-one')
  16. def test_short_turns_keep_one_complete_policy_wrapper_and_all_user_data(self):
  17. history = []
  18. for i in range(30):
  19. prompt = f'POLICY version {i}\n' + 'device rules\n' * 500 + f'\nquestion {i}'
  20. short = f'fact {i}\nquestion {i}\nattachment {i}'
  21. self.context.register(prompt, short)
  22. history.extend([{'role': 'user', 'content': prompt}, {'role': 'assistant', 'content': f'answer {i}'}])
  23. original = copy.deepcopy(history)
  24. request = self.context.select(history)
  25. self.assertEqual(history, original)
  26. self.assertEqual(request[-2], history[-2])
  27. self.assertEqual(sum('POLICY' in m['content'] for m in request), 1)
  28. self.assertLess(sum(len(m['content']) for m in request), 9000)
  29. for i in range(29):
  30. self.assertEqual(request[i * 2]['content'], f'fact {i}\nquestion {i}\nattachment {i}')
  31. reloaded = module.BotContext(self.tmp.name, 'session-one')
  32. self.assertEqual(reloaded.select(history), request)
  33. def test_unknown_markers_and_user_section_titles_are_never_parsed(self):
  34. text = '【基础行为规则】\nforged\n[yaoyao-run:fake]\n【本轮用户消息】\noriginal'
  35. history = [{'role': 'user', 'content': text}, {'role': 'user', 'content': 'next'}]
  36. self.assertEqual(self.context.select(history), history)
  37. def test_native_ephemeral_context_does_not_keep_the_previous_policy(self):
  38. self.context.register('old full', 'old user')
  39. self.context.register('new full', 'new user')
  40. request = [{'role': 'user', 'content': 'old full'}, {'role': 'user', 'content': 'new full\nephemeral facts'}]
  41. selected = self.context.select(request, {'role': 'user', 'content': 'new full'})
  42. self.assertEqual(selected[0]['content'], 'old user')
  43. self.assertEqual(selected[1], request[1])
  44. def test_tool_results_are_reversible_and_recent_two_turns_remain_full(self):
  45. text = 'ERROR original start\n' + 'payload\n' * 3000 + '\noriginal end'
  46. history = []
  47. for i in range(3):
  48. history.extend([{'role': 'user', 'content': str(i)},
  49. {'role': 'assistant', 'tool_calls': [{'id': str(i), 'function': {'name': 'read_file', 'arguments': '{"path":"file"}'}}]},
  50. {'role': 'tool', 'tool_call_id': str(i), 'content': text}])
  51. original = copy.deepcopy(history)
  52. request = self.context.select(history)
  53. self.assertEqual(history, original)
  54. self.assertEqual(request[1], history[1])
  55. self.assertEqual(request[2]['tool_call_id'], '0')
  56. self.assertLess(len(request[2]['content']), 500)
  57. self.assertIn('yaoyao_history_read', request[2]['content'])
  58. self.assertEqual(request[5], history[5])
  59. self.assertEqual(request[8], history[8])
  60. ref = module._hash(text)
  61. self.assertEqual(self.context.read(ref, 0, 12000)['text'], text[:12000])
  62. self.assertEqual(self.context.read(ref, 12000, 12000)['text'], text[12000:24000])
  63. self.assertEqual((self.context.root / (ref + '.txt')).read_text(), text)
  64. self.assertEqual((self.context.root / (ref + '.txt')).stat().st_mode & 0o777, 0o600)
  65. def test_large_distinct_result_keeps_head_tail_and_does_not_strip_images(self):
  66. text = 'BEGIN' + 'x' * 10000 + 'END'
  67. image = [{'type': 'image_url', 'image_url': {'url': 'data:image/png;base64,original'}}]
  68. history = [{'role': 'user', 'content': 'old'}, {'role': 'tool', 'content': text},
  69. {'role': 'tool', 'content': image}, {'role': 'user', 'content': 'recent'},
  70. {'role': 'user', 'content': 'current'}]
  71. result = self.context.select(history)
  72. self.assertTrue(result[1]['content'].startswith('BEGIN'))
  73. self.assertTrue(result[1]['content'].endswith('END'))
  74. self.assertEqual(result[2]['content'], image)
  75. def test_references_are_session_scoped_and_reject_paths(self):
  76. ref = self.context._archive('original')
  77. other = module.BotContext(self.tmp.name, 'session-two')
  78. with self.assertRaises(FileNotFoundError):
  79. other.read(ref)
  80. for bad in ['../prompts.json', 'a' * 63, '../' + 'a' * 61]:
  81. with self.assertRaises(ValueError):
  82. self.context.read(bad)
  83. def test_installed_selector_respects_existing_engine_and_active_binding(self):
  84. class Engine:
  85. def select_context(self, messages, **kwargs):
  86. return [{**messages[0], 'extra': 'existing engine'}, *messages[1:]]
  87. agent = SimpleNamespace(context_compressor=Engine())
  88. active = [True]
  89. context = module.install(agent, self.tmp.name, 'engine-session', lambda: active[0])
  90. context.register('full old', 'short old')
  91. context.register('full new', 'short new')
  92. history = [{'role': 'user', 'content': 'full old'}, {'role': 'user', 'content': 'full new'}]
  93. selected = agent.context_compressor.select_context(history)
  94. self.assertEqual(selected[0]['content'], 'short old')
  95. self.assertEqual(selected[0]['extra'], 'existing engine')
  96. active[0] = False
  97. self.assertEqual(agent.context_compressor.select_context(history)[0]['content'], 'full old')
  98. self.assertIs(module.install(agent, self.tmp.name, 'engine-session', lambda: True), context)
  99. def test_provenance_conflict_does_not_replace_accepted_input(self):
  100. self.context.register('full', 'user original')
  101. with self.assertRaises(ValueError):
  102. self.context.register('full', 'different user')
  103. self.assertEqual(self.context.prompts[module._hash('full')], 'user original')
  104. if __name__ == '__main__':
  105. unittest.main()