refactor: split flat core into capability packages, layer the conversations service, English defaults for every model-facing text

This commit is contained in:
hh
2026-09-02 00:13:20 +02:00
parent 253bde1b11
commit 9aaddbed75
77 changed files with 2987 additions and 2944 deletions
+17 -13
View File
@@ -3,8 +3,12 @@ from datetime import UTC, date, datetime, timedelta
from test_conversations import ScriptedClient, StubFrontend, World, world
from beaver_gateway.core.conversations import ConversationTexts
from beaver_gateway.core.rotation import HandoutContext, Rotation, RotationPolicy
from beaver_gateway.conversations.service import ConversationTexts
from beaver_gateway.conversations.rotation import (
HandoutContext,
Rotation,
RotationPolicy,
)
from beaver_gateway.storage.models import Conversation, Usage
__all__ = ["world"]
@@ -28,7 +32,7 @@ def master(
def test_night_rule_needs_silence_and_a_master_from_before_four() -> None:
now = datetime(2026, 8, 29, 2, 30, tzinfo=UTC)
quiet = master(created_ago=timedelta(hours=20), silence=timedelta(hours=4), now=now)
assert POLICY.reason(quiet, now=now, context_tokens=0) == "ночь"
assert POLICY.reason(quiet, now=now, context_tokens=0) == "night"
active = master(
created_ago=timedelta(hours=20), silence=timedelta(minutes=5), now=now
)
@@ -49,13 +53,13 @@ def test_age_and_context_rules_need_thirty_minutes_of_silence() -> None:
old = master(
created_ago=timedelta(hours=37), silence=timedelta(minutes=31), now=now
)
assert POLICY.reason(old, now=now, context_tokens=0) == "возраст"
assert POLICY.reason(old, now=now, context_tokens=0) == "age"
busy = master(
created_ago=timedelta(hours=37), silence=timedelta(minutes=5), now=now
)
assert POLICY.reason(busy, now=now, context_tokens=0) is None
big = master(created_ago=timedelta(hours=2), silence=timedelta(minutes=31), now=now)
assert POLICY.reason(big, now=now, context_tokens=90_000) == "транскрипт"
assert POLICY.reason(big, now=now, context_tokens=90_000) == "context"
assert POLICY.reason(big, now=now, context_tokens=70_000) is None
@@ -73,7 +77,7 @@ async def test_rotation_does_not_touch_a_master_mid_turn(world: World) -> None:
await world.conversations.post(conv, "working")
await asyncio.sleep(0.2)
rotation = Rotation(world.conversations, POLICY)
assert await rotation.rotate(conv, "возраст") is None
assert await rotation.rotate(conv, "age") is None
assert (await world.conversations.get(conv.external_id)).status == "open"
assert len(await world.conversations.find(kind="master")) == 1
ScriptedClient.hold.set()
@@ -119,7 +123,7 @@ async def test_rotation_order_handout_close_marks_moves_and_new_day(
await world.conversations.inject(old, "later", urgency="normal", origin="крон")
old_client = ScriptedClient.instances[0]
new = await Rotation(world.conversations, POLICY).rotate(old, "ночь")
new = await Rotation(world.conversations, POLICY).rotate(old, "night")
assert new is not None and new.kind == "master"
assert handouts[0].day == date(2026, 8, 27)
assert old_client.prompts[-1] == "напиши хендаут за 2026-08-27"
@@ -138,10 +142,10 @@ async def test_rotation_order_handout_close_marks_moves_and_new_day(
await world.settle(new, 1)
new_client = ScriptedClient.instances[-1]
prompt = new_client.prompts[0]
assert prompt.startswith("[сид: morning] master")
assert "[инжект: ротация" in prompt
assert "Новый день 20" in prompt and "(ночь)" in prompt
assert "переехало из старого мастера: 1" in prompt
assert prompt.startswith("[seed: morning] master")
assert "[inject: rotation" in prompt
assert "Новый день 20" in prompt and "(night)" in prompt
assert "1 queued injects moved over" in prompt
moved = await world.conversations.queue.pending(new.id)
assert [(i.priority, i.text) for i in moved] == [("normal", "later")]
assert (await world.conversations.find(kind="master", status="open")) == [
@@ -171,11 +175,11 @@ async def test_due_uses_last_usage_row_for_context_size(world: World) -> None:
assert await rotation.due() == []
rotation = Rotation(world.conversations, RotationPolicy(max_context_tokens=5))
(pair,) = await rotation.due()
assert pair[0].id == conv.id and pair[1] == "транскрипт"
assert pair[0].id == conv.id and pair[1] == "context"
def test_context_of_prefers_last_call_and_averages_old_rows() -> None:
from beaver_gateway.core.conversations import context_of
from beaver_gateway.conversations.service import context_of
from beaver_gateway.storage.models import Usage
fresh = Usage(