- Каждый запрос уходит в модель отдельно: системный промпт, текущие дата и время, запрос - Окно уточнений: цепочка обменов с паузами меньше follow_up_seconds (по умолчанию 120 с), не больше трёх последних - Окно чата стало журналом с временем и пометкой уточнений, «Очистить журнал» вместо «Новый диалог» - Настройка окна уточнений в окне настроек - Тесты контекста уточнений и сборки запроса Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
77 lines
2.6 KiB
Python
77 lines
2.6 KiB
Python
from datetime import datetime
|
||
|
||
from agr_assistent.core.context import (
|
||
MAX_FOLLOW_UP_EXCHANGES,
|
||
FollowUpContext,
|
||
build_messages,
|
||
format_datetime,
|
||
)
|
||
|
||
|
||
class FakeClock:
|
||
def __init__(self) -> None:
|
||
self.now = 1000.0
|
||
|
||
def __call__(self) -> float:
|
||
return self.now
|
||
|
||
|
||
def test_build_messages_adds_date_context_and_request() -> None:
|
||
context = [{"role": "user", "content": "погода?"}, {"role": "assistant", "content": "солнце"}]
|
||
|
||
messages = build_messages(" будь краток ", context, "а завтра?", datetime(2026, 9, 17, 4, 5))
|
||
|
||
assert messages == [
|
||
{"role": "system", "content": "будь краток\n\nСейчас четверг, 17 сентября 2026 года, 04:05."},
|
||
*context,
|
||
{"role": "user", "content": "а завтра?"},
|
||
]
|
||
|
||
|
||
def test_format_datetime() -> None:
|
||
assert format_datetime(datetime(2026, 3, 1, 23, 59)) == "воскресенье, 1 марта 2026 года, 23:59"
|
||
|
||
|
||
def test_follow_up_is_available_only_within_window() -> None:
|
||
clock = FakeClock()
|
||
context = FollowUpContext(window_seconds=120, clock=clock)
|
||
|
||
context.remember("погода?", "солнце")
|
||
clock.now += 119
|
||
assert [m["content"] for m in context.recent()] == ["погода?", "солнце"]
|
||
|
||
clock.now += 2
|
||
assert context.recent() == []
|
||
|
||
|
||
def test_follow_up_chain_extends_window_and_keeps_last_exchanges() -> None:
|
||
clock = FakeClock()
|
||
context = FollowUpContext(window_seconds=60, clock=clock)
|
||
|
||
for index in range(MAX_FOLLOW_UP_EXCHANGES + 2):
|
||
context.remember(f"вопрос {index}", f"ответ {index}")
|
||
clock.now += 50 # каждый следующий вопрос успевает в окно предыдущего
|
||
|
||
requests = [m["content"] for m in context.recent() if m["role"] == "user"]
|
||
assert requests == [f"вопрос {i}" for i in range(2, MAX_FOLLOW_UP_EXCHANGES + 2)]
|
||
|
||
|
||
def test_chain_restarts_after_pause() -> None:
|
||
clock = FakeClock()
|
||
context = FollowUpContext(window_seconds=60, clock=clock)
|
||
|
||
context.remember("старый", "ответ")
|
||
clock.now += 61
|
||
context.remember("новый", "ответ")
|
||
|
||
assert [m["content"] for m in context.recent()] == ["новый", "ответ"]
|
||
|
||
|
||
def test_zero_window_disables_follow_ups_and_empty_replies_are_ignored() -> None:
|
||
clock = FakeClock()
|
||
assert FollowUpContext(window_seconds=0, clock=clock).recent() == []
|
||
|
||
context = FollowUpContext(window_seconds=60, clock=clock)
|
||
context.remember("вопрос", " ")
|
||
assert context.recent() == []
|